From f7916a0161657e641717dca19f7c81af2d081ec4 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Wed, 1 Mar 2023 17:47:51 -0500
Subject: Added basic search functionality: :

---
 src/client/views/nodes/MapBox/MapBox.tsx | 40 ++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 039f11cd1..3c83698d8 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,6 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
 import BingMapsReact from 'bingmaps-react';
+import { EditableText } from 'browndash-components';
 import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -591,6 +592,25 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         });
     };
 
+
+    bingSearchBarContents: any; // For Bing Maps: The contents of the Bing search bar (string)
+
+    /*
+     * For Bing Maps
+     * Called by search button's onClick 
+     * Finds the geocode of the searched contents and sets location to that location
+    **/
+   @action
+     bingSearch = async() =>{
+        const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
+        this._bingMap.current.setView({
+           center: new this.MicrosoftMaps.Location(location.latitude, location.longitude),
+           zoom: 15,
+        });
+    }
+
+
+
     bingViewOptions = {
         center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },
         mapTypeId: 'grayscale',
@@ -617,12 +637,31 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         // });
                     }}
                     style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
+
+                        
                     <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                     {renderAnnotations(this.opaqueFilter)}
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
                     {this.annotationLayer}
 
+
+
+                    {!MapBox.UseBing ? null : 
+                    <EditableText editing 
+                        onEdit={(newText: string) => this.bingSearchBarContents = newText}
+                        placeholder="..."
+                        size="medium"
+                        text="Boston, MA"
+                    />}
+                    {!MapBox.UseBing ? null : 
+                    <input type="button" value="Search" 
+                    onClick={this.bingSearch}/>}
+                    
+
+
+
                     {!MapBox.UseBing ? null : <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} />}
+
                     <div style={{ display: MapBox.UseBing ? 'none' : undefined }}>
                         <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
                             <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
@@ -647,6 +686,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 ))}
                             {/* {this.handleMapCenter(this._map)} */}
                         </GoogleMap>
+                        
                     </div>
                     {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
                         <MarqueeAnnotator
-- 
cgit v1.2.3-70-g09d2


From c50fc16a161379b20eed04794d1433499b1483c7 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Wed, 15 Mar 2023 17:17:38 -0400
Subject: yuh

---
 src/client/views/nodes/MapBox/MapBox.tsx | 63 ++++++++++++++++++++++++++++----
 1 file changed, 55 insertions(+), 8 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 3c83698d8..f4f9e949f 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -22,6 +22,7 @@ import { AnchorMenu } from '../../pdf/AnchorMenu';
 import { Annotation } from '../../pdf/Annotation';
 import { SidebarAnnos } from '../../SidebarAnnos';
 import { FieldView, FieldViewProps } from '../FieldView';
+import { PinProps } from '../trails/PresBox';
 import './MapBox.scss';
 import { MapBoxInfoWindow } from './MapBoxInfoWindow';
 
@@ -527,8 +528,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
     }
 
-    getAnchor = (addAsAnnotation: boolean) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc;
-
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc;
     /**
      * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
      * @returns
@@ -581,7 +581,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     res(this.bingGeocode(map, query));
                 });
             } else {
-                this._bingSearchManager.geocode({
+                this._bingSearchManager.geocode({   
                     where: query,
                     callback: action((r: any) => {
                         res(r.results[0].location);
@@ -593,7 +593,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
 
-    bingSearchBarContents: any; // For Bing Maps: The contents of the Bing search bar (string)
+
+
+    bingSearchBarContents: any = "Boston, MA"; // For Bing Maps: The contents of the Bing search bar (string)
 
     /*
      * For Bing Maps
@@ -602,17 +604,60 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     **/
    @action
      bingSearch = async() =>{
+
+        // clear all pins
+        this._bingMap.current.entities.clear();
+
         const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
+
+        // this.rootDoc.latitude =location.latitude;
+        // this.rootDoc.longitude =location.longitude; // TODO: How to update the rootDoc with the correct info?
+        //DocComponents file is where rootDoc is
+
+        // call a helper method that updates the this._bingMap.current.setView, 
+        // replaces this method call below
         this._bingMap.current.setView({
            center: new this.MicrosoftMaps.Location(location.latitude, location.longitude),
-           zoom: 15,
+        //    zoom: ,
+        });
+
+        //Create custom Pushpin
+        var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
+            title: this.bingSearchBarContents,
+            subTitle: 'subtitle here',
+            text: '1'
         });
+
+        //Add the pushpin to the map
+        this._bingMap.current.entities.push(pin);
+        // const mapMarker = Docs.Create.MapMarkerDocument(this._bingMap.location.latitude, this._bingMap.location.latitude, false, [], {});
+        //     this.addDocument(mapMarker, this.annotationKey); // tells mapbox to add this marker to set of annotations on doc
     }
 
 
+    // /**
+    //  * For Bing Maps
+    //  * Place the marker on bing maps & store the empty marker as a MapMarker Document in allMarkers list
+    //  * @param position - the LatLng position where the marker is placed
+    //  * @param map
+    //  */
+    // @action
+    // private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
+    //     const marker = new google.maps.Marker({
+    //         position: position,
+    //         map: map,
+    //     });
+    //     map.panTo(position);
+    //     const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
+    //     this.addDocument(mapMarker, this.annotationKey);
+    // };
+
+
+
+
 
     bingViewOptions = {
-        center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },
+        center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },// TODO: latitude: this.rootDoc.latitude, longitude: this.rootDoc.longitude
         mapTypeId: 'grayscale',
     };
     bingMapOptions = {
@@ -650,9 +695,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     <EditableText editing 
                         onEdit={(newText: string) => this.bingSearchBarContents = newText}
                         placeholder="..."
-                        size="medium"
+                        size="medium" 
                         text="Boston, MA"
+                        onKeyPress={e => console.log(e.key)}
                     />}
+                    
                     {!MapBox.UseBing ? null : 
                     <input type="button" value="Search" 
                     onClick={this.bingSearch}/>}
@@ -686,8 +733,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 ))}
                             {/* {this.handleMapCenter(this._map)} */}
                         </GoogleMap>
-                        
                     </div>
+
                     {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
                         <MarqueeAnnotator
                             rootDoc={this.rootDoc}
-- 
cgit v1.2.3-70-g09d2


From f4589aa05911fdd0799a5649351c5e8eae35cacf Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Tue, 4 Apr 2023 10:16:50 -0400
Subject: Fixed make map error

---
 package-lock.json                         | 191 +++-----
 src/client/views/nodes/MapBox/MapBox.tsx  | 287 +++++++----
 src/client/views/nodes/MapBox/MapBox2.tsx | 777 ++++++++++++++++++++++++++++++
 3 files changed, 1041 insertions(+), 214 deletions(-)
 create mode 100644 src/client/views/nodes/MapBox/MapBox2.tsx

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/package-lock.json b/package-lock.json
index 3b834a75e..41d46fd62 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5337,16 +5337,6 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-        "d": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-            "dev": true,
-            "requires": {
-                "es5-ext": "^0.10.50",
-                "type": "^1.0.1"
-            }
-        },
         "d3-array": {
             "version": "3.2.2",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz",
@@ -6569,28 +6559,6 @@
                 "is-symbol": "^1.0.2"
             }
         },
-        "es5-ext": {
-            "version": "0.10.62",
-            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
-            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
-            "dev": true,
-            "requires": {
-                "es6-iterator": "^2.0.3",
-                "es6-symbol": "^3.1.3",
-                "next-tick": "^1.1.0"
-            }
-        },
-        "es6-iterator": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
-            "dev": true,
-            "requires": {
-                "d": "1",
-                "es5-ext": "^0.10.35",
-                "es6-symbol": "^3.1.1"
-            }
-        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6602,7 +6570,6 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
-                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -13650,7 +13617,7 @@
             "dependencies": {
                 "@iarna/cli": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/@iarna/cli/-/cli-2.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-rvVVqDa2g860niRbqs3D5RhL4la3dc1vwk+NlpKPZxKaMSHtE2se6C2x8NeveN+rcjp3/686X+u+09CZ+7lmAQ==",
                     "requires": {
                         "glob": "^7.1.2",
@@ -13753,7 +13720,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -13768,7 +13735,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -13777,12 +13744,12 @@
                 },
                 "asap": {
                     "version": "2.0.6",
-                    "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+                    "resolved": false,
                     "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
                 },
                 "asn1": {
                     "version": "0.2.6",
-                    "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+                    "resolved": false,
                     "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
                     "requires": {
                         "safer-buffer": "~2.1.0"
@@ -13790,7 +13757,7 @@
                 },
                 "assert-plus": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="
                 },
                 "asynckit": {
@@ -13800,22 +13767,22 @@
                 },
                 "aws-sign2": {
                     "version": "0.7.0",
-                    "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA=="
                 },
                 "aws4": {
                     "version": "1.11.0",
-                    "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
                 },
                 "balanced-match": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
                 },
                 "bcrypt-pbkdf": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
                     "requires": {
                         "tweetnacl": "^0.14.3"
@@ -13836,7 +13803,7 @@
                 },
                 "bluebird": {
                     "version": "3.7.2",
-                    "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
                 },
                 "boxen": {
@@ -13884,7 +13851,7 @@
                 },
                 "cacache": {
                     "version": "12.0.4",
-                    "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
                     "requires": {
                         "bluebird": "^3.5.5",
@@ -13921,7 +13888,7 @@
                 },
                 "caseless": {
                     "version": "0.12.0",
-                    "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
                 },
                 "chalk": {
@@ -14065,7 +14032,7 @@
                 },
                 "combined-stream": {
                     "version": "1.0.8",
-                    "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+                    "resolved": false,
                     "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
                     "requires": {
                         "delayed-stream": "~1.0.0"
@@ -14073,7 +14040,7 @@
                 },
                 "concat-map": {
                     "version": "0.0.1",
-                    "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
                 },
                 "concat-stream": {
@@ -14103,7 +14070,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -14118,7 +14085,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -14127,7 +14094,7 @@
                 },
                 "config-chain": {
                     "version": "1.1.13",
-                    "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+                    "resolved": false,
                     "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
                     "requires": {
                         "ini": "^1.3.4",
@@ -14228,7 +14195,7 @@
                 },
                 "dashdash": {
                     "version": "1.14.1",
-                    "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
                     "requires": {
                         "assert-plus": "^1.0.0"
@@ -14261,7 +14228,7 @@
                 },
                 "decode-uri-component": {
                     "version": "0.2.2",
-                    "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
                 },
                 "deep-extend": {
@@ -14307,7 +14274,7 @@
                 },
                 "dezalgo": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
                     "requires": {
                         "asap": "^2.0.0",
@@ -14359,7 +14326,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -14374,7 +14341,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -14383,7 +14350,7 @@
                 },
                 "ecc-jsbn": {
                     "version": "0.1.2",
-                    "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
                     "requires": {
                         "jsbn": "~0.1.0",
@@ -14418,7 +14385,7 @@
                 },
                 "env-paths": {
                     "version": "2.2.1",
-                    "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="
                 },
                 "err-code": {
@@ -14502,7 +14469,7 @@
                 },
                 "extsprintf": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="
                 },
                 "fast-json-stable-stringify": {
@@ -14512,12 +14479,12 @@
                 },
                 "figgy-pudding": {
                     "version": "3.5.2",
-                    "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw=="
                 },
                 "filter-obj": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ=="
                 },
                 "find-npm-prefix": {
@@ -14550,7 +14517,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -14565,7 +14532,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -14574,12 +14541,12 @@
                 },
                 "forever-agent": {
                     "version": "0.6.1",
-                    "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw=="
                 },
                 "form-data": {
                     "version": "2.3.3",
-                    "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
                     "requires": {
                         "asynckit": "^0.4.0",
@@ -14612,7 +14579,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -14627,7 +14594,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -14695,7 +14662,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -14710,7 +14677,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -14719,7 +14686,7 @@
                 },
                 "fs.realpath": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
                 },
                 "function-bind": {
@@ -14809,7 +14776,7 @@
                 },
                 "getpass": {
                     "version": "0.1.7",
-                    "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+                    "resolved": false,
                     "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
                     "requires": {
                         "assert-plus": "^1.0.0"
@@ -14817,7 +14784,7 @@
                 },
                 "glob": {
                     "version": "7.2.3",
-                    "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
                     "requires": {
                         "fs.realpath": "^1.0.0",
@@ -14830,7 +14797,7 @@
                     "dependencies": {
                         "minimatch": {
                             "version": "3.1.2",
-                            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+                            "resolved": false,
                             "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
                             "requires": {
                                 "brace-expansion": "^1.1.7"
@@ -14873,12 +14840,12 @@
                 },
                 "graceful-fs": {
                     "version": "4.2.10",
-                    "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+                    "resolved": false,
                     "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
                 },
                 "har-schema": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q=="
                 },
                 "har-validator": {
@@ -14957,7 +14924,7 @@
                 },
                 "http-signature": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
                     "requires": {
                         "assert-plus": "^1.0.0",
@@ -15084,7 +15051,7 @@
                 },
                 "is-cidr": {
                     "version": "3.1.1",
-                    "resolved": "https://registry.npmjs.org/is-cidr/-/is-cidr-3.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Gx+oErgq1j2jAKCR2Kbq0b3wbH0vQKqZ0wOlHxm0o56nq51Cs/DZA8oz9dMDhbHyHEGgJ86eTeVudtgMMOx3Mw==",
                     "requires": {
                         "cidr-regex": "^2.0.10"
@@ -15163,7 +15130,7 @@
                 },
                 "is-typedarray": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
                 },
                 "isarray": {
@@ -15178,12 +15145,12 @@
                 },
                 "isstream": {
                     "version": "0.1.2",
-                    "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="
                 },
                 "jsbn": {
                     "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="
                 },
                 "json-parse-better-errors": {
@@ -15193,7 +15160,7 @@
                 },
                 "json-parse-even-better-errors": {
                     "version": "2.3.1",
-                    "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
                 },
                 "json-schema": {
@@ -15203,7 +15170,7 @@
                 },
                 "json-stringify-safe": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="
                 },
                 "jsonparse": {
@@ -15421,7 +15388,7 @@
                 },
                 "lock-verify": {
                     "version": "2.2.2",
-                    "resolved": "https://registry.npmjs.org/lock-verify/-/lock-verify-2.2.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-2CUNtr1ZSVKJHcYP8uEzafmmuyauCB5zZimj8TvQd/Lflt9kXVZs+8S+EbAzZLaVUDn8CYGmeC3DFGdYfnCzeQ==",
                     "requires": {
                         "@iarna/cli": "^2.1.0",
@@ -15550,7 +15517,7 @@
                 },
                 "meant": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/meant/-/meant-1.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-88ZRGcNxAq4EH38cQ4D85PM57pikCwS8Z99EWHODxN7KBY+UuPiqzRTtZzS8KTXO/ywSWbdjjJST2Hly/EQxLw=="
                 },
                 "mime-db": {
@@ -15568,7 +15535,7 @@
                 },
                 "minimatch": {
                     "version": "3.1.2",
-                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
                     "requires": {
                         "brace-expansion": "^1.1.7"
@@ -15617,7 +15584,7 @@
                 },
                 "mkdirp": {
                     "version": "0.5.6",
-                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+                    "resolved": false,
                     "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
                     "requires": {
                         "minimist": "^1.2.6"
@@ -15665,7 +15632,7 @@
                 },
                 "node-gyp": {
                     "version": "5.1.1",
-                    "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-WH0WKGi+a4i4DUt2mHnvocex/xPLp9pYt5R6M2JdFB7pJ7Z34hveZ4nDTGTiLXCkitA9T8HFZjhinBCiVHYcWw==",
                     "requires": {
                         "env-paths": "^2.2.0",
@@ -16003,7 +15970,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -16018,7 +15985,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -16032,7 +15999,7 @@
                 },
                 "path-is-absolute": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
                 },
                 "path-is-inside": {
@@ -16052,7 +16019,7 @@
                 },
                 "performance-now": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
                 },
                 "pify": {
@@ -16101,7 +16068,7 @@
                 },
                 "proto-list": {
                     "version": "1.2.4",
-                    "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="
                 },
                 "protoduck": {
@@ -16124,7 +16091,7 @@
                 },
                 "psl": {
                     "version": "1.9.0",
-                    "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
                 },
                 "pump": {
@@ -16164,12 +16131,12 @@
                 },
                 "qs": {
                     "version": "6.5.3",
-                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA=="
                 },
                 "query-string": {
                     "version": "6.14.1",
-                    "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
                     "requires": {
                         "decode-uri-component": "^0.2.0",
@@ -16180,7 +16147,7 @@
                 },
                 "qw": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/qw/-/qw-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-1PhZ/iLKwlVNq45dnerTMKFjMof49uqli7/0QsvPNbX5OJ3IZ8msa9lUpvPheVdP+IYYPrf6cOaVil7S35joVA=="
                 },
                 "rc": {
@@ -16226,7 +16193,7 @@
                 },
                 "read-package-json": {
                     "version": "2.1.2",
-                    "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==",
                     "requires": {
                         "glob": "^7.1.1",
@@ -16285,7 +16252,7 @@
                 },
                 "request": {
                     "version": "2.88.2",
-                    "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
                     "requires": {
                         "aws-sign2": "~0.7.0",
@@ -16355,7 +16322,7 @@
                 },
                 "safe-buffer": {
                     "version": "5.2.1",
-                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
                 },
                 "safer-buffer": {
@@ -16526,7 +16493,7 @@
                 },
                 "sshpk": {
                     "version": "1.17.0",
-                    "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
                     "requires": {
                         "asn1": "~0.2.3",
@@ -16582,7 +16549,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -16597,7 +16564,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -16611,7 +16578,7 @@
                 },
                 "strict-uri-encode": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ=="
                 },
                 "string-width": {
@@ -16767,7 +16734,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -16782,7 +16749,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -16801,7 +16768,7 @@
                 },
                 "tough-cookie": {
                     "version": "2.5.0",
-                    "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
                     "requires": {
                         "psl": "^1.1.28",
@@ -16810,7 +16777,7 @@
                     "dependencies": {
                         "punycode": {
                             "version": "2.1.1",
-                            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+                            "resolved": false,
                             "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
                         }
                     }
@@ -16825,7 +16792,7 @@
                 },
                 "tweetnacl": {
                     "version": "0.14.5",
-                    "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+                    "resolved": false,
                     "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
                 },
                 "typedarray": {
@@ -16896,7 +16863,7 @@
                 },
                 "uri-js": {
                     "version": "4.4.1",
-                    "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
                     "requires": {
                         "punycode": "^2.1.0"
@@ -16937,7 +16904,7 @@
                 },
                 "uuid": {
                     "version": "3.4.0",
-                    "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
                 },
                 "validate-npm-package-license": {
@@ -16959,7 +16926,7 @@
                 },
                 "verror": {
                     "version": "1.10.0",
-                    "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
                     "requires": {
                         "assert-plus": "^1.0.0",
@@ -22156,7 +22123,7 @@
         "tweetnacl": {
             "version": "0.14.5",
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-            "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
+            "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
         "type-check": {
             "version": "0.4.0",
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index d35a5ef2e..b01426bcd 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -201,6 +201,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         markerIcon.style.position = 'absolute';
         markerIcon.innerHTML = 'Add';
         controlUI.appendChild(markerIcon);
+        
 
         // Setup the click event listeners
         controlUI.addEventListener('click', () => {
@@ -219,6 +220,27 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         controlDiv.appendChild(controlUI);
         return controlDiv;
     };
+    /**
+     * Load and render all map markers
+     * @param marker
+     * @param place
+     */
+    @action
+    private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
+        place[Id] ? (this.markerMap[place[Id]] = marker) : null;
+    };
+
+    /**
+     * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true
+     * @param e
+     * @param place
+     */
+    @action
+    private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
+        // set which place was clicked
+        this.selectedPlace = place;
+        place.infoWindowOpen = true;
+    };
 
     /**
      * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list
@@ -299,27 +321,52 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     /**
-     * Load and render all map markers
-     * @param marker
-     * @param place
+     * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
+     * add a customized temporary marker on the map
      */
     @action
-    private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
-        place[Id] ? (this.markerMap[place[Id]] = marker) : null;
-    };
+    private handlePlaceChanged = () => {
+        const place = this.searchBox.getPlace();
 
-    /**
-     * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true
-     * @param e
-     * @param place
-     */
-    @action
-    private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
-        // set which place was clicked
-        this.selectedPlace = place;
-        place.infoWindowOpen = true;
+        if (!place.geometry || !place.geometry.location) {
+            // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed
+            window.alert("No details available for input: '" + place.name + "'");
+            return;
+        }
+
+        // zoom in on the location of the search result
+        if (place.geometry.viewport) {
+            this._map.fitBounds(place.geometry.viewport);
+        } else {
+            this._map.setCenter(place.geometry.location);
+            this._map.setZoom(17);
+        }
+
+        // customize icon => customized icon for the nature of the location selected
+        const icon = {
+            url: place.icon as string,
+            size: new google.maps.Size(71, 71),
+            origin: new google.maps.Point(0, 0),
+            anchor: new google.maps.Point(17, 34),
+            scaledSize: new google.maps.Size(25, 25),
+        };
+
+        // put temporary cutomized marker on searched location
+        this.searchMarkers.forEach(marker => {
+            marker.setMap(null);
+        });
+        this.searchMarkers = [];
+        this.searchMarkers.push(
+            new window.google.maps.Marker({
+                map: this._map,
+                icon,
+                title: place.name,
+                position: place.geometry.location,
+            })
+        );
     };
 
+
     /**
      * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts
      * @param doc
@@ -397,52 +444,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4'));
     }
 
-    /**
-     * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
-     * add a customized temporary marker on the map
-     */
-    @action
-    private handlePlaceChanged = () => {
-        const place = this.searchBox.getPlace();
-
-        if (!place.geometry || !place.geometry.location) {
-            // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed
-            window.alert("No details available for input: '" + place.name + "'");
-            return;
-        }
-
-        // zoom in on the location of the search result
-        if (place.geometry.viewport) {
-            this._map.fitBounds(place.geometry.viewport);
-        } else {
-            this._map.setCenter(place.geometry.location);
-            this._map.setZoom(17);
-        }
-
-        // customize icon => customized icon for the nature of the location selected
-        const icon = {
-            url: place.icon as string,
-            size: new google.maps.Size(71, 71),
-            origin: new google.maps.Point(0, 0),
-            anchor: new google.maps.Point(17, 34),
-            scaledSize: new google.maps.Size(25, 25),
-        };
-
-        // put temporary cutomized marker on searched location
-        this.searchMarkers.forEach(marker => {
-            marker.setMap(null);
-        });
-        this.searchMarkers = [];
-        this.searchMarkers.push(
-            new window.google.maps.Marker({
-                map: this._map,
-                icon,
-                title: place.name,
-                position: place.geometry.location,
-            })
-        );
-    };
-
     /**
      * Handles toggle of sidebar on click the little comment button
      */
@@ -595,71 +596,153 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
 
 
+    /**
+     *
+     * 
+     *  ERIC'S BING MAP CODE BELOW
+     * 
+     * 
+     * 
+    **/
+
+    @observable
+    _bingLatitude:number | undefined;
+
+    @observable
+    _bingLongitude:number | undefined;
+
     bingSearchBarContents: any = "Boston, MA"; // For Bing Maps: The contents of the Bing search bar (string)
 
+    geoDataRequestOptions = {
+        entityType: 'PopulatedPlace'
+    };
     /*
      * For Bing Maps
      * Called by search button's onClick 
      * Finds the geocode of the searched contents and sets location to that location
     **/
    @action
-     bingSearch = async() =>{
-
-        // clear all pins
-        this._bingMap.current.entities.clear();
-
-        const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
-
-        // this.rootDoc.latitude =location.latitude;
-        // this.rootDoc.longitude =location.longitude; // TODO: How to update the rootDoc with the correct info?
-        //DocComponents file is where rootDoc is
-
-        // call a helper method that updates the this._bingMap.current.setView, 
-        // replaces this method call below
-        this._bingMap.current.setView({
-           center: new this.MicrosoftMaps.Location(location.latitude, location.longitude),
-        //    zoom: ,
-        });
-
-        //Create custom Pushpin
-        var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
-            title: this.bingSearchBarContents,
-            subTitle: 'subtitle here',
-            text: '1'
-        });
-
-        //Add the pushpin to the map
-        this._bingMap.current.entities.push(pin);
-        // const mapMarker = Docs.Create.MapMarkerDocument(this._bingMap.location.latitude, this._bingMap.location.latitude, false, [], {});
-        //     this.addDocument(mapMarker, this.annotationKey); // tells mapbox to add this marker to set of annotations on doc
-    }
+   bingSearch = async() =>{ //TODO: PlaceResult, searching more formally
+
+      // clear all pins
+      // this._bingMap.current.entities.clear();
+
+      const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
+
+      this.rootDoc.latitude = location.latitude;
+      this.rootDoc.longitude = location.longitude;
+
+      // this.rootDoc.latitude =location.latitude;
+      // this.rootDoc.longitude =location.longitude; // TODO: How to update the rootDoc with the correct info?
+      //DocComponents file is where rootDoc is
+
+      // call a helper method that updates the this._bingMap.current.setView, 
+      // replaces this method call below
+      this._bingMap.current.setView({
+         center: new this.MicrosoftMaps.Location(location.latitude, location.longitude),
+          // zoom: location
+      });
+      // this.MicrosoftMaps.SpatialDataService.GeoDataAPIManager.getBoundary(
+      //     this._bingMap.current.getCenter(),
+      //     this.geoDataRequestOptions,
+      //     this._bingMap.current,
+      //     function (data) {
+      //         if (data.results && data.results.length > 0) {
+      //             map.entities.push(data.results[0].Polygons);
+      //         }
+      //     },
+      //     null,
+      //     function errCallback(networkStatus, statusMessage) {
+      //         console.log(networkStatus);
+      //         console.log(statusMessage);
+      //     }
+      // );
+
+      //Create custom Pushpin
+      var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
+          title: this.bingSearchBarContents,
+          subTitle: 'subtitle here',
+          text: '1'
+      });
+
+      //Add the pushpin to the map
+      this._bingMap.current.entities.push(pin);
+      // const mapMarker = Docs.Create.MapMarkerDocument(this._bingMap.location.latitude, this._bingMap.location.latitude, false, [], {});
+      //     this.addDocument(mapMarker, this.annotationKey); // tells mapbox to add this marker to set of annotations on doc
+  }
+
+  // _loadPending = true;
+  // /**
+  //  * store a reference to google map instance
+  //  * setup the drawing manager on the top right corner of map
+  //  * fit map bounds to contain all markers
+  //  * 
+  //  */
+  // @action
+  // private loadHandler = () => {
+
+  //     // this._loadPending = true;
+
+  //     // // for making GoogleMap markers
+  //     // // const centerControlDiv = this.CenterControl();
+  //     // // map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv);
+      
+  //     // // this._bingMap.current.
+
+  //     // map.setZoom(NumCast(this.dataDoc.mapZoom, 2.5));
+  //     // map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
+  //     // setTimeout(() => {
+  //     //     if (this._loadPending && this._map.getBounds()) {
+  //     //         this._loadPending = false;
+  //     //         this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+  //     //     }
+  //     // }, 250);
+
+  //     // // listener to addmarker event, creates pushpin onClick
+  //     // this._bingMap.addListener('click', (e: MouseEvent) => {
+  //     //     if (this.toggleAddMarker === true) {
+  //     //         this.placeMarker((e as any).latLng, map); //TODO: Implement placeMarker
+  //     //     }
+  //     // });
+  // };
 
 
     // /**
     //  * For Bing Maps
     //  * Place the marker on bing maps & store the empty marker as a MapMarker Document in allMarkers list
-    //  * @param position - the LatLng position where the marker is placed
-    //  * @param map
+    //  * @param location - this.MicrosoftMaps.Location
+    //  * @param map - this._bingMap
     //  */
     // @action
-    // private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
-    //     const marker = new google.maps.Marker({
-    //         position: position,
-    //         map: map,
+    // private placeMarker = (location: any) => {
+    //     const pin = new this.MicrosoftMaps.Pushpin(location, {
+    //         title: this.bingSearchBarContents,
+    //         subTitle: 'subtitle here',
+    //         text: '1'
     //     });
-    //     map.panTo(position);
-    //     const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
-    //     this.addDocument(mapMarker, this.annotationKey);
-    // };
 
+    //     this._bingMap.current.panTo(location);
+    //     this._bingMap.current.entities.push(pin);
 
+    //     // const mapMarker = Docs.Create.MapMarkerDocument(NumCast(location.latitude), NumCast(location.longitude), false, [], {});
+    //     // this.addDocument(mapMarker, this.annotationKey);
+    // };
 
 
 
+
+    /** 
+     * View options for bing maps
+    */
     bingViewOptions = {
         center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },// TODO: latitude: this.rootDoc.latitude, longitude: this.rootDoc.longitude
         mapTypeId: 'grayscale',
     };
+
+    /**
+     * Map options
+     * TODO: CHANGE TO BE MORE USER-FRIENDLY
+     */
     bingMapOptions = {
         navigationBarMode: 'square',
     };
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
new file mode 100644
index 000000000..c11f76439
--- /dev/null
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -0,0 +1,777 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
+import BingMapsReact from 'bingmaps-react';
+import { EditableText } from 'browndash-components';
+import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { InkTool } from '../../../../fields/InkField';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { Docs } from '../../../documents/Documents';
+import { DragManager } from '../../../util/DragManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { UndoManager } from '../../../util/UndoManager';
+import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
+import { Colors } from '../../global/globalEnums';
+import { MarqueeAnnotator } from '../../MarqueeAnnotator';
+import { AnchorMenu } from '../../pdf/AnchorMenu';
+import { Annotation } from '../../pdf/Annotation';
+import { SidebarAnnos } from '../../SidebarAnnos';
+import { FieldView, FieldViewProps } from '../FieldView';
+import { PinProps } from '../trails';
+import './MapBox.scss';
+import { MapBoxInfoWindow } from './MapBoxInfoWindow';
+
+/**
+ * MapBox architecture:
+ * Main component: MapBox.tsx
+ * Supporting Components: SidebarAnnos, CollectionStackingView
+ *
+ * MapBox is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content.
+ * The main body of MapBox uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view.
+ * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available,
+ * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map).
+ * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts).
+ * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
+ */
+
+// const _global = (window /* browser */ || global /* node */) as any;
+
+const mapContainerStyle = {
+    height: '100%',
+};
+
+const defaultCenter = {
+    lat: 42.360081,
+    lng: -71.058884,
+};
+
+const mapOptions = {
+    fullscreenControl: false,
+};
+
+const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/  and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey>
+const apiKey = process.env.GOOGLE_MAPS;
+
+const script = document.createElement('script');
+script.defer = true;
+script.async = true;
+script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
+console.log(script.src);
+document.head.appendChild(script);
+
+/**
+ * Consider integrating later: allows for drawing, circling, making shapes on map
+ */
+// const drawingManager = new window.google.maps.drawing.DrawingManager({
+//     drawingControl: true,
+//     drawingControlOptions: {
+//         position: google.maps.ControlPosition.TOP_RIGHT,
+//         drawingModes: [
+//             google.maps.drawing.OverlayType.MARKER,
+//             // currently we are not supporting the following drawing mode on map, a thought for future development
+//             google.maps.drawing.OverlayType.CIRCLE,
+//             google.maps.drawing.OverlayType.POLYLINE,
+//         ],
+//     },
+// });
+
+// options for searchbox in Google Maps Places Autocomplete API
+const options = {
+    fields: ['formatted_address', 'geometry', 'name'], // note: level of details is charged by item per retrieval, not recommended to return all fields
+    strictBounds: false,
+    types: ['establishment'], // type pf places, subject of change according to user need
+} as google.maps.places.AutocompleteOptions;
+
+@observer
+export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
+    static UseBing = true;
+    private _dropDisposer?: DragManager.DragDropDisposer;
+    private _disposers: { [name: string]: IReactionDisposer } = {};
+    private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+    @observable private _overlayAnnoInfo: Opt<Doc>;
+    showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(MapBox2, fieldKey);
+    }
+    public get SidebarKey() {
+        return this.fieldKey + '-sidebar';
+    }
+    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
+    @computed get inlineTextAnnotations() {
+        return this.allMapMarkers.filter(a => a.textInlineAnnotations);
+    }
+
+    @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
+    @observable private selectedPlace: Doc | undefined;
+    @observable private markerMap: { [id: string]: google.maps.Marker } = {};
+    @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
+    @observable private _marqueeing: number[] | undefined;
+    @observable private _isAnnotating = false;
+    @observable private inputRef = React.createRef<HTMLInputElement>();
+    @observable private searchMarkers: google.maps.Marker[] = [];
+    @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
+    @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
+    @computed get allSidebarDocs() {
+        return DocListCast(this.dataDoc[this.SidebarKey]);
+    }
+    @computed get allMapMarkers() {
+        return DocListCast(this.dataDoc[this.annotationKey]);
+    }
+    @observable private toggleAddMarker = false;
+    private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
+
+    @observable _showSidebar = false;
+    @computed get SidebarShown() {
+        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+    }
+
+    static _canAnnotate = true;
+    static _hadSelection: boolean = false;
+    private _sidebarRef = React.createRef<SidebarAnnos>();
+    private _ref: React.RefObject<HTMLDivElement> = React.createRef();
+
+    componentDidMount() {
+        this.props.setContentView?.(this);
+    }
+
+    @action
+    private setSearchBox = (searchBox: any) => {
+        this.searchBox = searchBox;
+    };
+
+    // iterate allMarkers to size, center, and zoom map to contain all markers
+    private fitBounds = (map: google.maps.Map) => {
+        const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
+        const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
+        !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds()));
+    };
+
+    /**
+     * Custom control for add marker button
+     * @param controlDiv
+     * @param map
+     */
+    private CenterControl = () => {
+        const controlDiv = document.createElement('div');
+        controlDiv.className = 'mapBox-addMarker';
+        // Set CSS for the control border.
+        const controlUI = document.createElement('div');
+        controlUI.style.backgroundColor = '#fff';
+        controlUI.style.borderRadius = '3px';
+        controlUI.style.cursor = 'pointer';
+        controlUI.style.marginTop = '10px';
+        controlUI.style.borderRadius = '4px';
+        controlUI.style.marginBottom = '22px';
+        controlUI.style.textAlign = 'center';
+        controlUI.style.position = 'absolute';
+        controlUI.style.width = '32px';
+        controlUI.style.height = '32px';
+        controlUI.title = 'Click to toggle marker mode.  In marker mode, click on map to place a marker.';
+
+        const plIcon = document.createElement('img');
+        plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png';
+        plIcon.style.color = 'rgb(25,25,25)';
+        plIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
+        plIcon.style.fontSize = '16px';
+        plIcon.style.lineHeight = '32px';
+        plIcon.style.left = '18';
+        plIcon.style.top = '15';
+        plIcon.style.position = 'absolute';
+        plIcon.width = 14;
+        plIcon.height = 14;
+        plIcon.innerHTML = 'Add';
+        controlUI.appendChild(plIcon);
+
+        // Set CSS for the control interior.
+        const markerIcon = document.createElement('img');
+        markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png';
+        markerIcon.style.color = 'rgb(25,25,25)';
+        markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
+        markerIcon.style.fontSize = '16px';
+        markerIcon.style.lineHeight = '32px';
+        markerIcon.style.left = '-2';
+        markerIcon.style.top = '1';
+        markerIcon.width = 30;
+        markerIcon.height = 30;
+        markerIcon.style.position = 'absolute';
+        markerIcon.innerHTML = 'Add';
+        controlUI.appendChild(markerIcon);
+
+        // Setup the click event listeners
+        controlUI.addEventListener('click', () => {
+            if (this.toggleAddMarker === true) {
+                this.toggleAddMarker = false;
+                console.log('add marker button status:' + this.toggleAddMarker);
+                controlUI.style.backgroundColor = '#fff';
+                markerIcon.style.color = 'rgb(25,25,25)';
+            } else {
+                this.toggleAddMarker = true;
+                console.log('add marker button status:' + this.toggleAddMarker);
+                controlUI.style.backgroundColor = '#4476f7';
+                markerIcon.style.color = 'rgb(255,255,255)';
+            }
+        });
+        controlDiv.appendChild(controlUI);
+        return controlDiv;
+    };
+
+    /**
+     * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list
+     * @param position - the LatLng position where the marker is placed
+     * @param map
+     */
+    @action
+    private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
+        const marker = new google.maps.Marker({
+            position: position,
+            map: map,
+        });
+        map.panTo(position);
+        const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
+        this.addDocument(mapMarker, this.annotationKey);
+    };
+
+    _loadPending = true;
+    /**
+     * store a reference to google map instance
+     * setup the drawing manager on the top right corner of map
+     * fit map bounds to contain all markers
+     * @param map
+     */
+    @action
+    private loadHandler = (map: google.maps.Map) => {
+        this._map = map;
+        this._loadPending = true;
+        const centerControlDiv = this.CenterControl();
+        map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv);
+        //drawingManager.setMap(map);
+        // if (navigator.geolocation) {
+        //     navigator.geolocation.getCurrentPosition(
+        //         (position: Position) => {
+        //             const pos = {
+        //                 lat: position.coords.latitude,
+        //                 lng: position.coords.longitude,
+        //             };
+        //             this._map.setCenter(pos);
+        //         }
+        //     );
+        // } else {
+        //     alert("Your geolocation is not supported by browser.")
+        // };
+        map.setZoom(NumCast(this.dataDoc.mapZoom, 2.5));
+        map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
+        setTimeout(() => {
+            if (this._loadPending && this._map.getBounds()) {
+                this._loadPending = false;
+                this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+            }
+        }, 250);
+        // listener to addmarker event
+        this._map.addListener('click', (e: MouseEvent) => {
+            if (this.toggleAddMarker === true) {
+                this.placeMarker((e as any).latLng, map);
+            }
+        });
+    };
+
+    @action
+    centered = () => {
+        if (this._loadPending && this._map.getBounds()) {
+            this._loadPending = false;
+            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+        }
+        this.dataDoc.mapLat = this._map.getCenter()?.lat();
+        this.dataDoc.mapLng = this._map.getCenter()?.lng();
+    };
+
+    @action
+    zoomChanged = () => {
+        if (this._loadPending && this._map.getBounds()) {
+            this._loadPending = false;
+            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+        }
+        this.dataDoc.mapZoom = this._map.getZoom();
+    };
+
+    /**
+     * Load and render all map markers
+     * @param marker
+     * @param place
+     */
+    @action
+    private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
+        place[Id] ? (this.markerMap[place[Id]] = marker) : null;
+    };
+
+    /**
+     * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true
+     * @param e
+     * @param place
+     */
+    @action
+    private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
+        // set which place was clicked
+        this.selectedPlace = place;
+        place.infoWindowOpen = true;
+    };
+
+    /**
+     * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts
+     * @param doc
+     * @param sidebarKey
+     * @returns
+     */
+    sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
+        console.log('print all sidebar Docs');
+        if (!this.layoutDoc._showSidebar) this.toggleSidebar();
+        const docs = doc instanceof Doc ? [doc] : doc;
+        docs.forEach(doc => {
+            if (doc.lat !== undefined && doc.lng !== undefined) {
+                const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng);
+                if (existingMarker) {
+                    Doc.AddDocToList(existingMarker, 'data', doc);
+                } else {
+                    const marker = Docs.Create.MapMarkerDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
+                    this.addDocument(marker, this.annotationKey);
+                }
+            }
+        }); //add to annotation list
+
+        return this.addDocument(doc, sidebarKey); // add to sidebar list
+    };
+
+    /**
+     * Removing documents from the sidebar
+     * @param doc
+     * @param sidebarKey
+     * @returns
+     */
+    sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
+        if (this.layoutDoc._showSidebar) this.toggleSidebar();
+        const docs = doc instanceof Doc ? [doc] : doc;
+        return this.removeDocument(doc, sidebarKey);
+    };
+
+    /**
+     * Toggle sidebar onclick the tiny comment button on the top right corner
+     * @param e
+     */
+    sidebarBtnDown = (e: React.PointerEvent) => {
+        setupMoveUpEvents(
+            this,
+            e,
+            (e, down, delta) =>
+                runInAction(() => {
+                    const localDelta = this.props
+                        .ScreenToLocalTransform()
+                        .scale(this.props.NativeDimScaling?.() || 1)
+                        .transformDirection(delta[0], delta[1]);
+                    const fullWidth = this.layoutDoc[WidthSym]();
+                    const mapWidth = fullWidth - this.sidebarWidth();
+                    if (this.sidebarWidth() + localDelta[0] > 0) {
+                        this._showSidebar = true;
+                        this.layoutDoc._width = fullWidth + localDelta[0];
+                        this.layoutDoc._sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
+                    } else {
+                        this._showSidebar = false;
+                        this.layoutDoc._width = mapWidth;
+                        this.layoutDoc._sidebarWidthPercent = '0%';
+                    }
+                    return false;
+                }),
+            emptyFunction,
+            () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map')
+        );
+    };
+
+    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+    @computed get sidebarWidthPercent() {
+        return StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
+    }
+    @computed get sidebarColor() {
+        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4'));
+    }
+
+    /**
+     * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
+     * add a customized temporary marker on the map
+     */
+    @action
+    private handlePlaceChanged = () => {
+        const place = this.searchBox.getPlace();
+
+        if (!place.geometry || !place.geometry.location) {
+            // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed
+            window.alert("No details available for input: '" + place.name + "'");
+            return;
+        }
+
+        // zoom in on the location of the search result
+        if (place.geometry.viewport) {
+            this._map.fitBounds(place.geometry.viewport);
+        } else {
+            this._map.setCenter(place.geometry.location);
+            this._map.setZoom(17);
+        }
+
+        // customize icon => customized icon for the nature of the location selected
+        const icon = {
+            url: place.icon as string,
+            size: new google.maps.Size(71, 71),
+            origin: new google.maps.Point(0, 0),
+            anchor: new google.maps.Point(17, 34),
+            scaledSize: new google.maps.Size(25, 25),
+        };
+
+        // put temporary cutomized marker on searched location
+        this.searchMarkers.forEach(marker => {
+            marker.setMap(null);
+        });
+        this.searchMarkers = [];
+        this.searchMarkers.push(
+            new window.google.maps.Marker({
+                map: this._map,
+                icon,
+                title: place.name,
+                position: place.geometry.location,
+            })
+        );
+    };
+
+    /**
+     * Handles toggle of sidebar on click the little comment button
+     */
+    @computed get sidebarHandle() {
+        return (
+            <div
+                className="mapBox-overlayButton-sidebar"
+                key="sidebar"
+                title="Toggle Sidebar"
+                style={{
+                    display: !this.props.isContentActive() ? 'none' : undefined,
+                    top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                    backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
+                }}
+                onPointerDown={this.sidebarBtnDown}>
+                <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
+            </div>
+        );
+    }
+
+    // TODO: Adding highlight box layer to Maps
+    @action
+    toggleSidebar = () => {
+        //1.2 * w * ? = .2 * w      .2/1.2
+        const prevWidth = this.sidebarWidth();
+        this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
+        this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+    };
+
+    sidebarDown = (e: React.PointerEvent) => {
+        setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
+    };
+    sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
+        const bounds = this._ref.current!.getBoundingClientRect();
+        this.layoutDoc._sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
+        this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%';
+        e.preventDefault();
+        return false;
+    };
+
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
+
+    @action
+    onMarqueeDown = (e: React.PointerEvent) => {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+            setupMoveUpEvents(
+                this,
+                e,
+                action(e => {
+                    MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                    this._marqueeing = [e.clientX, e.clientY];
+                    return true;
+                }),
+                returnFalse,
+                () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
+                false
+            );
+        }
+    };
+    @action finishMarquee = (x?: number, y?: number) => {
+        this._marqueeing = undefined;
+        this._isAnnotating = false;
+        x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false);
+    };
+
+    addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
+        return this.addDocument(doc, annotationKey);
+    };
+
+    pointerEvents = () => {
+        return this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none';
+    };
+    @computed get annotationLayer() {
+        return (
+            <div className="mapBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
+                {this.inlineTextAnnotations
+                    .sort((a, b) => NumCast(a.y) - NumCast(b.y))
+                    .map(anno => (
+                        <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />
+                    ))}
+            </div>
+        );
+    }
+
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc;
+
+    /**
+     * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
+     * @returns
+     */
+    private renderMarkers = () => {
+        return this.allMapMarkers.map(place => (
+            <Marker key={place[Id]} position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }} onLoad={marker => this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} />
+        ));
+    };
+
+    // TODO: auto center on select a document in the sidebar
+    private handleMapCenter = (map: google.maps.Map) => {
+        // console.log("print the selected views in selectionManager:")
+        // if (SelectionManager.Views().lastElement()) {
+        //     console.log(SelectionManager.Views().lastElement());
+        // }
+    };
+
+    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
+    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
+    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
+    transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
+    opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
+    infoWidth = () => this.props.PanelWidth() / 5;
+    infoHeight = () => this.props.PanelHeight() / 5;
+    anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
+    savedAnnotations = () => this._savedAnnotations;
+
+    _bingSearchManager: any;
+    _bingMap: any;
+    get MicrosoftMaps() {
+        return (window as any).Microsoft.Maps;
+    }
+    // uses Bing Search to retrieve lat/lng for a location.  eg.,
+    //   const results = this.geocodeQuery(map.map, 'Philadelphia, PA');
+    // to move the map to that location:
+    //     const location = await this.geocodeQuery(this._bingMap, 'Philadelphia, PA');
+    //     this._bingMap.current.setView({
+    //        mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
+    //        center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
+    //     });
+    //
+    bingGeocode = (map: any, query: string) => {
+        return new Promise<{ latitude: number; longitude: number }>((res, reject) => {
+            //If search manager is not defined, load the search module.
+            if (!this._bingSearchManager) {
+                //Create an instance of the search manager and call the geocodeQuery function again.
+                this.MicrosoftMaps.loadModule('Microsoft.Maps.Search', () => {
+                    this._bingSearchManager = new this.MicrosoftMaps.Search.SearchManager(map.current);
+                    res(this.bingGeocode(map, query));
+                });
+            } else {
+                this._bingSearchManager.geocode({   
+                    where: query,
+                    callback: action((r: any) => {
+                        res(r.results[0].location);
+                    }),
+                    errorCallback: (e: any) => reject(),
+                });
+            }
+        });
+    };
+
+
+
+
+    bingSearchBarContents: any = "Boston, MA"; // For Bing Maps: The contents of the Bing search bar (string)
+
+    /*
+     * For Bing Maps
+     * Called by search button's onClick 
+     * Finds the geocode of the searched contents and sets location to that location
+    **/
+   @action
+     bingSearch = async() =>{
+
+        // clear all pins
+        this._bingMap.current.entities.clear();
+
+        const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
+
+        // this.rootDoc.latitude =location.latitude;
+        // this.rootDoc.longitude =location.longitude; // TODO: How to update the rootDoc with the correct info?
+        //DocComponents file is where rootDoc is
+
+        // call a helper method that updates the this._bingMap.current.setView, 
+        // replaces this method call below
+        this._bingMap.current.setView({
+           center: new this.MicrosoftMaps.Location(location.latitude, location.longitude),
+        //    zoom: ,
+        });
+
+        //Create custom Pushpin
+        var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
+            title: this.bingSearchBarContents,
+            subTitle: 'subtitle here',
+            text: '1'
+        });
+
+        //Add the pushpin to the map
+        this._bingMap.current.entities.push(pin);
+        // const mapMarker = Docs.Create.MapMarkerDocument(this._bingMap.location.latitude, this._bingMap.location.latitude, false, [], {});
+        //     this.addDocument(mapMarker, this.annotationKey); // tells mapbox to add this marker to set of annotations on doc
+    }
+
+
+    // /**
+    //  * For Bing Maps
+    //  * Place the marker on bing maps & store the empty marker as a MapMarker Document in allMarkers list
+    //  * @param position - the LatLng position where the marker is placed
+    //  * @param map
+    //  */
+    // @action
+    // private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
+    //     const marker = new google.maps.Marker({
+    //         position: position,
+    //         map: map,
+    //     });
+    //     map.panTo(position);
+    //     const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
+    //     this.addDocument(mapMarker, this.annotationKey);
+    // };
+
+
+
+
+
+    bingViewOptions = {
+        center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },// TODO: latitude: this.rootDoc.latitude, longitude: this.rootDoc.longitude
+        mapTypeId: 'grayscale',
+    };
+    bingMapOptions = {
+        navigationBarMode: 'square',
+    };
+    bingMapReady = (map: any) => (this._bingMap = map.map);
+    render() {
+        const renderAnnotations = (docFilters?: () => string[]) => null;
+        return (
+            <div className="mapBox" ref={this._ref}>
+                <div
+                    className="mapBox-wrapper"
+                    onWheel={e => e.stopPropagation()}
+                    onPointerDown={async e => {
+                        e.button === 0 && !e.ctrlKey && e.stopPropagation();
+                        // just a simple test of bing maps geocode api
+                        // const loc = await this.bingGeocode(this._bingMap, 'Philadelphia, PA');
+                        // this._bingMap.current.setView({
+                        //     mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
+                        //     center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
+                        //     zoom: 15,
+                        // });
+                    }}
+                    style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
+
+                        
+                    <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
+                    {renderAnnotations(this.opaqueFilter)}
+                    {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
+                    {this.annotationLayer}
+
+
+
+                    {!MapBox2.UseBing ? null : 
+                    <EditableText editing 
+                        onEdit={(newText: string) => this.bingSearchBarContents = newText}
+                        placeholder="..."
+                        size="medium" 
+                        text="Boston, MA"
+                        onKeyPress={e => console.log(e.key)}
+                    />}
+                    
+                    {!MapBox2.UseBing ? null : 
+                    <input type="button" value="Search" 
+                    onClick={this.bingSearch}/>}
+                    
+
+
+
+                    {!MapBox2.UseBing ? null : <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} />}
+
+                    <div style={{ display: MapBox2.UseBing ? 'none' : undefined }}>
+                        <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
+                            <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
+                                <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
+                            </Autocomplete>
+
+                            {this.renderMarkers()}
+                            {this.allMapMarkers
+                                .filter(marker => marker.infoWindowOpen)
+                                .map(marker => (
+                                    <MapBoxInfoWindow
+                                        key={marker[Id]}
+                                        {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                                        place={marker}
+                                        markerMap={this.markerMap}
+                                        PanelWidth={this.infoWidth}
+                                        PanelHeight={this.infoHeight}
+                                        moveDocument={this.moveDocument}
+                                        isAnyChildContentActive={this.isAnyChildContentActive}
+                                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                                    />
+                                ))}
+                            {/* {this.handleMapCenter(this._map)} */}
+                        </GoogleMap>
+                    </div>
+
+                    {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+                        <MarqueeAnnotator
+                            rootDoc={this.rootDoc}
+                            anchorMenuClick={this.anchorMenuClick}
+                            scrollTop={0}
+                            down={this._marqueeing}
+                            scaling={returnOne}
+                            addDocument={this.addDocumentWrapper}
+                            docView={this.props.docViewPath().lastElement()}
+                            finishMarquee={this.finishMarquee}
+                            savedAnnotations={this.savedAnnotations}
+                            annotationLayer={this._annotationLayer.current}
+                            selectionText={returnEmptyString}
+                            mainCont={this._mainCont.current}
+                        />
+                    )}
+                </div>
+                {/* </LoadScript > */}
+                <div className="mapBox-sidebar" style={{ position: 'absolute', right: 0, height: '100%', width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+                    <SidebarAnnos
+                        ref={this._sidebarRef}
+                        {...this.props}
+                        fieldKey={this.fieldKey}
+                        rootDoc={this.rootDoc}
+                        layoutDoc={this.layoutDoc}
+                        dataDoc={this.dataDoc}
+                        showSidebar={this.SidebarShown}
+                        nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
+                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                        PanelWidth={this.sidebarWidth}
+                        sidebarAddDocument={this.sidebarAddDocument}
+                        moveDocument={this.moveDocument}
+                        removeDocument={this.sidebarRemoveDocument}
+                    />
+                </div>
+                {this.sidebarHandle}
+            </div>
+        );
+    }
+}
-- 
cgit v1.2.3-70-g09d2


From 5185db43b2e48f049690fadcee0081aca634cf4d Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Wed, 5 Apr 2023 15:13:33 -0400
Subject: Added infobox after pushpin clicked

---
 src/client/views/nodes/MapBox/MapBox.tsx | 93 ++++++++++++++++++++++----------
 1 file changed, 66 insertions(+), 27 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index b01426bcd..cc7d12128 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -606,16 +606,64 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     **/
 
     @observable
-    _bingLatitude:number | undefined;
-
-    @observable
-    _bingLongitude:number | undefined;
+    pins: any[] = []; // List of active pushpins
 
     bingSearchBarContents: any = "Boston, MA"; // For Bing Maps: The contents of the Bing search bar (string)
 
     geoDataRequestOptions = {
         entityType: 'PopulatedPlace'
     };
+
+    @action
+    updateView = ()=> {
+        this._bingMap.current.setView({
+            center: new this.MicrosoftMaps.Location(this.rootDoc.latitude, this.rootDoc.longitude),
+             // zoom: location
+         });
+    }
+
+    infobox:any; 
+
+    @action
+    createPushpin = (latitude:number, longitude:number) =>{
+        var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(latitude, longitude), {
+            title: this.bingSearchBarContents,
+            subTitle: 'subtitle here',
+            text: '1'
+        });
+
+        //Create an infobox at the pin
+        this.infobox = new this.MicrosoftMaps.Infobox(new this.MicrosoftMaps.Location(latitude, longitude), {
+            visible: false
+        });
+
+        //Assign the infobox to a map instance.
+        this.infobox.setMap(this._bingMap.current);
+        //Store some metadata with the pushpin.
+        pin.metadata = {
+        title: 'Pin Title',
+        description: 'Pin discription'
+        };
+        //Add a click event handler to the pushpin.
+        this.MicrosoftMaps.Events.addHandler(pin, 'click', this.pushpinClicked);
+        //Add pushpin to the map.
+        this._bingMap.current.entities.push(pin);
+    }
+
+
+    pushpinClicked = (e: { target: { metadata: { title: any; description: any; }; getLocation: () => any; }; }) => {
+        //Make sure the infobox has metadata to display.
+        if (e.target.metadata) {
+            //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e?
+            this.infobox.setOptions({
+            location: e.target.getLocation(),
+            title: e.target.metadata.title,
+            description: e.target.metadata.description,
+            visible: true
+            });
+        }
+    } 
+
     /*
      * For Bing Maps
      * Called by search button's onClick 
@@ -631,17 +679,21 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
       this.rootDoc.latitude = location.latitude;
       this.rootDoc.longitude = location.longitude;
+      this.updateView();
+
+
+
+      //Create custom Pushpin TODO: MAKE THIS ITS OWN METHOD
+      this.createPushpin(location.latitude, location.longitude);
+
+      
+        // // Adds all pins to the map
+        // for (let i = 0; i < temp.length; i++) {
+        //     this._bingMap.current.entities.push(temp[i]);
+        // }
+  }
 
-      // this.rootDoc.latitude =location.latitude;
-      // this.rootDoc.longitude =location.longitude; // TODO: How to update the rootDoc with the correct info?
-      //DocComponents file is where rootDoc is
 
-      // call a helper method that updates the this._bingMap.current.setView, 
-      // replaces this method call below
-      this._bingMap.current.setView({
-         center: new this.MicrosoftMaps.Location(location.latitude, location.longitude),
-          // zoom: location
-      });
       // this.MicrosoftMaps.SpatialDataService.GeoDataAPIManager.getBoundary(
       //     this._bingMap.current.getCenter(),
       //     this.geoDataRequestOptions,
@@ -658,19 +710,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
       //     }
       // );
 
-      //Create custom Pushpin
-      var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
-          title: this.bingSearchBarContents,
-          subTitle: 'subtitle here',
-          text: '1'
-      });
-
-      //Add the pushpin to the map
-      this._bingMap.current.entities.push(pin);
-      // const mapMarker = Docs.Create.MapMarkerDocument(this._bingMap.location.latitude, this._bingMap.location.latitude, false, [], {});
-      //     this.addDocument(mapMarker, this.annotationKey); // tells mapbox to add this marker to set of annotations on doc
-  }
-
   // _loadPending = true;
   // /**
   //  * store a reference to google map instance
@@ -780,7 +819,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         placeholder="..."
                         size="medium" 
                         text="Boston, MA"
-                        onKeyPress={e => console.log(e.key)}
+                        onKeyPress={(e: { key: any; }) => console.log(e.key)}
                     />}
                     
                     {!MapBox.UseBing ? null : 
-- 
cgit v1.2.3-70-g09d2


From e1494deb519cbd492be6cfe413b886bdfbb9404e Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Wed, 5 Apr 2023 17:30:59 -0400
Subject: Working on pushpin and infowindow datadoc stuff

---
 src/client/views/nodes/MapBox/MapBox.tsx  | 95 ++++++++++++++++---------------
 src/client/views/nodes/MapBox/MapBox2.tsx | 20 ++++++-
 2 files changed, 67 insertions(+), 48 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index cc7d12128..ac26fbe08 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -604,9 +604,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * 
      * 
     **/
-
-    @observable
-    pins: any[] = []; // List of active pushpins
+   
 
     bingSearchBarContents: any = "Boston, MA"; // For Bing Maps: The contents of the Bing search bar (string)
 
@@ -617,13 +615,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     updateView = ()=> {
         this._bingMap.current.setView({
-            center: new this.MicrosoftMaps.Location(this.rootDoc.latitude, this.rootDoc.longitude),
+            center: new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude),
              // zoom: location
          });
     }
 
-    infobox:any; 
 
+    infobox:any; 
     @action
     createPushpin = (latitude:number, longitude:number) =>{
         var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(latitude, longitude), {
@@ -641,13 +639,17 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.infobox.setMap(this._bingMap.current);
         //Store some metadata with the pushpin.
         pin.metadata = {
-        title: 'Pin Title',
-        description: 'Pin discription'
+            title: this.bingSearchBarContents,
+            description: 'Pin description'
         };
         //Add a click event handler to the pushpin.
         this.MicrosoftMaps.Events.addHandler(pin, 'click', this.pushpinClicked);
         //Add pushpin to the map.
         this._bingMap.current.entities.push(pin);
+
+        // Stores the pushpin as a MapMarkerDocument
+        const mapMarker = Docs.Create.MapMarkerDocument(NumCast(latitude), NumCast(longitude), false, [], {});
+        this.addDocument(mapMarker, this.annotationKey);
     }
 
 
@@ -656,13 +658,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (e.target.metadata) {
             //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e?
             this.infobox.setOptions({
-            location: e.target.getLocation(),
-            title: e.target.metadata.title,
-            description: e.target.metadata.description,
-            visible: true
+                location: e.target.getLocation(),
+                title: e.target.metadata.title,
+                description: e.target.metadata.description,
+                visible: true
             });
         }
-    } 
+    }
+
+    /**
+     * Returns a list of MapMarkerDocument
+     */
+    @computed get allMapPushpins() {
+        return DocListCast(this.dataDoc[this.annotationKey]);
+    }
 
     /*
      * For Bing Maps
@@ -670,20 +679,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Finds the geocode of the searched contents and sets location to that location
     **/
    @action
-   bingSearch = async() =>{ //TODO: PlaceResult, searching more formally
+   bingSearch = async() =>{ //TODO: searching more formally
 
       // clear all pins
       // this._bingMap.current.entities.clear();
 
       const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
 
-      this.rootDoc.latitude = location.latitude;
-      this.rootDoc.longitude = location.longitude;
+      this.dataDoc.latitude = location.latitude;
+      this.dataDoc.longitude = location.longitude;
       this.updateView();
-
-
-
-      //Create custom Pushpin TODO: MAKE THIS ITS OWN METHOD
+       // Each marker be its own document -- pin and info
+       
       this.createPushpin(location.latitude, location.longitude);
 
       
@@ -692,6 +699,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //     this._bingMap.current.entities.push(temp[i]);
         // }
   }
+    // const mapMarker = Docs.Create.MapMarkerDocument(NumCast(location.latitude), NumCast(location.longitude), false, [], {});
+    // this.addDocument(mapMarker, this.annotationKey);
 
 
       // this.MicrosoftMaps.SpatialDataService.GeoDataAPIManager.getBoundary(
@@ -710,6 +719,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
       //     }
       // );
 
+      
   // _loadPending = true;
   // /**
   //  * store a reference to google map instance
@@ -746,27 +756,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
   // };
 
 
-    // /**
-    //  * For Bing Maps
-    //  * Place the marker on bing maps & store the empty marker as a MapMarker Document in allMarkers list
-    //  * @param location - this.MicrosoftMaps.Location
-    //  * @param map - this._bingMap
-    //  */
-    // @action
-    // private placeMarker = (location: any) => {
-    //     const pin = new this.MicrosoftMaps.Pushpin(location, {
-    //         title: this.bingSearchBarContents,
-    //         subTitle: 'subtitle here',
-    //         text: '1'
-    //     });
-
-    //     this._bingMap.current.panTo(location);
-    //     this._bingMap.current.entities.push(pin);
-
-    //     // const mapMarker = Docs.Create.MapMarkerDocument(NumCast(location.latitude), NumCast(location.longitude), false, [], {});
-    //     // this.addDocument(mapMarker, this.annotationKey);
-    // };
-
 
 
 
@@ -774,17 +763,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * View options for bing maps
     */
     bingViewOptions = {
-        center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },// TODO: latitude: this.rootDoc.latitude, longitude: this.rootDoc.longitude
+        center: { latitude: this.dataDoc.latitude ?? defaultCenter.lat , longitude: this.dataDoc.longitude ?? defaultCenter.lng},
+        zoom: this.dataDoc.latitude ?? 10,
         mapTypeId: 'grayscale',
     };
 
     /**
      * Map options
-     * TODO: CHANGE TO BE MORE USER-FRIENDLY
      */
     bingMapOptions = {
         navigationBarMode: 'square',
+        backgroundColor:'#f1f3f4',
+        enableInertia:true,
     };
+
     bingMapReady = (map: any) => (this._bingMap = map.map);
     render() {
         const renderAnnotations = (docFilters?: () => string[]) => null;
@@ -826,10 +818,23 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     <input type="button" value="Search" 
                     onClick={this.bingSearch}/>}
                     
-
-
-
                     {!MapBox.UseBing ? null : <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} />}
+                    {this.allMapMarkers
+                                .filter(marker => marker.infoWindowOpen)
+                                .map(marker => (
+                                    <MapBoxInfoWindow
+                                        key={marker[Id]}
+                                        {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                                        place={marker}
+                                        markerMap={this.markerMap}
+                                        PanelWidth={this.infoWidth}
+                                        PanelHeight={this.infoHeight}
+                                        moveDocument={this.moveDocument}
+                                        isAnyChildContentActive={this.isAnyChildContentActive}
+                                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                                    />
+                                ))}
+
 
                     <div style={{ display: MapBox.UseBing ? 'none' : undefined }}>
                         <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index c11f76439..4c28d4df1 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -703,11 +703,25 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {!MapBox2.UseBing ? null : 
                     <input type="button" value="Search" 
                     onClick={this.bingSearch}/>}
-                    
-
-
+                
 
                     {!MapBox2.UseBing ? null : <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} />}
+                    {this.allMapMarkers
+                                .filter(marker => marker.infoWindowOpen)
+                                .map(marker => (
+                                    <MapBoxInfoWindow
+                                        key={marker[Id]}
+                                        {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                                        place={marker}
+                                        markerMap={this.markerMap}
+                                        PanelWidth={this.infoWidth}
+                                        PanelHeight={this.infoHeight}
+                                        moveDocument={this.moveDocument}
+                                        isAnyChildContentActive={this.isAnyChildContentActive}
+                                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                                    />
+                                ))}
+
 
                     <div style={{ display: MapBox2.UseBing ? 'none' : undefined }}>
                         <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
-- 
cgit v1.2.3-70-g09d2


From b703dc034c84ecc2842f34b562097ee253ba09ac Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Thu, 6 Apr 2023 10:24:55 -0400
Subject: Working on mapboxinfowindow integration

---
 src/client/views/nodes/MapBox/MapBox.tsx | 180 ++++++++++++++-----------------
 1 file changed, 81 insertions(+), 99 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index ac26fbe08..1892a5b61 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -624,29 +624,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     infobox:any; 
     @action
     createPushpin = (latitude:number, longitude:number) =>{
-        var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(latitude, longitude), {
-            title: this.bingSearchBarContents,
-            subTitle: 'subtitle here',
-            text: '1'
-        });
-
-        //Create an infobox at the pin
-        this.infobox = new this.MicrosoftMaps.Infobox(new this.MicrosoftMaps.Location(latitude, longitude), {
-            visible: false
-        });
-
-        //Assign the infobox to a map instance.
-        this.infobox.setMap(this._bingMap.current);
-        //Store some metadata with the pushpin.
-        pin.metadata = {
-            title: this.bingSearchBarContents,
-            description: 'Pin description'
-        };
-        //Add a click event handler to the pushpin.
-        this.MicrosoftMaps.Events.addHandler(pin, 'click', this.pushpinClicked);
-        //Add pushpin to the map.
-        this._bingMap.current.entities.push(pin);
-
         // Stores the pushpin as a MapMarkerDocument
         const mapMarker = Docs.Create.MapMarkerDocument(NumCast(latitude), NumCast(longitude), false, [], {});
         this.addDocument(mapMarker, this.annotationKey);
@@ -666,6 +643,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     }
 
+    //PushpinClicked using MapBoxInfoWindow
+    private pushpinClicked2 = (e: { target: { metadata: { title: any; description: any; }; getLocation: () => any; }; }, pin: Doc) => {
+        // // set which place was clicked
+        // this.selectedPlace = place;
+        pin.infoWindowOpen = true;
+    }
+
     /**
      * Returns a list of MapMarkerDocument
      */
@@ -682,81 +666,73 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
    bingSearch = async() =>{ //TODO: searching more formally
 
       // clear all pins
-      // this._bingMap.current.entities.clear();
+      this._bingMap.current.entities.clear();
+      //TODO: clear all infoboxes
 
       const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
 
       this.dataDoc.latitude = location.latitude;
       this.dataDoc.longitude = location.longitude;
       this.updateView();
-       // Each marker be its own document -- pin and info
-       
-      this.createPushpin(location.latitude, location.longitude);
 
-      
-        // // Adds all pins to the map
-        // for (let i = 0; i < temp.length; i++) {
-        //     this._bingMap.current.entities.push(temp[i]);
-        // }
-  }
-    // const mapMarker = Docs.Create.MapMarkerDocument(NumCast(location.latitude), NumCast(location.longitude), false, [], {});
-    // this.addDocument(mapMarker, this.annotationKey);
-
-
-      // this.MicrosoftMaps.SpatialDataService.GeoDataAPIManager.getBoundary(
-      //     this._bingMap.current.getCenter(),
-      //     this.geoDataRequestOptions,
-      //     this._bingMap.current,
-      //     function (data) {
-      //         if (data.results && data.results.length > 0) {
-      //             map.entities.push(data.results[0].Polygons);
-      //         }
-      //     },
-      //     null,
-      //     function errCallback(networkStatus, statusMessage) {
-      //         console.log(networkStatus);
-      //         console.log(statusMessage);
-      //     }
-      // );
 
       
-  // _loadPending = true;
-  // /**
-  //  * store a reference to google map instance
-  //  * setup the drawing manager on the top right corner of map
-  //  * fit map bounds to contain all markers
-  //  * 
-  //  */
-  // @action
-  // private loadHandler = () => {
-
-  //     // this._loadPending = true;
-
-  //     // // for making GoogleMap markers
-  //     // // const centerControlDiv = this.CenterControl();
-  //     // // map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv);
+    //   // Creates a temporary pin but does not add it to the dataDoc, UNCOMMENT LATER
+    //   var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
+    //     title: this.bingSearchBarContents,
+    //     subTitle: 'subtitle here',
+    //     text: '1',
+    //     color: 'blue'
+    // });
+    // this._bingMap.current.entities.push(pin);
       
-  //     // // this._bingMap.current.
+        this.createPushpin(location.latitude, location.longitude);  // Creates an actual pushpin
+        this.addAllPins();  // Adds all pushpins in the datadoc onto the map
 
-  //     // map.setZoom(NumCast(this.dataDoc.mapZoom, 2.5));
-  //     // map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
-  //     // setTimeout(() => {
-  //     //     if (this._loadPending && this._map.getBounds()) {
-  //     //         this._loadPending = false;
-  //     //         this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
-  //     //     }
-  //     // }, 250);
+        console.log(this.allMapPushpins
+            .filter(marker => marker.infoWindowOpen)
+            .length)
+   }
 
-  //     // // listener to addmarker event, creates pushpin onClick
-  //     // this._bingMap.addListener('click', (e: MouseEvent) => {
-  //     //     if (this.toggleAddMarker === true) {
-  //     //         this.placeMarker((e as any).latLng, map); //TODO: Implement placeMarker
-  //     //     }
-  //     // });
-  // };
+   /**
+    * Adds all pushpins in dataDoc onto the map
+    */
+   @action
+   addAllPins = () =>{
+    this.allMapPushpins.map(pin => this.createInfobox(pin)); 
+   }
 
+    @action
+    createInfobox = (pin:any) =>{
+        var pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {
+            title: this.bingSearchBarContents,
+            subTitle: 'subtitle here',
+            text: '1'
+        });
+        this._bingMap.current.entities.push(pushPin);
 
+        //Create an infobox at the pin
+        this.infobox = new this.MicrosoftMaps.Infobox(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {
+            visible: false
+        });
+        //Assign the infobox to a map instance.
+        this.infobox.setMap(this._bingMap.current);
+        //Store some metadata with the pushpin.
+        pushPin.metadata = {
+            title: pushPin.title,
+            description: 'Pin description'
+        };
 
+        //Add a click event handler to the pushpin.
+        // For bing maps infobox
+        // this.MicrosoftMaps.Events.addHandler(pushPin, 'click', this.pushpinClicked);
+
+        // For our infowindow
+        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e:any) => this.pushpinClicked2(e, pushPin));
+
+    }
+
+    
 
 
     /** 
@@ -818,22 +794,28 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     <input type="button" value="Search" 
                     onClick={this.bingSearch}/>}
                     
-                    {!MapBox.UseBing ? null : <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} />}
-                    {this.allMapMarkers
-                                .filter(marker => marker.infoWindowOpen)
-                                .map(marker => (
-                                    <MapBoxInfoWindow
-                                        key={marker[Id]}
-                                        {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
-                                        place={marker}
-                                        markerMap={this.markerMap}
-                                        PanelWidth={this.infoWidth}
-                                        PanelHeight={this.infoHeight}
-                                        moveDocument={this.moveDocument}
-                                        isAnyChildContentActive={this.isAnyChildContentActive}
-                                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                                    />
-                                ))}
+                    {!MapBox.UseBing ? null : 
+                    <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} >
+                        {this.allMapPushpins
+                            .filter(marker => marker.infoWindowOpen)
+                            .map(marker => (
+                                console.log('this is a marker window')
+                                // <MapBoxInfoWindow
+                                //     key={marker[Id]}
+                                //     {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                                //     place={marker}
+                                //     markerMap={this.markerMap}
+                                //     PanelWidth={this.infoWidth}
+                                //     PanelHeight={this.infoHeight}
+                                //     moveDocument={this.moveDocument}
+                                //     isAnyChildContentActive={this.isAnyChildContentActive}
+                                //     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                                // />
+                            ))}
+                    </BingMapsReact>
+                    
+                    }
+                    
 
 
                     <div style={{ display: MapBox.UseBing ? 'none' : undefined }}>
-- 
cgit v1.2.3-70-g09d2


From 4e2087a3d86ff2166ac4bca31d77850e3052ecfc Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Sat, 8 Apr 2023 17:42:19 -0400
Subject: Started on place pin mode

---
 src/client/views/nodes/MapBox/MapBox.tsx | 117 +++++++++++++++++++------------
 1 file changed, 74 insertions(+), 43 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 1892a5b61..ef45cdd1a 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -621,15 +621,19 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
 
-    infobox:any; 
+    
     @action
     createPushpin = (latitude:number, longitude:number) =>{
         // Stores the pushpin as a MapMarkerDocument
+        
         const mapMarker = Docs.Create.MapMarkerDocument(NumCast(latitude), NumCast(longitude), false, [], {});
         this.addDocument(mapMarker, this.annotationKey);
+        // mapMarker.infoWindowOpen = true;
+        console.log("original:" + mapMarker)
     }
 
 
+    infobox:any; 
     pushpinClicked = (e: { target: { metadata: { title: any; description: any; }; getLocation: () => any; }; }) => {
         //Make sure the infobox has metadata to display.
         if (e.target.metadata) {
@@ -648,6 +652,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // // set which place was clicked
         // this.selectedPlace = place;
         pin.infoWindowOpen = true;
+        console.log("later:" + pin.infoWindowOpen)
     }
 
     /**
@@ -662,45 +667,47 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Called by search button's onClick 
      * Finds the geocode of the searched contents and sets location to that location
     **/
-   @action
-   bingSearch = async() =>{ //TODO: searching more formally
-
-      // clear all pins
-      this._bingMap.current.entities.clear();
-      //TODO: clear all infoboxes
-
-      const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
-
-      this.dataDoc.latitude = location.latitude;
-      this.dataDoc.longitude = location.longitude;
-      this.updateView();
-
-
-      
-    //   // Creates a temporary pin but does not add it to the dataDoc, UNCOMMENT LATER
-    //   var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
-    //     title: this.bingSearchBarContents,
-    //     subTitle: 'subtitle here',
-    //     text: '1',
-    //     color: 'blue'
-    // });
-    // this._bingMap.current.entities.push(pin);
-      
-        this.createPushpin(location.latitude, location.longitude);  // Creates an actual pushpin
-        this.addAllPins();  // Adds all pushpins in the datadoc onto the map
-
-        console.log(this.allMapPushpins
-            .filter(marker => marker.infoWindowOpen)
-            .length)
-   }
-
-   /**
-    * Adds all pushpins in dataDoc onto the map
-    */
-   @action
-   addAllPins = () =>{
-    this.allMapPushpins.map(pin => this.createInfobox(pin)); 
-   }
+    @action
+    bingSearch = async() =>{
+
+        // clear all pins
+        this._bingMap.current.entities.clear();
+        //TODO: clear all infoboxes
+
+        const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
+
+        this.dataDoc.latitude = location.latitude;
+        this.dataDoc.longitude = location.longitude;
+        this.updateView();
+
+
+        
+        //   // Creates a temporary pin but does not add it to the dataDoc, UNCOMMENT LATER
+        //   var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
+        //     title: this.bingSearchBarContents,
+        //     subTitle: 'subtitle here',
+        //     text: '1',
+        //     color: 'blue'
+        // });
+        // this._bingMap.current.entities.push(pin);
+        
+            this.createPushpin(location.latitude, location.longitude);  // Creates an actual pushpin
+            this.addAllPins();  // Adds all pushpins in the datadoc onto the map
+
+            console.log(this.allMapPushpins
+                .filter(marker => marker.infoWindowOpen)
+                .length)
+            console.log(this.allMapPushpins
+                .map(marker => console.log(marker.infoWindowOpen)))
+    }
+
+    /**
+      * Adds all pushpins in dataDoc onto the map
+      */
+    @action
+    addAllPins = () =>{
+        this.allMapPushpins.map(pin => this.createInfobox(pin)); 
+    }
 
     @action
     createInfobox = (pin:any) =>{
@@ -720,8 +727,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //Store some metadata with the pushpin.
         pushPin.metadata = {
             title: pushPin.title,
-            description: 'Pin description'
+            description: 'Pin description',
         };
+        // pushPin.infoWindowOpen = true;
 
         //Add a click event handler to the pushpin.
         // For bing maps infobox
@@ -732,8 +740,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     }
 
-    
-
 
     /** 
      * View options for bing maps
@@ -753,6 +759,21 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         enableInertia:true,
     };
 
+
+
+    /**
+     * Toggles mode for placing a pin down on map
+     */
+    @observable
+    placePinOn: boolean | undefined;
+    @action
+    togglePlacePin = ()=>{
+        if (this.placePinOn)
+            this.placePinOn = false;
+        else this.placePinOn = true;
+    }
+
+
     bingMapReady = (map: any) => (this._bingMap = map.map);
     render() {
         const renderAnnotations = (docFilters?: () => string[]) => null;
@@ -793,6 +814,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {!MapBox.UseBing ? null : 
                     <input type="button" value="Search" 
                     onClick={this.bingSearch}/>}
+
+                    {!MapBox.UseBing ? null : 
+                        this.placePinOn ? 
+                            <input type="button" value="Place Pin Mode On" 
+                            onClick={this.togglePlacePin}/>
+                        : 
+                            <input type="button" value="Place Pin Mode Off" 
+                            onClick={this.togglePlacePin}/>
+                    }
+                    
                     
                     {!MapBox.UseBing ? null : 
                     <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} >
-- 
cgit v1.2.3-70-g09d2


From 1925afd8a5e2cea4fcb6d2e525264967a20f6841 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Mon, 10 Apr 2023 16:05:55 -0400
Subject: Pre-bob

---
 src/client/views/nodes/MapBox/MapBox.tsx           | 83 ++++++++++++++++------
 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx |  4 +-
 2 files changed, 64 insertions(+), 23 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index ef45cdd1a..902aa2d13 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -239,7 +239,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
         // set which place was clicked
         this.selectedPlace = place;
-        place.infoWindowOpen = true;
+        // place.infoWindowOpen = true;
     };
 
     /**
@@ -629,7 +629,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const mapMarker = Docs.Create.MapMarkerDocument(NumCast(latitude), NumCast(longitude), false, [], {});
         this.addDocument(mapMarker, this.annotationKey);
         // mapMarker.infoWindowOpen = true;
-        console.log("original:" + mapMarker)
     }
 
 
@@ -646,13 +645,25 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             });
         }
     }
+    onHover = (e: { target: { metadata: { title: any; description: any; }; getLocation: () => any; }; }) => {
+        //Make sure the infobox has metadata to display.
+        if (e.target.metadata) {
+            //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e?
+            this.infobox.setOptions({
+                location: e.target.getLocation(),
+                title: e.target.metadata.title,
+                description: e.target.metadata.description,
+                visible: true
+            });
+        }
+    }
 
     //PushpinClicked using MapBoxInfoWindow
     private pushpinClicked2 = (e: { target: { metadata: { title: any; description: any; }; getLocation: () => any; }; }, pin: Doc) => {
         // // set which place was clicked
         // this.selectedPlace = place;
         pin.infoWindowOpen = true;
-        console.log("later:" + pin.infoWindowOpen)
+        // console.log("later:" + pin.infoWindowOpen)
     }
 
     /**
@@ -661,7 +672,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get allMapPushpins() {
         return DocListCast(this.dataDoc[this.annotationKey]);
     }
+    /**
+     * Map OnClick create pushpin
+     */
+    @action
+    mapOnClick=(e: { location: { latitude: any; longitude: any;}; })=>{
+        if(this.placePinOn){
+            this.createPushpin(e.location.latitude, e.location.longitude);
+            this.addAllPins();
+            this.placePinOn = false;
+        }
+    }
+
 
+    searched_pin:any;
     /*
      * For Bing Maps
      * Called by search button's onClick 
@@ -669,10 +693,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     **/
     @action
     bingSearch = async() =>{
-
-        // clear all pins
-        this._bingMap.current.entities.clear();
-        //TODO: clear all infoboxes
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
+        this.addAllPins();
 
         const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
 
@@ -680,19 +702,19 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.dataDoc.longitude = location.longitude;
         this.updateView();
 
-
-        
-        //   // Creates a temporary pin but does not add it to the dataDoc, UNCOMMENT LATER
-        //   var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
-        //     title: this.bingSearchBarContents,
-        //     subTitle: 'subtitle here',
-        //     text: '1',
-        //     color: 'blue'
-        // });
-        // this._bingMap.current.entities.push(pin);
+        // Creates a temporary pin but does not add it to the dataDoc
+        var temp = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
+            // title: this.bingSearchBarContents,
+            // subTitle: 'subtitle here',
+            // text: '1',
+            color: 'blue'
+        });
+        if (temp!=this.searched_pin || this.searched_pin==null){
+            this._bingMap.current.entities.push(this.searched_pin);
+            this.searched_pin = temp;
+        }
+            
         
-            this.createPushpin(location.latitude, location.longitude);  // Creates an actual pushpin
-            this.addAllPins();  // Adds all pushpins in the datadoc onto the map
 
             console.log(this.allMapPushpins
                 .filter(marker => marker.infoWindowOpen)
@@ -706,15 +728,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
       */
     @action
     addAllPins = () =>{
+        this._bingMap.current.entities.clear(); 
+        //TODO: clear all infoboxes
+        if(this.searched_pin)
+            this._bingMap.current.entities.push(this.searched_pin);
         this.allMapPushpins.map(pin => this.createInfobox(pin)); 
+        this.allMapPushpins.map(pin => this._bingMap.current.entities.push(pin));
     }
 
     @action
     createInfobox = (pin:any) =>{
         var pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {
-            title: this.bingSearchBarContents,
-            subTitle: 'subtitle here',
-            text: '1'
+            // title: this.bingSearchBarContents,
+            // subTitle: 'subtitle here',
+            // text: '1'
         });
         this._bingMap.current.entities.push(pushPin);
 
@@ -737,6 +764,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         // For our infowindow
         this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e:any) => this.pushpinClicked2(e, pushPin));
+        this.MicrosoftMaps.Events.addHandler(pushPin, 'mouseover', (e:any) => this.onHover(e));
 
     }
 
@@ -846,6 +874,17 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     </BingMapsReact>
                     
                     }
+                    {/* <MapBoxInfoWindow
+                                    key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}
+                                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                                    place={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})}
+                                    markerMap={this.markerMap}
+                                    PanelWidth={this.infoWidth}
+                                    PanelHeight={this.infoHeight}
+                                    moveDocument={this.moveDocument}
+                                    isAnyChildContentActive={this.isAnyChildContentActive}
+                                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                                /> */}
                     
 
 
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index 00bedafbe..08ed7b40f 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -47,7 +47,9 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
     removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean);
     render() {
         return (
-            <InfoWindow anchor={this.props.markerMap[this.props.place[Id]]} onCloseClick={this.handleInfoWindowClose}>
+            <InfoWindow 
+            // anchor={this.props.markerMap[this.props.place[Id]]} 
+            onCloseClick={this.handleInfoWindowClose}>
                 <div className="mapbox-infowindow">
                     <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
                         <CollectionStackingView
-- 
cgit v1.2.3-70-g09d2


From ea3ee6661811bdfd4ff736c26dce5d3af23ff1dc Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Mon, 10 Apr 2023 18:24:31 -0400
Subject: Post-bob

---
 src/client/views/nodes/MapBox/MapBox.tsx | 93 +++++++++++---------------------
 1 file changed, 32 insertions(+), 61 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 902aa2d13..2298ddc60 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -5,6 +5,7 @@ import { EditableText } from 'browndash-components';
 import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import * as ReactDOM from 'react-dom/client';
 import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
@@ -628,7 +629,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         
         const mapMarker = Docs.Create.MapMarkerDocument(NumCast(latitude), NumCast(longitude), false, [], {});
         this.addDocument(mapMarker, this.annotationKey);
-        // mapMarker.infoWindowOpen = true;
+        mapMarker.infoWindowOpen = true;
     }
 
 
@@ -645,18 +646,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             });
         }
     }
-    onHover = (e: { target: { metadata: { title: any; description: any; }; getLocation: () => any; }; }) => {
-        //Make sure the infobox has metadata to display.
-        if (e.target.metadata) {
-            //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e?
-            this.infobox.setOptions({
-                location: e.target.getLocation(),
-                title: e.target.metadata.title,
-                description: e.target.metadata.description,
-                visible: true
-            });
-        }
-    }
 
     //PushpinClicked using MapBoxInfoWindow
     private pushpinClicked2 = (e: { target: { metadata: { title: any; description: any; }; getLocation: () => any; }; }, pin: Doc) => {
@@ -716,11 +705,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             
         
 
-            console.log(this.allMapPushpins
-                .filter(marker => marker.infoWindowOpen)
-                .length)
-            console.log(this.allMapPushpins
-                .map(marker => console.log(marker.infoWindowOpen)))
+            // console.log(this.allMapPushpins
+            //     .filter(marker => marker.infoWindowOpen)
+            //     .length)
+            // console.log(this.allMapPushpins
+            //     .map(marker => console.log(marker.infoWindowOpen)))
     }
 
     /**
@@ -744,11 +733,22 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             // text: '1'
         });
         this._bingMap.current.entities.push(pushPin);
+        
 
+        var id = Utils.GenerateGuid();
+        document.getElementById(id);
+        
+        var infoboxTemplate = '<div class="customInfobox"><div class="title">{title}</div>{description}</div>';
+        var infowindowtemp = '<MapBoxInfoWindow key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}{...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} place={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})} markerMap={this.markerMap} PanelWidth={this.infoWidth} PanelHeight={this.infoHeight} moveDocument={this.moveDocument} isAnyChildContentActive={this.isAnyChildContentActive} whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}/>'
         //Create an infobox at the pin
         this.infobox = new this.MicrosoftMaps.Infobox(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {
+            htmlContent: `<div id=${id} style="width:100; height:100; background:blue"/>`,
             visible: false
         });
+        const root = ReactDOM.createRoot(document.getElementById(id)!);
+        root.render(<div style={{width:100, height:100, background:"blue"}}>omg</div>);
+
+
         //Assign the infobox to a map instance.
         this.infobox.setMap(this._bingMap.current);
         //Store some metadata with the pushpin.
@@ -763,8 +763,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // this.MicrosoftMaps.Events.addHandler(pushPin, 'click', this.pushpinClicked);
 
         // For our infowindow
-        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e:any) => this.pushpinClicked2(e, pushPin));
-        this.MicrosoftMaps.Events.addHandler(pushPin, 'mouseover', (e:any) => this.onHover(e));
+        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e:any) => this.pushpinClicked(e));
+        // this.MicrosoftMaps.Events.addHandler(pushPin, 'mouseover', (e:any) => this.onHover(e));
 
     }
 
@@ -829,7 +829,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {this.annotationLayer}
 
 
-
                     {!MapBox.UseBing ? null : 
                     <EditableText editing 
                         onEdit={(newText: string) => this.bingSearchBarContents = newText}
@@ -858,21 +857,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         {this.allMapPushpins
                             .filter(marker => marker.infoWindowOpen)
                             .map(marker => (
-                                console.log('this is a marker window')
-                                // <MapBoxInfoWindow
-                                //     key={marker[Id]}
-                                //     {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
-                                //     place={marker}
-                                //     markerMap={this.markerMap}
-                                //     PanelWidth={this.infoWidth}
-                                //     PanelHeight={this.infoHeight}
-                                //     moveDocument={this.moveDocument}
-                                //     isAnyChildContentActive={this.isAnyChildContentActive}
-                                //     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                                // />
+                                // console.log('this is a marker window')
+                                <MapBoxInfoWindow
+                                    key={marker[Id]}
+                                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                                    place={marker}
+                                    markerMap={this.markerMap}
+                                    PanelWidth={this.infoWidth}
+                                    PanelHeight={this.infoHeight}
+                                    moveDocument={this.moveDocument}
+                                    isAnyChildContentActive={this.isAnyChildContentActive}
+                                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                                />
                             ))}
                     </BingMapsReact>
-                    
                     }
                     {/* <MapBoxInfoWindow
                                     key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}
@@ -887,33 +885,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 /> */}
                     
 
-
-                    <div style={{ display: MapBox.UseBing ? 'none' : undefined }}>
-                        <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
-                            <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
-                                <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
-                            </Autocomplete>
-
-                            {this.renderMarkers()}
-                            {this.allMapMarkers
-                                .filter(marker => marker.infoWindowOpen)
-                                .map(marker => (
-                                    <MapBoxInfoWindow
-                                        key={marker[Id]}
-                                        {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
-                                        place={marker}
-                                        markerMap={this.markerMap}
-                                        PanelWidth={this.infoWidth}
-                                        PanelHeight={this.infoHeight}
-                                        moveDocument={this.moveDocument}
-                                        isAnyChildContentActive={this.isAnyChildContentActive}
-                                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                                    />
-                                ))}
-                            {/* {this.handleMapCenter(this._map)} */}
-                        </GoogleMap>
-                    </div>
-
                     {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
                         <MarqueeAnnotator
                             rootDoc={this.rootDoc}
-- 
cgit v1.2.3-70-g09d2


From d0bfb8376bbff432691ca67a9547c374db8a1d07 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Wed, 12 Apr 2023 22:45:03 -0400
Subject: amongus

---
 src/client/views/nodes/MapBox/MapBox.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 2298ddc60..51b88fe5e 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -745,8 +745,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             htmlContent: `<div id=${id} style="width:100; height:100; background:blue"/>`,
             visible: false
         });
-        const root = ReactDOM.createRoot(document.getElementById(id)!);
-        root.render(<div style={{width:100, height:100, background:"blue"}}>omg</div>);
+        // const root = ReactDOM.createRoot(document.getElementById(id)!);
+        // root.render(<div style={{width:100, height:100, background:"blue"}}>omg</div>);
 
 
         //Assign the infobox to a map instance.
-- 
cgit v1.2.3-70-g09d2


From 4aa933c5b5da13cd10fe427b8513523b2e1d825f Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Wed, 12 Apr 2023 22:46:40 -0400
Subject: amongus2

---
 src/client/views/nodes/MapBox/MapBox.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 51b88fe5e..d0eb28f91 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -26,7 +26,7 @@ import { FieldView, FieldViewProps } from '../FieldView';
 import { PinProps } from '../trails';
 import './MapBox.scss';
 import { MapBoxInfoWindow } from './MapBoxInfoWindow';
-
+// amongus
 /**
  * MapBox architecture:
  * Main component: MapBox.tsx
-- 
cgit v1.2.3-70-g09d2


From f912a233a89c8772b22b71d34830ff4b0ba82310 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Thu, 4 May 2023 14:03:21 -0400
Subject: post-demo

---
 src/client/views/nodes/MapBox/MapBox.tsx | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index d0eb28f91..fed4535cb 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -26,6 +26,7 @@ import { FieldView, FieldViewProps } from '../FieldView';
 import { PinProps } from '../trails';
 import './MapBox.scss';
 import { MapBoxInfoWindow } from './MapBoxInfoWindow';
+import { ReactDOMServer } from "react";
 // amongus
 /**
  * MapBox architecture:
@@ -626,7 +627,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     createPushpin = (latitude:number, longitude:number) =>{
         // Stores the pushpin as a MapMarkerDocument
-        
         const mapMarker = Docs.Create.MapMarkerDocument(NumCast(latitude), NumCast(longitude), false, [], {});
         this.addDocument(mapMarker, this.annotationKey);
         mapMarker.infoWindowOpen = true;
@@ -670,6 +670,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this.createPushpin(e.location.latitude, e.location.longitude);
             this.addAllPins();
             this.placePinOn = false;
+            console.log("PUSHPIN CREATED")
         }
     }
 
@@ -702,7 +703,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this._bingMap.current.entities.push(this.searched_pin);
             this.searched_pin = temp;
         }
-            
+        
         
 
             // console.log(this.allMapPushpins
@@ -737,16 +738,28 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         var id = Utils.GenerateGuid();
         document.getElementById(id);
-        
         var infoboxTemplate = '<div class="customInfobox"><div class="title">{title}</div>{description}</div>';
-        var infowindowtemp = '<MapBoxInfoWindow key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}{...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} place={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})} markerMap={this.markerMap} PanelWidth={this.infoWidth} PanelHeight={this.infoHeight} moveDocument={this.moveDocument} isAnyChildContentActive={this.isAnyChildContentActive} whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}/>'
+        // var infowindowtemp = '<MapBoxInfoWindow key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}{...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} place={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})} markerMap={this.markerMap} PanelWidth={this.infoWidth} PanelHeight={this.infoHeight} moveDocument={this.moveDocument} isAnyChildContentActive={this.isAnyChildContentActive} whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}/>'
+        
+        // console.log(<div id={id} style={{width:100, height:100, background:"blue"}}/>)
+        // var htmlString = ReactDOMServer.renderToString(<div id={id} style={{width:100, height:100, background:"blue"}}/>);
+        
+
         //Create an infobox at the pin
         this.infobox = new this.MicrosoftMaps.Infobox(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {
-            htmlContent: `<div id=${id} style="width:100; height:100; background:blue"/>`,
+            htmlContent: infoboxTemplate, // style="width:100; height:100; background:blue"
             visible: false
         });
-        // const root = ReactDOM.createRoot(document.getElementById(id)!);
-        // root.render(<div style={{width:100, height:100, background:"blue"}}>omg</div>);
+        try{
+            // console.log(document.getElementById(id))
+            // console.log(this.infobox)
+            // const root = ReactDOM.createRoot(document.getElementById(id)!);
+            // root.render(<div style={{width:100, height:100, background:"blue"}}/>);
+        }
+        catch(e){
+            console.log(e)
+        }
+        
 
 
         //Assign the infobox to a map instance.
-- 
cgit v1.2.3-70-g09d2


From 480f155d69bd5546f04f0a9f5a78f026f8b6533a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 10 May 2023 10:44:30 -0400
Subject: formatting mapbox

---
 package-lock.json                         |  39 +++++++
 src/client/views/nodes/MapBox/MapBox.scss | 147 +++++++++++++-------------
 src/client/views/nodes/MapBox/MapBox.tsx  |  71 +++++--------
 src/client/views/nodes/MapBox/MapBox2.tsx | 168 ++----------------------------
 4 files changed, 151 insertions(+), 274 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/package-lock.json b/package-lock.json
index 196906718..111d1838a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5597,6 +5597,16 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
+        "d": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+            "dev": true,
+            "requires": {
+                "es5-ext": "^0.10.50",
+                "type": "^1.0.1"
+            }
+        },
         "d3": {
             "version": "7.8.4",
             "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz",
@@ -6977,6 +6987,28 @@
                 "is-symbol": "^1.0.2"
             }
         },
+        "es5-ext": {
+            "version": "0.10.62",
+            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
+            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+            "dev": true,
+            "requires": {
+                "es6-iterator": "^2.0.3",
+                "es6-symbol": "^3.1.3",
+                "next-tick": "^1.1.0"
+            }
+        },
+        "es6-iterator": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+            "dev": true,
+            "requires": {
+                "d": "1",
+                "es5-ext": "^0.10.35",
+                "es6-symbol": "^3.1.1"
+            }
+        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6988,6 +7020,7 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
+                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -22660,6 +22693,12 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
+        "type": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+            "dev": true
+        },
         "type-check": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index fb15520f6..539c506c7 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -1,87 +1,92 @@
-@import "../../global/globalCssVariables.scss";
+@import '../../global/globalCssVariables.scss';
 .mapBox {
-        width: 100%;
-        height: 100%;
-        overflow: hidden;
-        display: flex;
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+    display: flex;
 
-        .mapBox-infoWindow {
-            background-color: white;
-            opacity: 0.75;
-            padding: 12; 
-            font-size: 17;
-        }
+    .mapBox-infoWindow {
+        background-color: white;
+        opacity: 0.75;
+        padding: 12;
+        font-size: 17;
+    }
 
-        .mapBox-overlayButton-sidebar {
-            background: #121721;
-            height: 25px;
-            width: 25px;
-            right: 5px;
-            display: flex;
-            position: absolute;
-            align-items: center;
-            justify-content: center;
-            border-radius: 3px;
-            pointer-events: all;
-            z-index: 1; // so it appears on top of the document's title, if shown
-    
-            box-shadow: $standard-box-shadow;
-            transition: 0.2s;
-    
-            &:hover{
-                filter: brightness(0.85);
-            }
-        }
+    .mapBox-overlayButton-sidebar {
+        background: #121721;
+        height: 25px;
+        width: 25px;
+        right: 5px;
+        display: flex;
+        position: absolute;
+        align-items: center;
+        justify-content: center;
+        border-radius: 3px;
+        pointer-events: all;
+        z-index: 1; // so it appears on top of the document's title, if shown
 
-        .mapBox-wrapper {
-            width: 100%;
-            .mapBox-input {
-                box-sizing: border-box;
-                border: 1px solid transparent;
-                width: 240px;
-                height: 32px;
-                padding: 0 12px;
-                border-radius: 3px;
-                box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
-                font-size: 14px;
-                outline: none;
-                text-overflow: ellipses;
-                position: absolute;
-                left: 50%;
-                margin-left: -120px;
-            }
-        }
+        box-shadow: $standard-box-shadow;
+        transition: 0.2s;
 
-        .mapBox-sidebar-handle {
-            top: 0;
-            //top: calc(50% - 17.5px);  // use this to center vertically -- make sure it looks okay for slide views
-            width: 10px;
-            height: 100%;
-            max-height: 35px;
-            background: lightgray;
-            border-radius: 20px;
-            cursor:grabbing;
+        &:hover {
+            filter: brightness(0.85);
         }
-        .mapBox-addMarker {
+    }
+
+    .mapBox-wrapper {
+        width: 100%;
+        .mapBox-input {
+            box-sizing: border-box;
+            border: 1px solid transparent;
+            width: 240px;
+            height: 32px;
+            padding: 0 12px;
+            border-radius: 3px;
+            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
+            font-size: 14px;
+            outline: none;
+            text-overflow: ellipses;
+            position: absolute;
             left: 50%;
-            margin-left: 120px;
-            right: unset !important;
-            margin-top: -10;
-            height: max-content;
-        }
-        .searchbox {
-            display:none;
-        }
-        .mapBox-addMarker {
-            display:none;
+            margin-left: -120px;
         }
+    }
+    .mapBox-sidebar {
+        position: absolute;
+        right: 0;
+        height: 100%;
+    }
+
+    .mapBox-sidebar-handle {
+        top: 0;
+        //top: calc(50% - 17.5px);  // use this to center vertically -- make sure it looks okay for slide views
+        width: 10px;
+        height: 100%;
+        max-height: 35px;
+        background: lightgray;
+        border-radius: 20px;
+        cursor: grabbing;
+    }
+    .mapBox-addMarker {
+        left: 50%;
+        margin-left: 120px;
+        right: unset !important;
+        margin-top: -10;
+        height: max-content;
+    }
+    .searchbox {
+        display: none;
+    }
+    .mapBox-addMarker {
+        display: none;
+    }
 }
 
 .mapBox:hover {
     .mapBox-addMarker {
-        display:block;
+        display: block;
     }
     .searchbox {
-        display :block;
+        display: block;
     }
 }
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index c44f5765a..06021921c 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -55,14 +55,6 @@ const mapOptions = {
 };
 
 const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/  and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey>
-const apiKey = process.env.GOOGLE_MAPS;
-
-const script = document.createElement('script');
-script.defer = true;
-script.async = true;
-script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
-console.log(script.src);
-document.head.appendChild(script);
 
 /**
  * Consider integrating later: allows for drawing, circling, making shapes on map
@@ -89,7 +81,6 @@ const options = {
 
 @observer
 export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
-    static UseBing = true;
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -111,7 +102,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private markerMap: { [id: string]: google.maps.Marker } = {};
     @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
     @observable private _marqueeing: number[] | undefined;
-    @observable private _isAnnotating = false;
     @observable private inputRef = React.createRef<HTMLInputElement>();
     @observable private searchMarkers: google.maps.Marker[] = [];
     @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
@@ -139,11 +129,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.props.setContentView?.(this);
     }
 
-    @action
-    private setSearchBox = (searchBox: any) => {
-        this.searchBox = searchBox;
-    };
-
     // iterate allMarkers to size, center, and zoom map to contain all markers
     private fitBounds = (map: google.maps.Map) => {
         const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
@@ -503,7 +488,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
     @action finishMarquee = (x?: number, y?: number) => {
         this._marqueeing = undefined;
-        this._isAnnotating = false;
         x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false);
     };
 
@@ -813,33 +797,32 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
                     {this.annotationLayer}
 
-                    {!MapBox.UseBing ? null : <EditableText editing onEdit={(newText: string) => (this.bingSearchBarContents = newText)} placeholder="..." size="medium" text="Boston, MA" onKeyPress={(e: { key: any }) => console.log(e.key)} />}
-
-                    {!MapBox.UseBing ? null : <input type="button" value="Search" onClick={this.bingSearch} />}
-
-                    {!MapBox.UseBing ? null : this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />}
-
-                    {!MapBox.UseBing ? null : (
-                        <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions}>
-                            {this.allMapPushpins
-                                .filter(marker => marker.infoWindowOpen)
-                                .map(marker => (
-                                    // console.log('this is a marker window')
-                                    <MapBoxInfoWindow
-                                        key={marker[Id]}
-                                        {...this.props}
-                                        setContentView={emptyFunction}
-                                        place={marker}
-                                        markerMap={this.markerMap}
-                                        PanelWidth={this.infoWidth}
-                                        PanelHeight={this.infoHeight}
-                                        moveDocument={this.moveDocument}
-                                        isAnyChildContentActive={this.isAnyChildContentActive}
-                                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                                    />
-                                ))}
-                        </BingMapsReact>
-                    )}
+                    <EditableText setEditing={(isEditing: boolean) => ({})} editing onEdit={(newText: string) => (this.bingSearchBarContents = newText)} placeholder="..." text="Boston, MA" />
+
+                    <input type="button" value="Search" onClick={this.bingSearch} />
+
+                    {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />}
+
+                    <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions}>
+                        {this.allMapPushpins
+                            .filter(marker => marker.infoWindowOpen)
+                            .map(marker => (
+                                // console.log('this is a marker window')
+                                <MapBoxInfoWindow
+                                    key={marker[Id]}
+                                    {...this.props}
+                                    setContentView={emptyFunction}
+                                    place={marker}
+                                    markerMap={this.markerMap}
+                                    PanelWidth={this.infoWidth}
+                                    PanelHeight={this.infoHeight}
+                                    moveDocument={this.moveDocument}
+                                    isAnyChildContentActive={this.isAnyChildContentActive}
+                                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                                />
+                            ))}
+                    </BingMapsReact>
+
                     {/* <MapBoxInfoWindow
                                     key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}
                                     {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
@@ -870,7 +853,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     )}
                 </div>
                 {/* </LoadScript > */}
-                <div className="mapBox-sidebar" style={{ position: 'absolute', right: 0, height: '100%', width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+                <div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
                         {...this.props}
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 4c28d4df1..e3eeaba26 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -1,7 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
-import BingMapsReact from 'bingmaps-react';
-import { EditableText } from 'browndash-components';
 import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -9,7 +7,7 @@ import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
@@ -54,7 +52,6 @@ const mapOptions = {
     fullscreenControl: false,
 };
 
-const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/  and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey>
 const apiKey = process.env.GOOGLE_MAPS;
 
 const script = document.createElement('script');
@@ -88,15 +85,14 @@ const options = {
 } as google.maps.places.AutocompleteOptions;
 
 @observer
-export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
-    static UseBing = true;
+export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     @observable private _overlayAnnoInfo: Opt<Doc>;
     showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
     public static LayoutString(fieldKey: string) {
-        return FieldView.LayoutString(MapBox2, fieldKey);
+        return FieldView.LayoutString(MapBox, fieldKey);
     }
     public get SidebarKey() {
         return this.fieldKey + '-sidebar';
@@ -557,113 +553,9 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     savedAnnotations = () => this._savedAnnotations;
 
-    _bingSearchManager: any;
-    _bingMap: any;
     get MicrosoftMaps() {
         return (window as any).Microsoft.Maps;
     }
-    // uses Bing Search to retrieve lat/lng for a location.  eg.,
-    //   const results = this.geocodeQuery(map.map, 'Philadelphia, PA');
-    // to move the map to that location:
-    //     const location = await this.geocodeQuery(this._bingMap, 'Philadelphia, PA');
-    //     this._bingMap.current.setView({
-    //        mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
-    //        center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
-    //     });
-    //
-    bingGeocode = (map: any, query: string) => {
-        return new Promise<{ latitude: number; longitude: number }>((res, reject) => {
-            //If search manager is not defined, load the search module.
-            if (!this._bingSearchManager) {
-                //Create an instance of the search manager and call the geocodeQuery function again.
-                this.MicrosoftMaps.loadModule('Microsoft.Maps.Search', () => {
-                    this._bingSearchManager = new this.MicrosoftMaps.Search.SearchManager(map.current);
-                    res(this.bingGeocode(map, query));
-                });
-            } else {
-                this._bingSearchManager.geocode({   
-                    where: query,
-                    callback: action((r: any) => {
-                        res(r.results[0].location);
-                    }),
-                    errorCallback: (e: any) => reject(),
-                });
-            }
-        });
-    };
-
-
-
-
-    bingSearchBarContents: any = "Boston, MA"; // For Bing Maps: The contents of the Bing search bar (string)
-
-    /*
-     * For Bing Maps
-     * Called by search button's onClick 
-     * Finds the geocode of the searched contents and sets location to that location
-    **/
-   @action
-     bingSearch = async() =>{
-
-        // clear all pins
-        this._bingMap.current.entities.clear();
-
-        const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
-
-        // this.rootDoc.latitude =location.latitude;
-        // this.rootDoc.longitude =location.longitude; // TODO: How to update the rootDoc with the correct info?
-        //DocComponents file is where rootDoc is
-
-        // call a helper method that updates the this._bingMap.current.setView, 
-        // replaces this method call below
-        this._bingMap.current.setView({
-           center: new this.MicrosoftMaps.Location(location.latitude, location.longitude),
-        //    zoom: ,
-        });
-
-        //Create custom Pushpin
-        var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
-            title: this.bingSearchBarContents,
-            subTitle: 'subtitle here',
-            text: '1'
-        });
-
-        //Add the pushpin to the map
-        this._bingMap.current.entities.push(pin);
-        // const mapMarker = Docs.Create.MapMarkerDocument(this._bingMap.location.latitude, this._bingMap.location.latitude, false, [], {});
-        //     this.addDocument(mapMarker, this.annotationKey); // tells mapbox to add this marker to set of annotations on doc
-    }
-
-
-    // /**
-    //  * For Bing Maps
-    //  * Place the marker on bing maps & store the empty marker as a MapMarker Document in allMarkers list
-    //  * @param position - the LatLng position where the marker is placed
-    //  * @param map
-    //  */
-    // @action
-    // private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
-    //     const marker = new google.maps.Marker({
-    //         position: position,
-    //         map: map,
-    //     });
-    //     map.panTo(position);
-    //     const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
-    //     this.addDocument(mapMarker, this.annotationKey);
-    // };
-
-
-
-
-
-    bingViewOptions = {
-        center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },// TODO: latitude: this.rootDoc.latitude, longitude: this.rootDoc.longitude
-        mapTypeId: 'grayscale',
-    };
-    bingMapOptions = {
-        navigationBarMode: 'square',
-    };
-    bingMapReady = (map: any) => (this._bingMap = map.map);
     render() {
         const renderAnnotations = (docFilters?: () => string[]) => null;
         return (
@@ -673,57 +565,14 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     onWheel={e => e.stopPropagation()}
                     onPointerDown={async e => {
                         e.button === 0 && !e.ctrlKey && e.stopPropagation();
-                        // just a simple test of bing maps geocode api
-                        // const loc = await this.bingGeocode(this._bingMap, 'Philadelphia, PA');
-                        // this._bingMap.current.setView({
-                        //     mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
-                        //     center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
-                        //     zoom: 15,
-                        // });
                     }}
-                    style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
-
-                        
+                    style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
                     <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                     {renderAnnotations(this.opaqueFilter)}
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
                     {this.annotationLayer}
 
-
-
-                    {!MapBox2.UseBing ? null : 
-                    <EditableText editing 
-                        onEdit={(newText: string) => this.bingSearchBarContents = newText}
-                        placeholder="..."
-                        size="medium" 
-                        text="Boston, MA"
-                        onKeyPress={e => console.log(e.key)}
-                    />}
-                    
-                    {!MapBox2.UseBing ? null : 
-                    <input type="button" value="Search" 
-                    onClick={this.bingSearch}/>}
-                
-
-                    {!MapBox2.UseBing ? null : <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} />}
-                    {this.allMapMarkers
-                                .filter(marker => marker.infoWindowOpen)
-                                .map(marker => (
-                                    <MapBoxInfoWindow
-                                        key={marker[Id]}
-                                        {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
-                                        place={marker}
-                                        markerMap={this.markerMap}
-                                        PanelWidth={this.infoWidth}
-                                        PanelHeight={this.infoHeight}
-                                        moveDocument={this.moveDocument}
-                                        isAnyChildContentActive={this.isAnyChildContentActive}
-                                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                                    />
-                                ))}
-
-
-                    <div style={{ display: MapBox2.UseBing ? 'none' : undefined }}>
+                    <div>
                         <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
                             <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
                                 <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
@@ -735,7 +584,8 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 .map(marker => (
                                     <MapBoxInfoWindow
                                         key={marker[Id]}
-                                        {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                                        {...this.props}
+                                        setContentView={emptyFunction}
                                         place={marker}
                                         markerMap={this.markerMap}
                                         PanelWidth={this.infoWidth}
@@ -748,7 +598,6 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             {/* {this.handleMapCenter(this._map)} */}
                         </GoogleMap>
                     </div>
-
                     {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
                         <MarqueeAnnotator
                             rootDoc={this.rootDoc}
@@ -767,7 +616,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     )}
                 </div>
                 {/* </LoadScript > */}
-                <div className="mapBox-sidebar" style={{ position: 'absolute', right: 0, height: '100%', width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+                <div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
                         {...this.props}
@@ -775,6 +624,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         rootDoc={this.rootDoc}
                         layoutDoc={this.layoutDoc}
                         dataDoc={this.dataDoc}
+                        usePanelWidth={true}
                         showSidebar={this.SidebarShown}
                         nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
                         whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-- 
cgit v1.2.3-70-g09d2


From 14c57f0f70799ada841a03a150f4f759578fa69b Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Thu, 15 Jun 2023 13:33:30 -0400
Subject: added pushpin selected icon chagne

---
 src/client/views/SidebarAnnos.tsx        |  3 +++
 src/client/views/nodes/MapBox/MapBox.tsx | 43 +++++++++++++++++++-------------
 2 files changed, 29 insertions(+), 17 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 741e87644..42993bfc4 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -219,7 +219,10 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                     {Array.from(this.allMetadata.keys())
                         .sort()
                         .map(key => renderMeta(key, this.allMetadata.get(key)))}
+                    Hello
                 </div>
+
+
                 <div style={{ width: '100%', height: `calc(100% - 38px)`, position: 'relative' }}>
                     <CollectionStackingView
                         {...this.props}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index d87974bd4..fdd703604 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -104,7 +104,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private _marqueeing: number[] | undefined;
     @observable private inputRef = React.createRef<HTMLInputElement>();
     @observable private searchMarkers: google.maps.Marker[] = [];
-    @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
+    // @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @computed get allSidebarDocs() {
         return DocListCast(this.dataDoc[this.SidebarKey]);
@@ -607,17 +607,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     infobox: any;
-    pushpinClicked = (e: { target: { metadata: { title: any; description: any }; getLocation: () => any } }) => {
+    pushpinClicked = (e: {
+        isSelected: boolean; target: { metadata: { title: any; description: any }; getLocation: () => any } 
+}, pin:Doc) => {
+        // pin.isSelected = true;
         //Make sure the infobox has metadata to display.
-        if (e.target.metadata) {
-            //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e?
-            this.infobox.setOptions({
-                location: e.target.getLocation(),
-                title: e.target.metadata.title,
-                description: e.target.metadata.description,
-                visible: true,
-            });
-        }
+        // if (e.target.metadata) {
+        //     //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e?
+        //     this.infobox.setOptions({
+        //         location: e.target.getLocation(),
+        //         title: e.target.metadata.title,
+        //         description: e.target.metadata.description,
+        //         visible: true,
+        //     });
+        // }
     };
 
     //PushpinClicked using MapBoxInfoWindow
@@ -643,7 +646,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this.createPushpin(e.location.latitude, e.location.longitude);
             this.addAllPins();
             this.placePinOn = false;
-            console.log('PUSHPIN CREATED');
         }
     };
 
@@ -693,16 +695,23 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (this.searched_pin) this._bingMap.current.entities.push(this.searched_pin);
         this.allMapPushpins.map(pin => this.createInfobox(pin));
         this.allMapPushpins.map(pin => this._bingMap.current.entities.push(pin));
+        
+        var numSelected = 0
+        this.allMapPushpins.filter(pin => pin.isSelected).forEach(pin=>numSelected++)
+        console.log(numSelected)
+        
     };
 
     @action
     createInfobox = (pin: any) => {
+        var pushPin2 = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'});
         var pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {
-            // title: this.bingSearchBarContents,
-            // subTitle: 'subtitle here',
-            // text: '1'
+            title: this.bingSearchBarContents,
+            subTitle: 'subtitle here',
+            text: '1',
+            // height: '50px'
         });
-        this._bingMap.current.entities.push(pushPin);
+        this._bingMap.current.entities.push(pushPin2);
 
         var id = Utils.GenerateGuid();
         document.getElementById(id);
@@ -740,7 +749,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // this.MicrosoftMaps.Events.addHandler(pushPin, 'click', this.pushpinClicked);
 
         // For our infowindow
-        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(e));
+        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(e, pushPin));
         // this.MicrosoftMaps.Events.addHandler(pushPin, 'mouseover', (e:any) => this.onHover(e));
     };
 
-- 
cgit v1.2.3-70-g09d2


From 6059af1b5ebe7bd011635a56ea8f30519eb6037f Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Thu, 15 Jun 2023 14:03:59 -0400
Subject: day:

---
 src/client/views/nodes/MapBox/MapBox.tsx | 34 ++++++++++++++++++++------------
 1 file changed, 21 insertions(+), 13 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index fdd703604..ee7f89ac2 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -2,6 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { GoogleMapProps, Marker } from '@react-google-maps/api';
 import BingMapsReact from 'bingmaps-react';
 import { EditableText } from 'browndash-components';
+import e from 'connect-flash';
 import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -610,7 +611,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     pushpinClicked = (e: {
         isSelected: boolean; target: { metadata: { title: any; description: any }; getLocation: () => any } 
 }, pin:Doc) => {
-        // pin.isSelected = true;
+        pin["infoWindowOpen"] = !pin.infoWindowOpen;
+        console.log("Pin clicked")
         //Make sure the infobox has metadata to display.
         // if (e.target.metadata) {
         //     //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e?
@@ -627,7 +629,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private pushpinClicked2 = (e: { target: { metadata: { title: any; description: any }; getLocation: () => any } }, pin: Doc) => {
         // // set which place was clicked
         // this.selectedPlace = place;
-        pin.infoWindowOpen = true;
+        pin.infoWindowOpen = !pin.infoWindowOpen;
         // console.log("later:" + pin.infoWindowOpen)
     };
 
@@ -696,22 +698,28 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.allMapPushpins.map(pin => this.createInfobox(pin));
         this.allMapPushpins.map(pin => this._bingMap.current.entities.push(pin));
         
-        var numSelected = 0
-        this.allMapPushpins.filter(pin => pin.isSelected).forEach(pin=>numSelected++)
-        console.log(numSelected)
+        
         
     };
 
     @action
     createInfobox = (pin: any) => {
-        var pushPin2 = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'});
-        var pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {
-            title: this.bingSearchBarContents,
-            subTitle: 'subtitle here',
-            text: '1',
-            // height: '50px'
-        });
-        this._bingMap.current.entities.push(pushPin2);
+        var pushPin:any;
+        if (pin.infoWindowOpen){
+            pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {
+                title: this.bingSearchBarContents,
+                subTitle: 'subtitle here',
+                text: '1',
+                // height: '50px'
+            });
+        }
+        else{
+            pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'});
+        }
+        
+        this._bingMap.current.entities.push(pushPin);
+
+        console.log(this.allMapPushpins.filter(pin => pin.infoWindowOpen).length)
 
         var id = Utils.GenerateGuid();
         document.getElementById(id);
-- 
cgit v1.2.3-70-g09d2


From 1e41c94b0003822b102f0251f98676d80eb4b56b Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Tue, 20 Jun 2023 11:41:22 -0400
Subject: Basic selection

---
 src/client/views/nodes/MapBox/MapBox.tsx | 54 +++++++++++++-------------------
 1 file changed, 21 insertions(+), 33 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index ee7f89ac2..7d3dcb811 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -3,6 +3,7 @@ import { GoogleMapProps, Marker } from '@react-google-maps/api';
 import BingMapsReact from 'bingmaps-react';
 import { EditableText } from 'browndash-components';
 import e from 'connect-flash';
+import { truncateSync } from 'fs';
 import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -607,30 +608,21 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         mapMarker.infoWindowOpen = true;
     };
 
+
+    selectedPin: Doc | undefined;
     infobox: any;
-    pushpinClicked = (e: {
-        isSelected: boolean; target: { metadata: { title: any; description: any }; getLocation: () => any } 
-}, pin:Doc) => {
-        pin["infoWindowOpen"] = !pin.infoWindowOpen;
-        console.log("Pin clicked")
-        //Make sure the infobox has metadata to display.
-        // if (e.target.metadata) {
-        //     //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e?
-        //     this.infobox.setOptions({
-        //         location: e.target.getLocation(),
-        //         title: e.target.metadata.title,
-        //         description: e.target.metadata.description,
-        //         visible: true,
-        //     });
+    pushpinClicked = (pin:Doc) => {
+        pin["infoWindowOpen"] = 
+        // true
+        !pin.infoWindowOpen;
+        // if (this.selectedPin){
+        //     this.selectedPin.infoWindowOpen = false;
+        //     this.selectedPin = pin
         // }
-    };
-
-    //PushpinClicked using MapBoxInfoWindow
-    private pushpinClicked2 = (e: { target: { metadata: { title: any; description: any }; getLocation: () => any } }, pin: Doc) => {
-        // // set which place was clicked
-        // this.selectedPlace = place;
-        pin.infoWindowOpen = !pin.infoWindowOpen;
-        // console.log("later:" + pin.infoWindowOpen)
+        // else{
+        //     this.selectedPin = pin
+        // }
+        this.addAllPins();
     };
 
     /**
@@ -693,23 +685,21 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     addAllPins = () => {
         this._bingMap.current.entities.clear();
-        //TODO: clear all infoboxes
         if (this.searched_pin) this._bingMap.current.entities.push(this.searched_pin);
-        this.allMapPushpins.map(pin => this.createInfobox(pin));
-        this.allMapPushpins.map(pin => this._bingMap.current.entities.push(pin));
-        
         
+        this.allMapPushpins.map(pin => this.createPushPin(pin));
+
         
     };
 
     @action
-    createInfobox = (pin: any) => {
+    createPushPin = (pin: any) => {
         var pushPin:any;
         if (pin.infoWindowOpen){
             pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {
-                title: this.bingSearchBarContents,
-                subTitle: 'subtitle here',
-                text: '1',
+                // title: this.bingSearchBarContents,
+                // subTitle: 'subtitle here',
+                // text: '1',
                 // height: '50px'
             });
         }
@@ -719,8 +709,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         
         this._bingMap.current.entities.push(pushPin);
 
-        console.log(this.allMapPushpins.filter(pin => pin.infoWindowOpen).length)
-
         var id = Utils.GenerateGuid();
         document.getElementById(id);
         var infoboxTemplate = '<div class="customInfobox"><div class="title">{title}</div>{description}</div>';
@@ -757,7 +745,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // this.MicrosoftMaps.Events.addHandler(pushPin, 'click', this.pushpinClicked);
 
         // For our infowindow
-        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(e, pushPin));
+        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin));
         // this.MicrosoftMaps.Events.addHandler(pushPin, 'mouseover', (e:any) => this.onHover(e));
     };
 
-- 
cgit v1.2.3-70-g09d2


From 9d48c95e5a556f5be4abde83d9443e384a33197c Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Mon, 10 Jul 2023 14:26:37 -0400
Subject: Location metadata synced and reactions working

---
 src/client/documents/DocumentTypes.ts           |   2 +
 src/client/documents/Documents.ts               |  22 +-
 src/client/views/nodes/DocumentContentsView.tsx |   2 +
 src/client/views/nodes/MapBox/MapBox.tsx        | 264 +++++++++++++-----------
 src/client/views/nodes/MapBox/MapBox2.tsx       |   4 +-
 src/client/views/nodes/MapBox/MapMarkerBox.tsx  |   0
 src/client/views/nodes/MapBox/MapPushpinBox.tsx |  38 ++++
 src/client/views/nodes/trails/PresBox.tsx       |  19 +-
 src/fields/Doc.ts                               |   7 +
 9 files changed, 235 insertions(+), 123 deletions(-)
 delete mode 100644 src/client/views/nodes/MapBox/MapMarkerBox.tsx
 create mode 100644 src/client/views/nodes/MapBox/MapPushpinBox.tsx

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 2da3a24fd..2cfd9e680 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -27,6 +27,7 @@ export enum DocumentType {
     MAP = 'map',
     DATAVIZ = 'dataviz',
     LOADING = 'loading',
+    
 
     // special purpose wrappers that either take no data or are compositions of lower level types
     LINK = 'link',
@@ -40,6 +41,7 @@ export enum DocumentType {
     SEARCHITEM = 'searchitem',
     COMPARISON = 'comparison',
     GROUP = 'group',
+    PUSHPIN = "pushpin",
 
     LINKDB = 'linkdb', // database of links  ??? why do we have this
     SCRIPTDB = 'scriptdb', // database of scripts
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 849a5d6ae..5cced6d24 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -48,6 +48,7 @@ import { LinkBox } from '../views/nodes/LinkBox';
 import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup';
 import { LoadingBox } from '../views/nodes/LoadingBox';
 import { MapBox } from '../views/nodes/MapBox/MapBox';
+import {  MapPushpinBox } from '../views/nodes/MapBox/MapPushpinBox';
 import { PDFBox } from '../views/nodes/PDFBox';
 import { RecordingBox } from '../views/nodes/RecordingBox/RecordingBox';
 import { ScreenshotBox } from '../views/nodes/ScreenshotBox';
@@ -218,8 +219,8 @@ export class DocumentOptions {
     dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
     openFactoryLocation?: string; // an OpenWhere value to place the factory created document
     openFactoryAsDelegate?: boolean; //
-    lat?: number;
-    lng?: number;
+    lat?: NUMt = new NumInfo('latitude of a mapping view');
+    lng?: NUMt = new NumInfo('longitude of a mapping view');
     infoWindowOpen?: boolean;
     author?: string;
     _layout_fieldKey?: string;
@@ -286,6 +287,8 @@ export class DocumentOptions {
     viewTransitionTime?: number; // transition duration for view parameters
     presPanX?: number; // panX saved as a view spec
     presPanY?: number; // panY saved as a view spec
+    presLat?: NUMt = new NumInfo('latitude of a map'); // latitude of a map
+    presLong?: NUMt = new NumInfo('longitude of map'); // longitude of map
     presViewScale?: number; // viewScale saved as a view Spec
     presTransition?: number; //the time taken for the transition TO a document
     presDuration?: number; //the duration of the slide in presentation view
@@ -683,6 +686,13 @@ export namespace Docs {
                     options: { _layout_fitWidth: true, _fitHeight: true, nativeDimModifiable: true },
                 },
             ],
+            [
+                DocumentType.PUSHPIN,
+                {
+                    layout: { view: MapPushpinBox, dataField: defaultDataKey },
+                    options: {},
+                },
+            ],
         ]);
 
         const suffix = 'Proto';
@@ -1028,8 +1038,8 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.MAP), new List(documents), options);
         }
 
-        export function MapMarkerDocument(lat: number, lng: number, infoWindowOpen: boolean, documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.MARKER), new List(documents), { lat, lng, infoWindowOpen, ...options }, id);
+        export function PushpinDocument(lat: number, lng: number, infoWindowOpen: boolean, documents: Array<Doc>, options: DocumentOptions, id?: string) {
+            return InstanceFromProto(Prototypes.get(DocumentType.PUSHPIN), new List(documents), { lat, lng, infoWindowOpen, ...options }, id);
         }
 
         // shouldn't ever need to create a KVP document-- instead set the LayoutTemplateString to be a KeyValueBox for the DocumentView (see addDocTab in TabDocView)
@@ -1058,6 +1068,10 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
         }
 
+        export function MapanchorDocument(options: DocumentOptions = {}, id?: string) {
+            return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
+        }
+
         export function InkAnchorDocument(options: DocumentOptions, id?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
         }
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 348ef910a..9546a6d38 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -44,6 +44,7 @@ import { VideoBox } from './VideoBox';
 import { WebBox } from './WebBox';
 import React = require('react');
 import XRegExp = require('xregexp');
+import { MapPushpinBox } from './MapBox/MapPushpinBox';
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -266,6 +267,7 @@ export class DocumentContentsView extends React.Component<
                     ComparisonBox,
                     LoadingBox,
                     SchemaRowBox,
+                    MapPushpinBox,
                 }}
                 bindings={bindings}
                 jsx={layoutFrame}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 7d3dcb811..f1f6b0756 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -4,17 +4,19 @@ import BingMapsReact from 'bingmaps-react';
 import { EditableText } from 'browndash-components';
 import e from 'connect-flash';
 import { truncateSync } from 'fs';
-import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
+import { ScriptField } from '../../../../fields/ScriptField';
 import { NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
+import { Transform } from '../../../util/Transform';
 import { UndoManager } from '../../../util/UndoManager';
 import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
@@ -23,8 +25,9 @@ import { MarqueeAnnotator } from '../../MarqueeAnnotator';
 import { AnchorMenu } from '../../pdf/AnchorMenu';
 import { Annotation } from '../../pdf/Annotation';
 import { SidebarAnnos } from '../../SidebarAnnos';
+import { DocumentView } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
-import { PinProps } from '../trails';
+import { PinProps, PresBox } from '../trails';
 import './MapBox.scss';
 import { MapBoxInfoWindow } from './MapBoxInfoWindow';
 // amongus
@@ -126,11 +129,23 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     static _hadSelection: boolean = false;
     private _sidebarRef = React.createRef<SidebarAnnos>();
     private _ref: React.RefObject<HTMLDivElement> = React.createRef();
-
+    private _disposer: {[key:string]:IReactionDisposer} = {}
     componentDidMount() {
         this.props.setContentView?.(this);
+        this._disposer.location = reaction(() => ({lat:this.rootDoc.latitude, lng:this.rootDoc.longitude}), 
+        (locationObject) => {
+
+            //TODO: SAVE ZOOM? VIEW STYLE?
+            this._bingMap.current.setView({
+                center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng),
+            });
+
+        }, {fireImmediately: true});
     }
 
+    componentWillUnmount(): void {
+        Object.keys(this._disposer).forEach(key => this._disposer[key]?.())
+    }
     // iterate allMarkers to size, center, and zoom map to contain all markers
     private fitBounds = (map: google.maps.Map) => {
         const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
@@ -240,7 +255,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             map: map,
         });
         map.panTo(position);
-        const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
+        const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
         this.addDocument(mapMarker, this.annotationKey);
     };
 
@@ -368,7 +383,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 if (existingMarker) {
                     Doc.AddDocToList(existingMarker, 'data', doc);
                 } else {
-                    const marker = Docs.Create.MapMarkerDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
+                    const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
                     this.addDocument(marker, this.annotationKey);
                 }
             }
@@ -512,7 +527,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
     }
 
-    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc;
+    // Old get anchor function
+    // getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc;
 
     /**
      * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
@@ -592,57 +608,78 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         entityType: 'PopulatedPlace',
     };
 
-    @action
-    updateView = () => {
-        this._bingMap.current.setView({
-            center: new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude),
-            // zoom: location
-        });
-    };
+    
 
+    /*
+     * Creates Pushpin doc and adds it to the list of annotations
+     */
     @action
     createPushpin = (latitude: number, longitude: number) => {
         // Stores the pushpin as a MapMarkerDocument
-        const mapMarker = Docs.Create.MapMarkerDocument(NumCast(latitude), NumCast(longitude), false, [], {});
+        const mapMarker = Docs.Create.PushpinDocument(NumCast(latitude), NumCast(longitude), false, [], {});
         this.addDocument(mapMarker, this.annotationKey);
-        mapMarker.infoWindowOpen = true;
-    };
 
+        // mapMarker.infoWindowOpen = true;
+    };
 
-    selectedPin: Doc | undefined;
-    infobox: any;
+    /*
+     * Pushpin onclick
+     */
     pushpinClicked = (pin:Doc) => {
-        pin["infoWindowOpen"] = 
-        // true
-        !pin.infoWindowOpen;
-        // if (this.selectedPin){
-        //     this.selectedPin.infoWindowOpen = false;
-        //     this.selectedPin = pin
-        // }
-        // else{
-        //     this.selectedPin = pin
-        // }
-        this.addAllPins();
+        pin.infoWindowOpen = !pin.infoWindowOpen;
+
+    //     @action
+    // onPointerDown = (e: React.PointerEvent) => {
+    //     if (e.button === 2 || e.ctrlKey) {
+    //         AnchorMenu.Instance.Status = 'annotation';
+    //         AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
+    //         AnchorMenu.Instance.Pinned = false;
+    //         AnchorMenu.Instance.PinToPres = this.pinToPres;
+    //         AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle;
+    //         AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
+    //         AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion);
+    //         AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
+    //         e.stopPropagation();
+    //     } else if (e.button === 0) {
+    //         e.stopPropagation();
+    //         LinkFollower.FollowLink(undefined, this.annoTextRegion, false);
+    //     }
+    // };
+
+        // TODO: UPDATE FOR DASHDOC SELECTION
     };
 
     /**
-     * Returns a list of MapMarkerDocument
+     * Returns a list of Pushpin docs
      */
     @computed get allMapPushpins() {
         return DocListCast(this.dataDoc[this.annotationKey]);
     }
+
     /**
-     * Map OnClick create pushpin
+     * Map OnClick ~> creates a pushpin
      */
     @action
     mapOnClick = (e: { location: { latitude: any; longitude: any } }) => {
         if (this.placePinOn) {
             this.createPushpin(e.location.latitude, e.location.longitude);
-            this.addAllPins();
+            // this.addAllPins();
             this.placePinOn = false;
         }
     };
 
+
+    
+
+    /*
+     * Updates values of layout doc to match the current map
+     */
+    @action
+    updateLayout = () => {
+            this.dataDoc.latitude = this._bingMap.current.getCenter().latitude;
+            this.dataDoc.longitude = this._bingMap.current.getCenter().longitude;
+    };
+
     searched_pin: any;
     /*
      * For Bing Maps
@@ -651,20 +688,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      **/
     @action
     bingSearch = async () => {
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
-        this.addAllPins();
-
         const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
-
         this.dataDoc.latitude = location.latitude;
         this.dataDoc.longitude = location.longitude;
-        this.updateView();
+        // Centers on the searched location
+        this._bingMap.current.setView({
+            center: new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude),
+        });
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', this.updateLayout);
+
 
+        
         // Creates a temporary pin but does not add it to the dataDoc
         var temp = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
-            // title: this.bingSearchBarContents,
-            // subTitle: 'subtitle here',
-            // text: '1',
             color: 'blue',
         });
         if (temp != this.searched_pin || this.searched_pin == null) {
@@ -672,82 +709,65 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this.searched_pin = temp;
         }
 
-        // console.log(this.allMapPushpins
-        //     .filter(marker => marker.infoWindowOpen)
-        //     .length)
-        // console.log(this.allMapPushpins
-        //     .map(marker => console.log(marker.infoWindowOpen)))
     };
 
     /**
-     * Adds all pushpins in dataDoc onto the map
+     * Adds all pushpins in dataDoc onto the map (render) - OLD & UNUSED
      */
     @action
     addAllPins = () => {
         this._bingMap.current.entities.clear();
         if (this.searched_pin) this._bingMap.current.entities.push(this.searched_pin);
-        
-        this.allMapPushpins.map(pin => this.createPushPin(pin));
-
-        
+        // this.allMapPushpins.map(pin => this.addPushpin(pin));
     };
 
-    @action
-    createPushPin = (pin: any) => {
-        var pushPin:any;
-        if (pin.infoWindowOpen){
-            pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {
-                // title: this.bingSearchBarContents,
-                // subTitle: 'subtitle here',
-                // text: '1',
-                // height: '50px'
+    /*
+     * Returns doc w/ relevant info
+     */
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
+        const anchor =
+            Docs.Create.MapanchorDocument({
+                title: 'MapAnchor:' + this.rootDoc.title,
+                presLat: NumCast(this.dataDoc.latitude),
+                presLong: NumCast(this.dataDoc.longitude),
+                // presTransition: 1000,
+                unrendered: true,
+                annotationOn: this.rootDoc,
             });
+        if (anchor) {
+            if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
+            /* addAsAnnotation &&*/ this.addDocument(anchor);
+            PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc);
+            return anchor;
         }
-        else{
-            pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'});
-        }
-        
-        this._bingMap.current.entities.push(pushPin);
-
-        var id = Utils.GenerateGuid();
-        document.getElementById(id);
-        var infoboxTemplate = '<div class="customInfobox"><div class="title">{title}</div>{description}</div>';
-        // var infowindowtemp = '<MapBoxInfoWindow key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}{...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} place={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})} markerMap={this.markerMap} PanelWidth={this.infoWidth} PanelHeight={this.infoHeight} moveDocument={this.moveDocument} isAnyChildContentActive={this.isAnyChildContentActive} whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}/>'
-
-        // console.log(<div id={id} style={{width:100, height:100, background:"blue"}}/>)
-        // var htmlString = ReactDOMServer.renderToString(<div id={id} style={{width:100, height:100, background:"blue"}}/>);
-
-        //Create an infobox at the pin
-        this.infobox = new this.MicrosoftMaps.Infobox(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {
-            htmlContent: infoboxTemplate, // style="width:100; height:100; background:blue"
-            visible: false,
-        });
-        try {
-            // console.log(document.getElementById(id))
-            // console.log(this.infobox)
-            // const root = ReactDOM.createRoot(document.getElementById(id)!);
-            // root.render(<div style={{width:100, height:100, background:"blue"}}/>);
-        } catch (e) {
-            console.log(e);
-        }
+        return this.rootDoc;
+    };
 
-        //Assign the infobox to a map instance.
-        this.infobox.setMap(this._bingMap.current);
-        //Store some metadata with the pushpin.
-        pushPin.metadata = {
-            title: pushPin.title,
-            description: 'Pin description',
-        };
-        // pushPin.infoWindowOpen = true;
+    /*
+    *   Input: pin doc
+    *   Adds MicrosoftMaps Pushpin to the map (render)
+    */
+    @action
+    addPushpin = (pin: Doc) => {
+        const pushPin = pin.infoWindowOpen ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {}): new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'});
 
-        //Add a click event handler to the pushpin.
-        // For bing maps infobox
-        // this.MicrosoftMaps.Events.addHandler(pushPin, 'click', this.pushpinClicked);
+        
+        this._bingMap.current.entities.push(pushPin);
 
         // For our infowindow
         this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin));
-        // this.MicrosoftMaps.Events.addHandler(pushPin, 'mouseover', (e:any) => this.onHover(e));
-    };
+    }
+
+    /*
+    *   Input: pin doc
+    *   Adds MicrosoftMaps Pushpin to the map (render)
+    */
+    @action
+    removePushpin = (pin:any)=>{
+        this._bingMap.current.entities.clear();
+        this.allMapPushpins
+        this.allMapPushpins.map(pin => this.addPushpin(pin));
+    }
 
     /**
      * View options for bing maps
@@ -809,25 +829,37 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />}
 
                     <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions}>
+                    </BingMapsReact>
+                    <div>
                         {this.allMapPushpins
-                            .filter(marker => marker.infoWindowOpen)
-                            .map(marker => (
-                                // console.log('this is a marker window')
-                                <MapBoxInfoWindow
-                                    key={marker[Id]}
+                            .map(pushpin => (
+                                <DocumentView
                                     {...this.props}
-                                    setContentView={emptyFunction}
-                                    place={marker}
-                                    markerMap={this.markerMap}
-                                    PanelWidth={this.infoWidth}
-                                    PanelHeight={this.infoHeight}
-                                    moveDocument={this.moveDocument}
-                                    isAnyChildContentActive={this.isAnyChildContentActive}
-                                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                                />
+                                    
+                                    // HOW DO I PASS IN pushpin???
+                                    // HOW DO I MAKE SURE IT KNOWS TO MAKE MapPushpinBox???
+                                    Document={pushpin}
+                                    DataDoc={undefined}
+                                    PanelWidth={returnOne}
+                                    PanelHeight={returnOne}
+                                    NativeWidth={returnOne} 
+                                    NativeHeight={returnOne}
+                                    onClick={()=>new ScriptField(undefined)}
+                                    onKey={undefined}
+                                    onDoubleClick={undefined}
+                                    onBrowseClick={undefined}
+                                    docFilters={returnEmptyDoclist}
+                                    docRangeFilters={returnEmptyDoclist}
+                                    searchFilterDocs={returnEmptyDoclist}
+                                    isDocumentActive={returnFalse}
+                                    isContentActive={returnFalse}
+                                    addDocTab={returnFalse}
+                                    ScreenToLocalTransform={()=>new Transform(0,0,0)}
+                                    fitContentsToBox={undefined}
+                                    focus={returnOne}
+                        />
                             ))}
-                    </BingMapsReact>
-
+                    </div>
                     {/* <MapBoxInfoWindow
                                     key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}
                                     {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 9354f9639..f75b3a6a6 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -228,7 +228,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             map: map,
         });
         map.panTo(position);
-        const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
+        const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
         this.addDocument(mapMarker, this.annotationKey);
     };
 
@@ -332,7 +332,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 if (existingMarker) {
                     Doc.AddDocToList(existingMarker, 'data', doc);
                 } else {
-                    const marker = Docs.Create.MapMarkerDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
+                    const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
                     this.addDocument(marker, this.annotationKey);
                 }
             }
diff --git a/src/client/views/nodes/MapBox/MapMarkerBox.tsx b/src/client/views/nodes/MapBox/MapMarkerBox.tsx
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
new file mode 100644
index 000000000..13fb11054
--- /dev/null
+++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
@@ -0,0 +1,38 @@
+import { observer } from 'mobx-react';
+// import { SettingsManager } from '../../../util/SettingsManager';
+import { ViewBoxBaseComponent } from '../../DocComponent';
+import { FieldView, FieldViewProps } from '../FieldView';
+import React = require('react');
+import { computed } from 'mobx';
+import { MapBox } from './MapBox';
+
+
+/**
+ * Map Pushpin doc class
+ */
+@observer
+export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
+
+
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(MapPushpinBox, fieldKey);
+    }
+    componentDidMount() {
+        this.mapBoxView.addPushpin(this.rootDoc);
+    }
+    componentWillUnmount() {
+        this.mapBoxView.removePushpin(this.rootDoc);
+    }
+
+
+    @computed get mapBoxView() {
+        return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as MapBox;
+    }
+    @computed get mapBox() {
+        return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc;
+    }
+
+    render() {
+        return (<div></div>);
+    }
+}
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 858d83b7a..d1cfb86ae 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -41,6 +41,7 @@ export interface pinDataTypes {
     scrollable?: boolean;
     dataviz?: number[];
     pannable?: boolean;
+    map?:boolean;
     viewType?: boolean;
     inkable?: boolean;
     filters?: boolean;
@@ -378,6 +379,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const inkable = [DocumentType.INK].includes(targetType);
         const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetType) || target?._viewType === CollectionViewType.Stacking;
         const pannable = [DocumentType.IMG, DocumentType.PDF].includes(targetType) || (targetType === DocumentType.COL && target?._viewType === CollectionViewType.Freeform);
+        const map = [DocumentType.MAP].includes(targetType);
         const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(targetType);
         const clippable = [DocumentType.COMPARISON].includes(targetType);
         const datarange = [DocumentType.FUNCPLOT].includes(targetType);
@@ -387,7 +389,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const filters = true;
         const pivot = true;
         const dataannos = false;
-        return { scrollable, pannable, inkable, viewType, pivot, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos };
+        return { scrollable, pannable, inkable, viewType, pivot, map, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos };
     }
 
     @action
@@ -455,6 +457,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 changed = true;
             }
         }
+        if (pinDataTypes?.map || (!pinDataTypes && activeItem.presLat !== undefined)) {
+            if (bestTarget.latitude !== activeItem.presLat) {
+                Doc.SetInPlace(bestTarget, "latitude", NumCast(activeItem.presLat), true);
+                changed = true;
+            }
+            if (bestTarget.longitude !== activeItem.presLong) {
+                Doc.SetInPlace(bestTarget, "longitude", NumCast(activeItem.presLong), true);
+                bestTarget.restoreTargetOn = true;
+                changed = true;
+            }
+        }
         if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.presStartTime !== undefined)) {
             if (bestTarget._layout_currentTimecode !== activeItem.presStartTime) {
                 bestTarget._layout_currentTimecode = activeItem.presStartTime;
@@ -633,6 +646,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 pinDoc.presXRange = undefined; //targetDoc?.xrange;
                 pinDoc.presYRange = undefined; //targetDoc?.yrange;
             }
+            if (pinProps.pinData.map) {
+                pinDoc.presLat = targetDoc?.lat;
+                //...
+            }
             if (pinProps.pinData.poslayoutview)
                 pinDoc.presPinLayoutData = new List<string>(
                     DocListCast(targetDoc[fkey] as ObjectField).map(d =>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index deda4c876..0cf4256d6 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -522,6 +522,13 @@ export namespace Doc {
     export function IsDelegateField(doc: Doc, fieldKey: string) {
         return doc && Get(doc, fieldKey, true) !== undefined;
     }
+    //
+    // this will write the value to the key on either the data doc or the embedding doc.  The choice
+    // of where to write it is based on:
+    // 1) if the embedding Doc already has this field defined on it, then it will be written to the embedding
+    // 2) if the data doc has the field, then it's written there.
+    // 3) if neither already has the field, then 'defaultProto' determines whether to write it to the data doc (or the embedding)
+    //
     export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
         if (key.startsWith('_')) key = key.substring(1);
         const hasProto = Doc.GetProto(doc) !== doc ? Doc.GetProto(doc) : undefined;
-- 
cgit v1.2.3-70-g09d2


From b04306ce09bfd994c0535d77bae9c778dab97977 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Wed, 12 Jul 2023 11:40:44 -0400
Subject: Pre-zoom saving to doc

---
 src/client/views/nodes/MapBox/MapBox.tsx | 3 ---
 1 file changed, 3 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index f1f6b0756..3c8e6203a 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -835,9 +835,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             .map(pushpin => (
                                 <DocumentView
                                     {...this.props}
-                                    
-                                    // HOW DO I PASS IN pushpin???
-                                    // HOW DO I MAKE SURE IT KNOWS TO MAKE MapPushpinBox???
                                     Document={pushpin}
                                     DataDoc={undefined}
                                     PanelWidth={returnOne}
-- 
cgit v1.2.3-70-g09d2


From caa2e09edb9c1db0af571013d006179bdc84f755 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Mon, 31 Jul 2023 12:58:06 -0400
Subject: All mapType stuff and constructor is done

---
 src/client/documents/Documents.ts         |  3 ++
 src/client/views/nodes/MapBox/MapBox.tsx  | 70 +++++++++++++++++++++++--------
 src/client/views/nodes/trails/PresBox.tsx |  9 +++-
 3 files changed, 64 insertions(+), 18 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5cced6d24..ca62028c1 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -221,6 +221,7 @@ export class DocumentOptions {
     openFactoryAsDelegate?: boolean; //
     lat?: NUMt = new NumInfo('latitude of a mapping view');
     lng?: NUMt = new NumInfo('longitude of a mapping view');
+    zoom?: NUMt = new NumInfo('zoom of a mapping view');
     infoWindowOpen?: boolean;
     author?: string;
     _layout_fieldKey?: string;
@@ -289,6 +290,8 @@ export class DocumentOptions {
     presPanY?: number; // panY saved as a view spec
     presLat?: NUMt = new NumInfo('latitude of a map'); // latitude of a map
     presLong?: NUMt = new NumInfo('longitude of map'); // longitude of map
+    presZoom?: NUMt = new NumInfo('zoom of map'); // zoom of map
+    presMapType?:string;
     presViewScale?: number; // viewScale saved as a view Spec
     presTransition?: number; //the time taken for the transition TO a document
     presDuration?: number; //the duration of the slide in presentation view
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 3c8e6203a..ad534f5f2 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -132,15 +132,21 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _disposer: {[key:string]:IReactionDisposer} = {}
     componentDidMount() {
         this.props.setContentView?.(this);
-        this._disposer.location = reaction(() => ({lat:this.rootDoc.latitude, lng:this.rootDoc.longitude}), 
+        this._disposer.location = reaction(() => ({lat:this.rootDoc.latitude, lng:this.rootDoc.longitude, zoom:this.rootDoc.zoom,mapType:this.rootDoc.mapType}), 
         (locationObject) => {
 
-            //TODO: SAVE ZOOM? VIEW STYLE?
             this._bingMap.current.setView({
+                mapTypeId: locationObject.mapType,
+                zoom:locationObject.zoom,
                 center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng),
             });
+            // this._bingMap.current.setZoom(locationObject.zoom);
+
+
 
         }, {fireImmediately: true});
+
+        
     }
 
     componentWillUnmount(): void {
@@ -678,7 +684,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     updateLayout = () => {
             this.dataDoc.latitude = this._bingMap.current.getCenter().latitude;
             this.dataDoc.longitude = this._bingMap.current.getCenter().longitude;
+            this.dataDoc.zoom = this._bingMap.current.getZoom();
+            // this.dataDoc.mapType = new this.MicrosoftMaps.MapTypeId();
     };
+    /*
+     * Updates maptype
+     */
+    @action
+    updateMapType = () => {
+            this.dataDoc.mapType = this._bingMap.current.getMapTypeId();
+
+    };
+
 
     searched_pin: any;
     /*
@@ -691,13 +708,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
         this.dataDoc.latitude = location.latitude;
         this.dataDoc.longitude = location.longitude;
+        this.dataDoc.zoom = this._bingMap.current.getZoom();
+        // this.dataDoc.mapType = this._bingMap.current.getMapTypeId();
+        // this.bingSearchBarContents=location.name;
+
         // Centers on the searched location
-        this._bingMap.current.setView({
-            center: new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude),
-        });
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', this.updateLayout);
+        this._bingMap.current.panTo(new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude));
 
+        // this._bingMap.current.setView({
+        //     center: new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude),
+        // });
+        
+        
 
         
         // Creates a temporary pin but does not add it to the dataDoc
@@ -714,12 +736,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     /**
      * Adds all pushpins in dataDoc onto the map (render) - OLD & UNUSED
      */
-    @action
-    addAllPins = () => {
-        this._bingMap.current.entities.clear();
-        if (this.searched_pin) this._bingMap.current.entities.push(this.searched_pin);
-        // this.allMapPushpins.map(pin => this.addPushpin(pin));
-    };
+    // @action
+    // addAllPins = () => {
+    //     this._bingMap.current.entities.clear();
+    //     if (this.searched_pin) this._bingMap.current.entities.push(this.searched_pin);
+    //     // this.allMapPushpins.map(pin => this.addPushpin(pin));
+    // };
 
     /*
      * Returns doc w/ relevant info
@@ -730,7 +752,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 title: 'MapAnchor:' + this.rootDoc.title,
                 presLat: NumCast(this.dataDoc.latitude),
                 presLong: NumCast(this.dataDoc.longitude),
-                // presTransition: 1000,
+                presZoom: NumCast(this.dataDoc.zoom),
+                presMapType: this.dataDoc.mapType,
+                // presType: 
                 unrendered: true,
                 annotationOn: this.rootDoc,
             });
@@ -764,8 +788,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     */
     @action
     removePushpin = (pin:any)=>{
-        this._bingMap.current.entities.clear();
-        this.allMapPushpins
+        // this._bingMap.current.entities.clear();
+        // this.allMapPushpins
         this.allMapPushpins.map(pin => this.addPushpin(pin));
     }
 
@@ -798,7 +822,19 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         else this.placePinOn = true;
     };
 
-    bingMapReady = (map: any) => (this._bingMap = map.map);
+    /*
+     * Called when BingMap is first rendered 
+     * Initializes starting values
+     */
+    bingMapReady = (map: any) => {
+        this._bingMap = map.map;
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', this.updateLayout);
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', this.updateMapType);
+        this.updateLayout();
+        this.updateMapType();
+    }
+        
     render() {
         const renderAnnotations = (docFilters?: () => string[]) => null;
         return (
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index d1cfb86ae..0a907c958 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -464,7 +464,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
             if (bestTarget.longitude !== activeItem.presLong) {
                 Doc.SetInPlace(bestTarget, "longitude", NumCast(activeItem.presLong), true);
-                bestTarget.restoreTargetOn = true;
+                changed = true;
+            }
+            if (bestTarget.zoom !== activeItem.presZoom) {
+                Doc.SetInPlace(bestTarget, "zoom", NumCast(activeItem.presZoom), true);
+                changed = true;
+            }
+            if (bestTarget.mapType !== activeItem.presMapType) {
+                Doc.SetInPlace(bestTarget, "mapType", StrCast(activeItem.presMapType), true);
                 changed = true;
             }
         }
-- 
cgit v1.2.3-70-g09d2


From 94f122332d15b5844a2c46fc78f9a3a3125e7b06 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Mon, 31 Jul 2023 14:02:02 -0400
Subject: Pushpin onclick centers on pushpin

---
 src/client/views/nodes/MapBox/MapBox.tsx | 30 ++++++++++++++++++++++++++----
 1 file changed, 26 insertions(+), 4 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index ad534f5f2..8e460c090 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -634,6 +634,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     pushpinClicked = (pin:Doc) => {
         pin.infoWindowOpen = !pin.infoWindowOpen;
 
+        // TODO:
+        // if (sidebarannos is not open) open sidebarannos
+
+        // pan to pushpin location
+        this.dataDoc.latitude = pin.lat;
+        this.dataDoc.longitude = pin.lng;
+
+
     //     @action
     // onPointerDown = (e: React.PointerEvent) => {
     //     if (e.button === 2 || e.ctrlKey) {
@@ -685,6 +693,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this.dataDoc.latitude = this._bingMap.current.getCenter().latitude;
             this.dataDoc.longitude = this._bingMap.current.getCenter().longitude;
             this.dataDoc.zoom = this._bingMap.current.getZoom();
+            // if(this.dataDoc.mapType == 'x'){
+            //     this.dataDoc.locationToLookAt
+            // }
             // this.dataDoc.mapType = new this.MicrosoftMaps.MapTypeId();
     };
     /*
@@ -713,7 +724,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // this.bingSearchBarContents=location.name;
 
         // Centers on the searched location
-        this._bingMap.current.panTo(new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude));
+        // this._bingMap.current.panTo(new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude));
 
         // this._bingMap.current.setView({
         //     center: new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude),
@@ -727,7 +738,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             color: 'blue',
         });
         if (temp != this.searched_pin || this.searched_pin == null) {
-            this._bingMap.current.entities.push(this.searched_pin);
+            this._bingMap.current.entities.push(temp);
             this.searched_pin = temp;
         }
 
@@ -754,6 +765,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 presLong: NumCast(this.dataDoc.longitude),
                 presZoom: NumCast(this.dataDoc.zoom),
                 presMapType: this.dataDoc.mapType,
+                // preslocationToLookAt:this.dataDoc.locationToLookAt,
                 // presType: 
                 unrendered: true,
                 annotationOn: this.rootDoc,
@@ -773,7 +785,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     */
     @action
     addPushpin = (pin: Doc) => {
-        const pushPin = pin.infoWindowOpen ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {}): new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'});
+        const pushPin = pin.infoWindowOpen ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {}): new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), 
+        // {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'}
+        );
 
         
         this._bingMap.current.entities.push(pushPin);
@@ -806,9 +820,17 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Map options
      */
     bingMapOptions = {
-        navigationBarMode: 'square',
+        navigationBarMode: 'compact',
         backgroundColor: '#f1f3f4',
         enableInertia: true,
+        supportedMapTypes:['grayscale', 'canvasLight'],
+        // showScalebar:true
+        // disableRoadView:true,
+        // disableBirdseye:true
+        streetsideOptions:{
+            showProblemReporting:false,
+            showCurrentAddress:false,
+        },
     };
 
     /**
-- 
cgit v1.2.3-70-g09d2


From c059bdf0b2707181b4c6b7bf626f99ac33a81b58 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Tue, 1 Aug 2023 13:40:37 -0400
Subject: Pin onclick remove fully integrate, working but not tested a lot

---
 src/client/views/nodes/MapBox/MapBox.tsx        | 67 +++++++++++++++++++------
 src/client/views/nodes/MapBox/MapPushpinBox.tsx |  2 +-
 2 files changed, 52 insertions(+), 17 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 8e460c090..02fa31d9e 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -140,11 +140,36 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 zoom:locationObject.zoom,
                 center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng),
             });
-            // this._bingMap.current.setZoom(locationObject.zoom);
-
-
 
         }, {fireImmediately: true});
+        // this._disposer.pins = reaction(() => ({pinList:this.dataDoc[this.annotationKey]}), 
+        // (pinsObject) => {
+        //     this._bingMap.current.entities.clear();
+        //     pinsObject.pinList.map((pushpin: Doc) => (
+        //                         <DocumentView
+        //                             {...this.props}
+        //                             Document={pushpin}
+        //                             DataDoc={undefined}
+        //                             PanelWidth={returnOne}
+        //                             PanelHeight={returnOne}
+        //                             NativeWidth={returnOne} 
+        //                             NativeHeight={returnOne}
+        //                             onClick={()=>new ScriptField(undefined)}
+        //                             onKey={undefined}
+        //                             onDoubleClick={undefined}
+        //                             onBrowseClick={undefined}
+        //                             docFilters={returnEmptyDoclist}
+        //                             docRangeFilters={returnEmptyDoclist}
+        //                             searchFilterDocs={returnEmptyDoclist}
+        //                             isDocumentActive={returnFalse}
+        //                             isContentActive={returnFalse}
+        //                             addDocTab={returnFalse}
+        //                             ScreenToLocalTransform={()=>new Transform(0,0,0)}
+        //                             fitContentsToBox={undefined}
+        //                             focus={returnOne}
+        //                 />));
+
+        // }, {fireImmediately: false});
 
         
     }
@@ -615,15 +640,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     
-
+    incrementer: number = 0;
     /*
      * Creates Pushpin doc and adds it to the list of annotations
      */
     @action
     createPushpin = (latitude: number, longitude: number) => {
         // Stores the pushpin as a MapMarkerDocument
-        const mapMarker = Docs.Create.PushpinDocument(NumCast(latitude), NumCast(longitude), false, [], {});
+        const mapMarker = Docs.Create.PushpinDocument(NumCast(latitude), NumCast(longitude), false, [], {},'pushpinIDamongus'+this.incrementer);
         this.addDocument(mapMarker, this.annotationKey);
+        this.incrementer++;
 
         // mapMarker.infoWindowOpen = true;
     };
@@ -631,15 +657,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     /*
      * Pushpin onclick
      */
-    pushpinClicked = (pin:Doc) => {
-        pin.infoWindowOpen = !pin.infoWindowOpen;
-
+    @action
+    pushpinClicked = (pinDoc:Doc,pin:any) => {
         // TODO:
         // if (sidebarannos is not open) open sidebarannos
+        // creates button onclick removes the doc from annotations
 
         // pan to pushpin location
-        this.dataDoc.latitude = pin.lat;
-        this.dataDoc.longitude = pin.lng;
+        this.dataDoc.latitude = pinDoc.lat;
+        this.dataDoc.longitude = pinDoc.lng;
+        // this.dataDoc[this.annotationKey].pop(pin);
+        this.removePushpin(pinDoc,pin);
+        
+
+        
 
 
     //     @action
@@ -792,19 +823,22 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         
         this._bingMap.current.entities.push(pushPin);
 
-        // For our infowindow
-        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin));
+        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin,pushPin));
     }
 
     /*
     *   Input: pin doc
-    *   Adds MicrosoftMaps Pushpin to the map (render)
+    *   Removes MicrosoftMaps Pushpin to the map (render)
     */
     @action
-    removePushpin = (pin:any)=>{
-        // this._bingMap.current.entities.clear();
+    removePushpin = (pinDoc:Doc,pin:any)=>{
         // this.allMapPushpins
-        this.allMapPushpins.map(pin => this.addPushpin(pin));
+        // this.allMapPushpins.map(pin => this.addPushpin(pin));
+        // this._bingMap.current.entities.clear();
+        this._bingMap.current.entities.remove(pin);
+        this.removeDocument(pinDoc, this.annotationKey);
+
+        // this.dataDoc[this.annotationKey]
     }
 
     /**
@@ -889,6 +923,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions}>
                     </BingMapsReact>
                     <div>
+                        
                         {this.allMapPushpins
                             .map(pushpin => (
                                 <DocumentView
diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
index 13fb11054..588bddd1a 100644
--- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx
+++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
@@ -21,7 +21,7 @@ export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.mapBoxView.addPushpin(this.rootDoc);
     }
     componentWillUnmount() {
-        this.mapBoxView.removePushpin(this.rootDoc);
+        // this.mapBoxView.removePushpin(this.rootDoc);
     }
 
 
-- 
cgit v1.2.3-70-g09d2


From e1968a5f809629305f4358fe3942ad38c15e0ff2 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Tue, 1 Aug 2023 14:04:52 -0400
Subject: omg im so tired but yay remove pin works

---
 src/client/views/nodes/MapBox/MapBox.tsx | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 02fa31d9e..61296c6c8 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -826,6 +826,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin,pushPin));
     }
 
+
+    @observable
+    pinIsSelected_TEMPORARY:boolean=false; // toggles if remove pin button appears
+
     /*
     *   Input: pin doc
     *   Removes MicrosoftMaps Pushpin to the map (render)
@@ -858,6 +862,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         backgroundColor: '#f1f3f4',
         enableInertia: true,
         supportedMapTypes:['grayscale', 'canvasLight'],
+        disableMapTypeSelectorMouseOver :true,
         // showScalebar:true
         // disableRoadView:true,
         // disableBirdseye:true
@@ -919,7 +924,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     <input type="button" value="Search" onClick={this.bingSearch} />
 
                     {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />}
-
+                    {/* {this.pinIsSelected_TEMPORARY? <input type="button" value="Delete Pin" onClick={this.removeSelectedPin} /> : null} */}
                     <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions}>
                     </BingMapsReact>
                     <div>
-- 
cgit v1.2.3-70-g09d2


From 8a58d0205111e7a653c0a35acecb41790882cb7b Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Wed, 2 Aug 2023 16:40:03 -0400
Subject: Pre master

---
 src/client/views/nodes/MapBox/MapBox.tsx | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 61296c6c8..d5787db3d 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -654,6 +654,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // mapMarker.infoWindowOpen = true;
     };
 
+    /*
+     * Pushpin dblclick
+     */
+    @action
+    pushpinDblClicked = (pinDoc:Doc,pin:any) => {
+        this.removePushpin(pinDoc,pin);
+    };
     /*
      * Pushpin onclick
      */
@@ -666,9 +673,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // pan to pushpin location
         this.dataDoc.latitude = pinDoc.lat;
         this.dataDoc.longitude = pinDoc.lng;
-        // this.dataDoc[this.annotationKey].pop(pin);
-        this.removePushpin(pinDoc,pin);
-        
 
         
 
@@ -824,9 +828,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this._bingMap.current.entities.push(pushPin);
 
         this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin,pushPin));
+        this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pin,pushPin));
     }
 
 
+
     @observable
     pinIsSelected_TEMPORARY:boolean=false; // toggles if remove pin button appears
 
-- 
cgit v1.2.3-70-g09d2


From 7b2553514bb000eb7f618eb0f0d653baee78742c Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Sun, 13 Aug 2023 15:43:53 -0400
Subject: 1

---
 src/client/views/UndoStack.scss                    |   29 +
 src/client/views/UndoStack.tsx                     |   47 +
 src/client/views/nodes/MapBox/MapBox.tsx           |   12 +-
 .../nodes/PhysicsBox/PhysicsSimulationBox.scss     |   90 +
 .../nodes/PhysicsBox/PhysicsSimulationBox.tsx      | 1987 ++++++++++++++++++++
 .../PhysicsBox/PhysicsSimulationInputField.tsx     |  179 ++
 .../PhysicsBox/PhysicsSimulationQuestions.json     |  161 ++
 .../PhysicsBox/PhysicsSimulationTutorial.json      |  600 ++++++
 .../nodes/PhysicsBox/PhysicsSimulationWall.tsx     |   34 +
 .../nodes/PhysicsBox/PhysicsSimulationWeight.tsx   |  990 ++++++++++
 src/fields/DocSymbols.ts                           |   26 +
 11 files changed, 4150 insertions(+), 5 deletions(-)
 create mode 100644 src/client/views/UndoStack.scss
 create mode 100644 src/client/views/UndoStack.tsx
 create mode 100644 src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.scss
 create mode 100644 src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
 create mode 100644 src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx
 create mode 100644 src/client/views/nodes/PhysicsBox/PhysicsSimulationQuestions.json
 create mode 100644 src/client/views/nodes/PhysicsBox/PhysicsSimulationTutorial.json
 create mode 100644 src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx
 create mode 100644 src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
 create mode 100644 src/fields/DocSymbols.ts

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/UndoStack.scss b/src/client/views/UndoStack.scss
new file mode 100644
index 000000000..ab21e6d7e
--- /dev/null
+++ b/src/client/views/UndoStack.scss
@@ -0,0 +1,29 @@
+.undoStack-outerContainer {
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    position: relative;
+    pointer-events: all;
+    padding-left: 4px;
+}
+
+.undoStack-resultContainer {
+    border-radius: 5px;
+}
+
+.undoStack-commandInput {
+    width: 100%;
+}
+
+.undoStack-commandResult,
+.undoStack-commandString {
+    overflow-wrap: break-word;
+}
+
+.undoStack-commandsContainer {
+    background-color: whitesmoke;
+    flex: 1 1 auto;
+    overflow-y: scroll;
+    height: 30px;
+    border-radius: 5px;
+}
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
new file mode 100644
index 000000000..f5af09e5b
--- /dev/null
+++ b/src/client/views/UndoStack.tsx
@@ -0,0 +1,47 @@
+import { action, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { UndoManager } from '../util/UndoManager';
+import './UndoStack.scss';
+
+interface UndoStackProps {
+    width?: number;
+    height?: number;
+    inline?: boolean;
+}
+@observer
+export class UndoStack extends React.Component<UndoStackProps> {
+    @observable static HideInline: boolean;
+    @observable static Expand: boolean;
+    render() {
+        return this.props.inline && UndoStack.HideInline ? null : (
+            <div
+                className="undoStack-outerContainer"
+                style={{ width: this.props.width, height: this.props.height ? (UndoStack.Expand ? 4 : 1) * this.props.height : undefined, top: UndoStack.Expand && this.props.height ? -this.props.height * 3 : undefined }}
+                onClick={action(e => (UndoStack.Expand = !UndoStack.Expand))}
+                onDoubleClick={action(e => (UndoStack.Expand = UndoStack.HideInline = false))}>
+                <div className="undoStack-commandsContainer" ref={r => r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })} style={{ background: UndoManager.batchCounter.get() ? 'yellow' : undefined }}>
+                    <div className="undoStack-resultContainer" key={0}>
+                        <div className="undoStack-commandString" style={{ fontWeight: 'bold', textAlign: 'center' }}>
+                            Undo/Redo Stack
+                        </div>
+                    </div>
+                    {UndoManager.undoStackNames.map((name, i) => (
+                        <div className="undoStack-resultContainer" key={i}>
+                            <div className="undoStack-commandString">{name.replace(/[^\.]*\./, '')}</div>
+                        </div>
+                    ))}
+                    {Array.from(UndoManager.redoStackNames)
+                        .reverse()
+                        .map((name, i) => (
+                            <div className="undoStack-resultContainer" key={i}>
+                                <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: 'red' }}>
+                                    {name.replace(/[^\.]*\./, '')}
+                                </div>
+                            </div>
+                        ))}
+                </div>
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index d5787db3d..bfb0a6161 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -7,7 +7,8 @@ import { truncateSync } from 'fs';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
+import { Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { ScriptField } from '../../../../fields/ScriptField';
@@ -449,7 +450,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         .ScreenToLocalTransform()
                         .scale(this.props.NativeDimScaling?.() || 1)
                         .transformDirection(delta[0], delta[1]);
-                    const fullWidth = this.layoutDoc[WidthSym]();
+                    const fullWidth = this.layoutDoc[Width]();
                     const mapWidth = fullWidth - this.sidebarWidth();
                     if (this.sidebarWidth() + localDelta[0] > 0) {
                         this._showSidebar = true;
@@ -640,16 +641,17 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     
-    incrementer: number = 0;
+    // incrementer: number = 0;
     /*
      * Creates Pushpin doc and adds it to the list of annotations
      */
     @action
     createPushpin = (latitude: number, longitude: number) => {
         // Stores the pushpin as a MapMarkerDocument
-        const mapMarker = Docs.Create.PushpinDocument(NumCast(latitude), NumCast(longitude), false, [], {},'pushpinIDamongus'+this.incrementer);
+        const mapMarker = Docs.Create.PushpinDocument(NumCast(latitude), NumCast(longitude), false, [], {}
+        // ,'pushpinIDamongus'+ this.incrementer++
+        );
         this.addDocument(mapMarker, this.annotationKey);
-        this.incrementer++;
 
         // mapMarker.infoWindowOpen = true;
     };
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.scss b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.scss
new file mode 100644
index 000000000..ac2c611c7
--- /dev/null
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.scss
@@ -0,0 +1,90 @@
+.physicsSimApp {
+    * {
+        box-sizing: border-box;
+        font-size: 14px;
+    }
+
+    .mechanicsSimulationContainer {
+        background-color: white;
+        height: 100%;
+        width: 100%;
+        display: flex;
+
+        .mechanicsSimulationEquationContainer {
+            position: fixed;
+            left: 60%;
+            padding: 1em;
+            height: 100%;
+            transform-origin: top left;
+
+            .mechanicsSimulationControls {
+                display: flex;
+                justify-content: space-between;
+            }
+        }
+        .rod,
+        .spring,
+        .wheel,
+        .showvecs,
+        .wedge {
+            pointer-events: none;
+            position: absolute;
+            left: 0;
+            top: 0;
+        }
+    }
+
+    .coordinateSystem {
+        z-index: -100;
+    }
+
+    th,
+    td {
+        border-collapse: collapse;
+        padding: 1em;
+    }
+
+    table {
+        min-width: 300px;
+    }
+
+    tr:nth-child(even) {
+        background-color: #d6eeee;
+    }
+
+    button {
+        z-index: 50;
+    }
+
+    .angleLabel {
+        font-weight: bold;
+        font-size: 20px;
+        user-select: none;
+        pointer-events: none;
+    }
+
+    .mechanicsSimulationSettingsMenu {
+        width: 100%;
+        height: 100%;
+        font-size: 12px;
+        background-color: rgb(224, 224, 224);
+        border-radius: 2px;
+        border-color: black;
+        border-style: solid;
+        padding: 10px;
+        position: fixed;
+        z-index: 1000;
+    }
+
+    .mechanicsSimulationSettingsMenuRow {
+        display: flex;
+    }
+
+    .mechanicsSimulationSettingsMenuRowDescription {
+        width: 50%;
+    }
+
+    .dropdownMenu {
+        z-index: 50;
+    }
+}
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
new file mode 100644
index 000000000..2c06282ed
--- /dev/null
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
@@ -0,0 +1,1987 @@
+// import ArrowLeftIcon from '@mui/icons-material/ArrowLeft';
+// import ArrowRightIcon from '@mui/icons-material/ArrowRight';
+// import PauseIcon from '@mui/icons-material/Pause';
+// import PlayArrowIcon from '@mui/icons-material/PlayArrow';
+// import QuestionMarkIcon from '@mui/icons-material/QuestionMark';
+// import ReplayIcon from '@mui/icons-material/Replay';
+// import { Box, Button, Checkbox, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, FormControl, FormControlLabel, FormGroup, IconButton, LinearProgress, Stack } from '@mui/material';
+// import Typography from '@mui/material/Typography';
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import { NumListCast } from '../../../../fields/Doc';
+import { List } from '../../../../fields/List';
+import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
+import { ViewBoxAnnotatableComponent } from '../../DocComponent';
+import { FieldView, FieldViewProps } from './../FieldView';
+import './PhysicsSimulationBox.scss';
+import InputField from './PhysicsSimulationInputField';
+import * as questions from './PhysicsSimulationQuestions.json';
+import * as tutorials from './PhysicsSimulationTutorial.json';
+import Wall from './PhysicsSimulationWall';
+import Weight from './PhysicsSimulationWeight';
+import React = require('react');
+
+interface IWallProps {
+    length: number;
+    xPos: number;
+    yPos: number;
+    angleInDegrees: number;
+}
+interface IForce {
+    description: string;
+    magnitude: number;
+    directionInDegrees: number;
+}
+interface VectorTemplate {
+    top: number;
+    left: number;
+    width: number;
+    height: number;
+    x1: number;
+    y1: number;
+    x2: number;
+    y2: number;
+    weightX: number;
+    weightY: number;
+}
+interface QuestionTemplate {
+    questionSetup: string[];
+    variablesForQuestionSetup: string[];
+    question: string;
+    answerParts: string[];
+    answerSolutionDescriptions: string[];
+    goal: string;
+    hints: { description: string; content: string }[];
+}
+
+interface TutorialTemplate {
+    question: string;
+    steps: {
+        description: string;
+        content: string;
+        forces: {
+            description: string;
+            magnitude: number;
+            directionInDegrees: number;
+            component: boolean;
+        }[];
+        showMagnitude: boolean;
+    }[];
+}
+
+@observer
+export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(PhysicsSimulationBox, fieldKey);
+    }
+
+    _widthDisposer: IReactionDisposer | undefined;
+    @observable _simReset = 0;
+
+    // semi-Constants
+    xMin = 0;
+    yMin = 0;
+    xMax = this.props.PanelWidth() * 0.6;
+    yMax = this.props.PanelHeight();
+    color = `rgba(0,0,0,0.5)`;
+    radius = 50;
+    wallPositions: IWallProps[] = [];
+
+    @computed get circularMotionRadius() {
+        return (NumCast(this.dataDoc.circularMotionRadius, 150) * this.props.PanelWidth()) / 1000;
+    }
+    @computed get gravity() {
+        return NumCast(this.dataDoc.simulation_gravity, -9.81);
+    }
+    @computed get simulationType() {
+        return StrCast(this.dataDoc.simulation_type, 'Inclined Plane');
+    }
+    @computed get simulationMode() {
+        return StrCast(this.dataDoc.simulation_mode, 'Freeform');
+    }
+    // Used for spring simulation
+    @computed get springConstant() {
+        return NumCast(this.dataDoc.spring_constant, 0.5);
+    }
+    @computed get springLengthRest() {
+        return NumCast(this.dataDoc.spring_lengthRest, 200);
+    }
+    @computed get springLengthStart() {
+        return NumCast(this.dataDoc.spring_lengthStart, 200);
+    }
+
+    @computed get pendulumAngle() {
+        return NumCast(this.dataDoc.pendulum_angle);
+    }
+    @computed get pendulumAngleStart() {
+        return NumCast(this.dataDoc.pendulum_angleStart);
+    }
+    @computed get pendulumLength() {
+        return NumCast(this.dataDoc.pendulum_length);
+    }
+    @computed get pendulumLengthStart() {
+        return NumCast(this.dataDoc.pendulum_lengthStart);
+    }
+
+    // Used for wedge simulation
+    @computed get wedgeAngle() {
+        return NumCast(this.dataDoc.wedge_angle, 26);
+    }
+    @computed get wedgeHeight() {
+        return NumCast(this.dataDoc.wedge_height, Math.tan((26 * Math.PI) / 180) * this.xMax * 0.5);
+    }
+    @computed get wedgeWidth() {
+        return NumCast(this.dataDoc.wedge_width, this.xMax * 0.5);
+    }
+    @computed get mass1() {
+        return NumCast(this.dataDoc.mass1, 1);
+    }
+    @computed get mass2() {
+        return NumCast(this.dataDoc.mass2, 1);
+    }
+
+    @computed get mass1Radius() {
+        return NumCast(this.dataDoc.mass1_radius, 30);
+    }
+    @computed get mass1PosXStart() {
+        return NumCast(this.dataDoc.mass1_positionXstart);
+    }
+    @computed get mass1PosYStart() {
+        return NumCast(this.dataDoc.mass1_positionYstart);
+    }
+    @computed get mass1VelXStart() {
+        return NumCast(this.dataDoc.mass1_velocityXstart);
+    }
+    @computed get mass1VelYStart() {
+        return NumCast(this.dataDoc.mass1_velocityYstart);
+    }
+
+    @computed get mass2PosXStart() {
+        return NumCast(this.dataDoc.mass2_positionXstart);
+    }
+    @computed get mass2PosYStart() {
+        return NumCast(this.dataDoc.mass2_positionYstart);
+    }
+    @computed get mass2VelXStart() {
+        return NumCast(this.dataDoc.mass2_velocityXstart);
+    }
+    @computed get mass2VelYStart() {
+        return NumCast(this.dataDoc.mass2_velocityYstart);
+    }
+
+    @computed get selectedQuestion() {
+        return this.dataDoc.selectedQuestion ? (JSON.parse(StrCast(this.dataDoc.selectedQuestion)) as QuestionTemplate) : questions.inclinePlane[0];
+    }
+    @computed get tutorial() {
+        return this.dataDoc.tutorial ? (JSON.parse(StrCast(this.dataDoc.tutorial)) as TutorialTemplate) : tutorials.inclinePlane;
+    }
+    @computed get selectedSolutions() {
+        return NumListCast(this.dataDoc.selectedSolutions);
+    }
+    @computed get questionPartOne() {
+        return StrCast(this.dataDoc.questionPartOne);
+    }
+    @computed get questionPartTwo() {
+        return StrCast(this.dataDoc.questionPartTwo);
+    }
+
+    componentWillUnmount() {
+        this._widthDisposer?.();
+    }
+
+    componentDidMount() {
+        // Setup and update simulation
+        this._widthDisposer = reaction(() => [this.props.PanelWidth(), this.props.PanelHeight()], this.setupSimulation, { fireImmediately: true });
+
+        // Create walls
+        this.wallPositions = [
+            { length: 100, xPos: 0, yPos: 0, angleInDegrees: 0 },
+            { length: 100, xPos: 0, yPos: 100, angleInDegrees: 0 },
+            { length: 100, xPos: 0, yPos: 0, angleInDegrees: 90 },
+            { length: 100, xPos: (this.xMax / this.props.PanelWidth()) * 100, yPos: 0, angleInDegrees: 90 },
+        ];
+    }
+
+    componentDidUpdate() {
+        if (this.xMax !== this.props.PanelWidth() * 0.6 || this.yMax != this.props.PanelHeight()) {
+            this.xMax = this.props.PanelWidth() * 0.6;
+            this.yMax = this.props.PanelHeight();
+            this.setupSimulation();
+        }
+    }
+
+    gravityForce = (mass: number): IForce => ({
+        description: 'Gravity',
+        magnitude: mass * Math.abs(this.gravity),
+        directionInDegrees: 270,
+    });
+
+    @action
+    setupSimulation = () => {
+        const simulationType = this.simulationType;
+        const mode = this.simulationMode;
+        this.dataDoc.simulation_paused = true;
+        if (simulationType != 'Circular Motion') {
+            this.dataDoc.mass1_velocityXstart = 0;
+            this.dataDoc.mass1_velocityYstart = 0;
+            this.dataDoc.mass1_velocityX = 0;
+            this.dataDoc.mass1_velocityY = 0;
+        }
+        if (mode == 'Freeform') {
+            this.dataDoc.simulation_showForceMagnitudes = true;
+            // prettier-ignore
+            switch (simulationType) {
+                case 'One Weight': 
+                    this.dataDoc.simulation_showComponentForces = false;
+                    this.dataDoc.mass1_positionYstart = this.yMin + this.mass1Radius;
+                    this.dataDoc.mass1_positionXstart = (this.xMax + this.xMin) / 2 - this.mass1Radius;
+                    this.dataDoc.mass1_positionY = this.getDisplayYPos(this.yMin + this.mass1Radius);
+                    this.dataDoc.mass1_positionX = (this.xMax + this.xMin) / 2 - this.mass1Radius;
+                    this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1)]);
+                    this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1)]);
+                    break;
+                case 'Inclined Plane':  this.setupInclinedPlane(); break;
+                case 'Pendulum':        this.setupPendulum(); break;
+                case 'Spring':          this.setupSpring(); break;
+                case 'Circular Motion': this.setupCircular(20); break;
+                case 'Pulley':          this.setupPulley(); break;
+                case 'Suspension':      this.setupSuspension();break;
+            }
+            this._simReset++;
+        } else if (mode == 'Review') {
+            this.dataDoc.simulation_showComponentForces = false;
+            this.dataDoc.simulation_showForceMagnitudes = true;
+            this.dataDoc.simulation_showAcceleration = false;
+            this.dataDoc.simulation_showVelocity = false;
+            this.dataDoc.simulation_showForces = true;
+            this.generateNewQuestion();
+            // prettier-ignore
+            switch (simulationType) {
+                case 'One Weight' : break;// TODO - one weight review problems
+                case 'Spring':  this.setupSpring(); break; // TODO - spring review problems
+                case 'Inclined Plane': this.dataDoc.mass1_forcesUpdated =  this.dataDoc.mass1_forcesStart = ''; break;
+                case 'Pendulum':  this.setupPendulum(); break;  // TODO - pendulum review problems
+                case 'Circular Motion':  this.setupCircular(0); break;  // TODO - circular motion review problems
+                case 'Pulley':  this.setupPulley(); break; // TODO - pulley tutorial review problems
+                case 'Suspension': this.setupSuspension(); break;  // TODO - suspension tutorial review problems
+            }
+        } else if (mode == 'Tutorial') {
+            this.dataDoc.simulation_showComponentForces = false;
+            this.dataDoc.tutorial_stepNumber = 0;
+            this.dataDoc.simulation_showAcceleration = false;
+            if (this.simulationType != 'Circular Motion') {
+                this.dataDoc.mass1_velocityX = 0;
+                this.dataDoc.mass1_velocityY = 0;
+                this.dataDoc.simulation_showVelocity = false;
+            } else {
+                this.dataDoc.mass1_velocityX = 20;
+                this.dataDoc.mass1_velocityY = 0;
+                this.dataDoc.simulation_showVelocity = true;
+            }
+
+            switch (this.simulationType) {
+                case 'One Weight':
+                    this.dataDoc.simulation_showForces = true;
+                    this.dataDoc.mass1_positionYstart = this.yMax - 100;
+                    this.dataDoc.mass1_positionXstart = (this.xMax + this.xMin) / 2 - this.mass1Radius;
+                    this.dataDoc.tutorial = JSON.stringify(tutorials.freeWeight);
+                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.freeWeight.steps[0].forces);
+                    this.dataDoc.simulation_showForceMagnitudes = tutorials.freeWeight.steps[0].showMagnitude;
+                    break;
+                case 'Spring':
+                    this.dataDoc.simulation_showForces = true;
+                    this.setupSpring();
+                    this.dataDoc.mass1_positionYstart = this.yMin + 200 + 19.62;
+                    this.dataDoc.mass1_positionXstart = (this.xMax + this.xMin) / 2 - this.mass1Radius;
+                    this.dataDoc.tutorial = JSON.stringify(tutorials.spring);
+                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.spring.steps[0].forces);
+                    this.dataDoc.simulation_showForceMagnitudes = tutorials.spring.steps[0].showMagnitude;
+                    break;
+                case 'Pendulum':
+                    this.setupPendulum();
+                    this.dataDoc.tutorial = JSON.stringify(tutorials.pendulum);
+                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.pendulum.steps[0].forces);
+                    this.dataDoc.simulation_showForceMagnitudes = tutorials.pendulum.steps[0].showMagnitude;
+                    break;
+                case 'Inclined Plane':
+                    this.dataDoc.wedge_angle = 26;
+                    this.setupInclinedPlane();
+                    this.dataDoc.simulation_showForces = true;
+                    this.dataDoc.tutorial = JSON.stringify(tutorials.inclinePlane);
+                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.inclinePlane.steps[0].forces);
+                    this.dataDoc.simulation_showForceMagnitudes = tutorials.inclinePlane.steps[0].showMagnitude;
+                    break;
+                case 'Circular Motion':
+                    this.dataDoc.simulation_showForces = true;
+                    this.setupCircular(40);
+                    this.dataDoc.tutorial = JSON.stringify(tutorials.circular);
+                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.circular.steps[0].forces);
+                    this.dataDoc.simulation_showForceMagnitudes = tutorials.circular.steps[0].showMagnitude;
+                    break;
+                case 'Pulley':
+                    this.dataDoc.simulation_showForces = true;
+                    this.setupPulley();
+                    this.dataDoc.tutorial = JSON.stringify(tutorials.pulley);
+                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.pulley.steps[0].forces);
+                    this.dataDoc.simulation_showForceMagnitudes = tutorials.pulley.steps[0].showMagnitude;
+                    break;
+                case 'Suspension':
+                    this.dataDoc.simulation_showForces = true;
+                    this.setupSuspension();
+                    this.dataDoc.tutorial = JSON.stringify(tutorials.suspension);
+                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.suspension.steps[0].forces);
+                    this.dataDoc.simulation_showForceMagnitudes = tutorials.suspension.steps[0].showMagnitude;
+                    break;
+            }
+            this._simReset++;
+        }
+    };
+
+    // Helper function to go between display and real values
+    getDisplayYPos = (yPos: number) => this.yMax - yPos - 2 * this.mass1Radius + 5;
+    getYPosFromDisplay = (yDisplay: number) => this.yMax - yDisplay - 2 * this.mass1Radius + 5;
+
+    // Update forces when coefficient of static friction changes in freeform mode
+    updateForcesWithFriction = (coefficient: number, width = this.wedgeWidth, height = this.wedgeHeight) => {
+        const normalForce: IForce = {
+            description: 'Normal Force',
+            magnitude: Math.abs(this.gravity) * Math.cos(Math.atan(height / width)) * this.mass1,
+            directionInDegrees: 180 - 90 - (Math.atan(height / width) * 180) / Math.PI,
+        };
+        let frictionForce: IForce = {
+            description: 'Static Friction Force',
+            magnitude: coefficient * Math.abs(this.gravity) * Math.cos(Math.atan(height / width)) * this.mass1,
+            directionInDegrees: 180 - (Math.atan(height / width) * 180) / Math.PI,
+        };
+        // reduce magnitude or friction force if necessary such that block cannot slide up plane
+        let yForce = -Math.abs(this.gravity) * this.mass1;
+        yForce += normalForce.magnitude * Math.sin((normalForce.directionInDegrees * Math.PI) / 180);
+        yForce += frictionForce.magnitude * Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
+        if (yForce > 0) {
+            frictionForce.magnitude = (-normalForce.magnitude * Math.sin((normalForce.directionInDegrees * Math.PI) / 180) + Math.abs(this.gravity) * this.mass1) / Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
+        }
+
+        const normalForceComponent: IForce = {
+            description: 'Normal Force',
+            magnitude: this.mass1 * Math.abs(this.gravity) * Math.cos(Math.atan(height / width)),
+            directionInDegrees: 180 - 90 - (Math.atan(height / width) * 180) / Math.PI,
+        };
+        const gravityParallel: IForce = {
+            description: 'Gravity Parallel Component',
+            magnitude: this.mass1 * Math.abs(this.gravity) * Math.sin(Math.PI / 2 - Math.atan(height / width)),
+            directionInDegrees: 180 - 90 - (Math.atan(height / width) * 180) / Math.PI + 180,
+        };
+        const gravityPerpendicular: IForce = {
+            description: 'Gravity Perpendicular Component',
+            magnitude: this.mass1 * Math.abs(this.gravity) * Math.cos(Math.PI / 2 - Math.atan(height / width)),
+            directionInDegrees: 360 - (Math.atan(height / width) * 180) / Math.PI,
+        };
+        const gravityForce = this.gravityForce(this.mass1);
+        if (coefficient != 0) {
+            this.dataDoc.mass1_forcesStart = JSON.stringify([gravityForce, normalForce, frictionForce]);
+            this.dataDoc.mass1_forcesUpdated = JSON.stringify([gravityForce, normalForce, frictionForce]);
+            this.dataDoc.mass1_componentForces = JSON.stringify([frictionForce, normalForceComponent, gravityParallel, gravityPerpendicular]);
+        } else {
+            this.dataDoc.mass1_forcesStart = JSON.stringify([gravityForce, normalForce]);
+            this.dataDoc.mass1_forcesUpdated = JSON.stringify([gravityForce, normalForce]);
+            this.dataDoc.mass1_componentForces = JSON.stringify([normalForceComponent, gravityParallel, gravityPerpendicular]);
+        }
+    };
+
+    // Change wedge height and width and weight position to match new wedge angle
+    changeWedgeBasedOnNewAngle = (angle: number) => {
+        const radAng = (angle * Math.PI) / 180;
+        this.dataDoc.wedge_width = this.xMax * 0.5;
+        this.dataDoc.wedge_height = Math.tan(radAng) * this.dataDoc.wedge_width;
+
+        // update weight position based on updated wedge width/height
+        let yPos = this.yMax - this.dataDoc.wedge_height - this.mass1Radius * Math.cos(radAng) - this.mass1Radius;
+        let xPos = this.xMax * 0.25 + this.mass1Radius * Math.sin(radAng) - this.mass1Radius;
+
+        this.dataDoc.mass1_positionXstart = xPos;
+        this.dataDoc.mass1_positionYstart = yPos;
+        if (this.simulationMode == 'Freeform') {
+            this.updateForcesWithFriction(NumCast(this.dataDoc.coefficientOfStaticFriction), this.dataDoc.wedge_width, Math.tan(radAng) * this.dataDoc.wedge_width);
+        }
+    };
+
+    // In review mode, update forces when coefficient of static friction changed
+    updateReviewForcesBasedOnCoefficient = (coefficient: number) => {
+        let theta = this.wedgeAngle;
+        let index = this.selectedQuestion.variablesForQuestionSetup.indexOf('theta - max 45');
+        if (index >= 0) {
+            theta = NumListCast(this.dataDoc.questionVariables)[index];
+        }
+        if (isNaN(theta)) {
+            return;
+        }
+        this.dataDoc.review_GravityMagnitude = Math.abs(this.gravity);
+        this.dataDoc.review_GravityAngle = 270;
+        this.dataDoc.review_NormalMagnitude = Math.abs(this.gravity) * Math.cos((theta * Math.PI) / 180);
+        this.dataDoc.review_NormalAngle = 90 - theta;
+        let yForce = -Math.abs(this.gravity);
+        yForce += Math.abs(this.gravity) * Math.cos((theta * Math.PI) / 180) * Math.sin(((90 - theta) * Math.PI) / 180);
+        yForce += coefficient * Math.abs(this.gravity) * Math.cos((theta * Math.PI) / 180) * Math.sin(((180 - theta) * Math.PI) / 180);
+        let friction = coefficient * Math.abs(this.gravity) * Math.cos((theta * Math.PI) / 180);
+        if (yForce > 0) {
+            friction = (-(Math.abs(this.gravity) * Math.cos((theta * Math.PI) / 180)) * Math.sin(((90 - theta) * Math.PI) / 180) + Math.abs(this.gravity)) / Math.sin(((180 - theta) * Math.PI) / 180);
+        }
+        this.dataDoc.review_StaticMagnitude = friction;
+        this.dataDoc.review_StaticAngle = 180 - theta;
+    };
+
+    // In review mode, update forces when wedge angle changed
+    updateReviewForcesBasedOnAngle = (angle: number) => {
+        this.dataDoc.review_GravityMagnitude = Math.abs(this.gravity);
+        this.dataDoc.review_GravityAngle = 270;
+        this.dataDoc.review_NormalMagnitude = Math.abs(this.gravity) * Math.cos((angle * Math.PI) / 180);
+        this.dataDoc.review_NormalAngle = 90 - angle;
+        let yForce = -Math.abs(this.gravity);
+        yForce += Math.abs(this.gravity) * Math.cos((angle * Math.PI) / 180) * Math.sin(((90 - angle) * Math.PI) / 180);
+        yForce += NumCast(this.dataDoc.review_Coefficient) * Math.abs(this.gravity) * Math.cos((angle * Math.PI) / 180) * Math.sin(((180 - angle) * Math.PI) / 180);
+        let friction = NumCast(this.dataDoc.review_Coefficient) * Math.abs(this.gravity) * Math.cos((angle * Math.PI) / 180);
+        if (yForce > 0) {
+            friction = (-(Math.abs(this.gravity) * Math.cos((angle * Math.PI) / 180)) * Math.sin(((90 - angle) * Math.PI) / 180) + Math.abs(this.gravity)) / Math.sin(((180 - angle) * Math.PI) / 180);
+        }
+        this.dataDoc.review_StaticMagnitude = friction;
+        this.dataDoc.review_StaticAngle = 180 - angle;
+    };
+
+    // Solve for the correct answers to the generated problem
+    getAnswersToQuestion = (question: QuestionTemplate, questionVars: number[]) => {
+        const solutions: number[] = [];
+
+        let theta = this.wedgeAngle;
+        let index = question.variablesForQuestionSetup.indexOf('theta - max 45');
+        if (index >= 0) {
+            theta = questionVars[index];
+        }
+        let muS: number = NumCast(this.dataDoc.coefficientOfStaticFriction);
+        index = question.variablesForQuestionSetup.indexOf('coefficient of static friction');
+        if (index >= 0) {
+            muS = questionVars[index];
+        }
+
+        for (let i = 0; i < question.answerSolutionDescriptions.length; i++) {
+            const description = question.answerSolutionDescriptions[i];
+            if (!isNaN(NumCast(description))) {
+                solutions.push(NumCast(description));
+            } else if (description == 'solve normal force angle from wedge angle') {
+                solutions.push(90 - theta);
+            } else if (description == 'solve normal force magnitude from wedge angle') {
+                solutions.push(Math.abs(this.gravity) * Math.cos((theta / 180) * Math.PI));
+            } else if (description == 'solve static force magnitude from wedge angle given equilibrium') {
+                let normalForceMagnitude = Math.abs(this.gravity) * Math.cos((theta / 180) * Math.PI);
+                let normalForceAngle = 90 - theta;
+                let frictionForceAngle = 180 - theta;
+                let frictionForceMagnitude = (-normalForceMagnitude * Math.sin((normalForceAngle * Math.PI) / 180) + Math.abs(this.gravity)) / Math.sin((frictionForceAngle * Math.PI) / 180);
+                solutions.push(frictionForceMagnitude);
+            } else if (description == 'solve static force angle from wedge angle given equilibrium') {
+                solutions.push(180 - theta);
+            } else if (description == 'solve minimum static coefficient from wedge angle given equilibrium') {
+                let normalForceMagnitude = Math.abs(this.gravity) * Math.cos((theta / 180) * Math.PI);
+                let normalForceAngle = 90 - theta;
+                let frictionForceAngle = 180 - theta;
+                let frictionForceMagnitude = (-normalForceMagnitude * Math.sin((normalForceAngle * Math.PI) / 180) + Math.abs(this.gravity)) / Math.sin((frictionForceAngle * Math.PI) / 180);
+                let frictionCoefficient = frictionForceMagnitude / normalForceMagnitude;
+                solutions.push(frictionCoefficient);
+            } else if (description == 'solve maximum wedge angle from coefficient of static friction given equilibrium') {
+                solutions.push((Math.atan(muS) * 180) / Math.PI);
+            }
+        }
+        this.dataDoc.selectedSolutions = new List<number>(solutions);
+        return solutions;
+    };
+
+    // In review mode, check if input answers match correct answers and optionally generate alert
+    checkAnswers = (showAlert: boolean = true) => {
+        let error: boolean = false;
+        let epsilon: number = 0.01;
+        if (this.selectedQuestion) {
+            for (let i = 0; i < this.selectedQuestion.answerParts.length; i++) {
+                if (this.selectedQuestion.answerParts[i] == 'force of gravity') {
+                    if (Math.abs(NumCast(this.dataDoc.review_GravityMagnitude) - this.selectedSolutions[i]) > epsilon) {
+                        error = true;
+                    }
+                } else if (this.selectedQuestion.answerParts[i] == 'angle of gravity') {
+                    if (Math.abs(NumCast(this.dataDoc.review_GravityAngle) - this.selectedSolutions[i]) > epsilon) {
+                        error = true;
+                    }
+                } else if (this.selectedQuestion.answerParts[i] == 'normal force') {
+                    if (Math.abs(NumCast(this.dataDoc.review_NormalMagnitude) - this.selectedSolutions[i]) > epsilon) {
+                        error = true;
+                    }
+                } else if (this.selectedQuestion.answerParts[i] == 'angle of normal force') {
+                    if (Math.abs(NumCast(this.dataDoc.review_NormalAngle) - this.selectedSolutions[i]) > epsilon) {
+                        error = true;
+                    }
+                } else if (this.selectedQuestion.answerParts[i] == 'force of static friction') {
+                    if (Math.abs(NumCast(this.dataDoc.review_StaticMagnitude) - this.selectedSolutions[i]) > epsilon) {
+                        error = true;
+                    }
+                } else if (this.selectedQuestion.answerParts[i] == 'angle of static friction') {
+                    if (Math.abs(NumCast(this.dataDoc.review_StaticAngle) - this.selectedSolutions[i]) > epsilon) {
+                        error = true;
+                    }
+                } else if (this.selectedQuestion.answerParts[i] == 'coefficient of static friction') {
+                    if (Math.abs(NumCast(this.dataDoc.coefficientOfStaticFriction) - this.selectedSolutions[i]) > epsilon) {
+                        error = true;
+                    }
+                } else if (this.selectedQuestion.answerParts[i] == 'wedge angle') {
+                    if (Math.abs(this.wedgeAngle - this.selectedSolutions[i]) > epsilon) {
+                        error = true;
+                    }
+                }
+            }
+        }
+        if (showAlert) {
+            this.dataDoc.simulation_paused = false;
+            setTimeout(() => (this.dataDoc.simulation_paused = true), 3000);
+        }
+        if (this.selectedQuestion.goal == 'noMovement') {
+            this.dataDoc.noMovement = !error;
+        }
+    };
+
+    // Reset all review values to default
+    resetReviewValuesToDefault = () => {
+        this.dataDoc.review_GravityMagnitude = 0;
+        this.dataDoc.review_GravityAngle = 0;
+        this.dataDoc.review_NormalMagnitude = 0;
+        this.dataDoc.review_NormalAngle = 0;
+        this.dataDoc.review_StaticMagnitude = 0;
+        this.dataDoc.review_StaticAngle = 0;
+        this.dataDoc.coefficientOfKineticFriction = 0;
+        this.dataDoc.simulation_paused = true;
+    };
+
+    // In review mode, reset problem variables and generate a new question
+    generateNewQuestion = () => {
+        this.resetReviewValuesToDefault();
+
+        const vars: number[] = [];
+        let question: QuestionTemplate = questions.inclinePlane[0];
+
+        if (this.simulationType === 'Inclined Plane') {
+            this.dataDoc.questionNumber = (NumCast(this.dataDoc.questionNumber) + 1) % questions.inclinePlane.length;
+            question = questions.inclinePlane[NumCast(this.dataDoc.questionNumber)];
+
+            let coefficient = 0;
+            let wedge_angle = 0;
+
+            for (let i = 0; i < question.variablesForQuestionSetup.length; i++) {
+                if (question.variablesForQuestionSetup[i] == 'theta - max 45') {
+                    let randValue = Math.floor(Math.random() * 44 + 1);
+                    vars.push(randValue);
+                    wedge_angle = randValue;
+                } else if (question.variablesForQuestionSetup[i] == 'coefficient of static friction') {
+                    let randValue = Math.round(Math.random() * 1000) / 1000;
+                    vars.push(randValue);
+                    coefficient = randValue;
+                }
+            }
+            this.dataDoc.wedge_angle = wedge_angle;
+            this.changeWedgeBasedOnNewAngle(wedge_angle);
+            this.dataDoc.coefficientOfStaticFriction = coefficient;
+            this.dataDoc.review_Coefficient = coefficient;
+        }
+        let q = '';
+        for (let i = 0; i < question.questionSetup.length; i++) {
+            q += question.questionSetup[i];
+            if (i != question.questionSetup.length - 1) {
+                q += vars[i];
+                if (question.variablesForQuestionSetup[i].includes('theta')) {
+                    q += ' degree (≈' + Math.round((1000 * (vars[i] * Math.PI)) / 180) / 1000 + ' rad)';
+                }
+            }
+        }
+        this.dataDoc.questionVariables = new List<number>(vars);
+        this.dataDoc.selectedQuestion = JSON.stringify(question);
+        this.dataDoc.questionPartOne = q;
+        this.dataDoc.questionPartTwo = question.question;
+        this.dataDoc.answers = new List<number>(this.getAnswersToQuestion(question, vars));
+        //this.dataDoc.simulation_reset = (!this.dataDoc.simulation_reset);
+    };
+
+    // Default setup for uniform circular motion simulation
+    @action
+    setupCircular = (value: number) => {
+        this.dataDoc.simulation_showComponentForces = false;
+        this.dataDoc.mass1_velocityYstart = 0;
+        this.dataDoc.mass1_velocityXstart = value;
+        let xPos = (this.xMax + this.xMin) / 2 - this.mass1Radius;
+        let yPos = (this.yMax + this.yMin) / 2 + this.circularMotionRadius - this.mass1Radius;
+        this.dataDoc.mass1_positionYstart = yPos;
+        this.dataDoc.mass1_positionXstart = xPos;
+        const tensionForce: IForce = {
+            description: 'Centripetal Force',
+            magnitude: (this.dataDoc.mass1_velocityXstart ** 2 * this.mass1) / this.circularMotionRadius,
+            directionInDegrees: 90,
+        };
+        this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce]);
+        this.dataDoc.mass1_forcesStart = JSON.stringify([tensionForce]);
+        this._simReset++;
+    };
+
+    setupInclinedPlane = () => {
+        this.changeWedgeBasedOnNewAngle(this.wedgeAngle);
+        this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1)]);
+        this.updateForcesWithFriction(NumCast(this.dataDoc.coefficientOfStaticFriction));
+    };
+
+    // Default setup for pendulum simulation
+    setupPendulum = () => {
+        const length = (300 * this.props.PanelWidth()) / 1000;
+        const angle = 30;
+        const x = length * Math.cos(((90 - angle) * Math.PI) / 180);
+        const y = length * Math.sin(((90 - angle) * Math.PI) / 180);
+        const xPos = this.xMax / 2 - x - this.mass1Radius;
+        const yPos = y - this.mass1Radius - 5;
+        this.dataDoc.mass1_positionXstart = xPos;
+        this.dataDoc.mass1_positionYstart = yPos;
+        const forceOfTension: IForce = {
+            description: 'Tension',
+            magnitude: this.mass1 * Math.abs(this.gravity) * Math.sin((60 * Math.PI) / 180),
+            directionInDegrees: 90 - angle,
+        };
+        const gravityParallel: IForce = {
+            description: 'Gravity Parallel Component',
+            magnitude: this.mass1 * Math.abs(this.gravity) * Math.sin(((90 - angle) * Math.PI) / 180),
+            directionInDegrees: -angle - 90,
+        };
+        const gravityPerpendicular: IForce = {
+            description: 'Gravity Perpendicular Component',
+            magnitude: this.mass1 * Math.abs(this.gravity) * Math.cos(((90 - angle) * Math.PI) / 180),
+            directionInDegrees: -angle,
+        };
+
+        this.dataDoc.mass1_componentForces = JSON.stringify([forceOfTension, gravityParallel, gravityPerpendicular]);
+        this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1)]);
+        this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1), forceOfTension]);
+        this.dataDoc.pendulum_angle = this.dataDoc.pendulum_angleStart = 30;
+        this.dataDoc.pendulum_length = this.dataDoc.pendulum_lengthStart = 300;
+    };
+
+    // Default setup for spring simulation
+    @action
+    setupSpring = () => {
+        this.dataDoc.simulation_showComponentForces = false;
+        this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1)]);
+        this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1)]);
+        this.dataDoc.mass1_positionXstart = this.xMax / 2 - this.mass1Radius;
+        this.dataDoc.mass1_positionYstart = 200;
+        this.dataDoc.spring_constant = 0.5;
+        this.dataDoc.spring_lengthRest = 200;
+        this.dataDoc.spring_lengthStart = 200;
+        this._simReset++;
+    };
+
+    // Default setup for suspension simulation
+    @action
+    setupSuspension = () => {
+        let xPos = (this.xMax + this.xMin) / 2 - this.mass1Radius;
+        let yPos = this.yMin + 200;
+        this.dataDoc.mass1_positionYstart = yPos;
+        this.dataDoc.mass1_positionXstart = xPos;
+        this.dataDoc.mass1_positionY = this.getDisplayYPos(yPos);
+        this.dataDoc.mass1_positionX = xPos;
+        let tensionMag = (this.mass1 * Math.abs(this.gravity)) / (2 * Math.sin(Math.PI / 4));
+        const tensionForce1: IForce = {
+            description: 'Tension',
+            magnitude: tensionMag,
+            directionInDegrees: 45,
+        };
+        const tensionForce2: IForce = {
+            description: 'Tension',
+            magnitude: tensionMag,
+            directionInDegrees: 135,
+        };
+        const gravity = this.gravityForce(this.mass1);
+        this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce1, tensionForce2, gravity]);
+        this.dataDoc.mass1_forcesStart = JSON.stringify([tensionForce1, tensionForce2, gravity]);
+        this._simReset++;
+    };
+
+    // Default setup for pulley simulation
+    @action
+    setupPulley = () => {
+        this.dataDoc.simulation_showComponentForces = false;
+        this.dataDoc.mass1_positionYstart = (this.yMax + this.yMin) / 2;
+        this.dataDoc.mass1_positionXstart = (this.xMin + this.xMax) / 2 - 2 * this.mass1Radius - 5;
+        this.dataDoc.mass1_positionY = this.getDisplayYPos((this.yMax + this.yMin) / 2);
+        this.dataDoc.mass1_positionX = (this.xMin + this.xMax) / 2 - 2 * this.mass1Radius - 5;
+        const a = (-1 * ((this.mass1 - this.mass2) * Math.abs(this.gravity))) / (this.mass1 + this.mass2);
+        const gravityForce1 = this.gravityForce(this.mass1);
+        const tensionForce1: IForce = {
+            description: 'Tension',
+            magnitude: this.mass1 * a + this.mass1 * Math.abs(this.gravity),
+            directionInDegrees: 90,
+        };
+        this.dataDoc.mass1_forcesUpdated = JSON.stringify([gravityForce1, tensionForce1]);
+        this.dataDoc.mass1_forcesStart = JSON.stringify([gravityForce1, tensionForce1]);
+
+        const gravityForce2 = this.gravityForce(this.mass2);
+        const tensionForce2: IForce = {
+            description: 'Tension',
+            magnitude: -this.mass2 * a + this.mass2 * Math.abs(this.gravity),
+            directionInDegrees: 90,
+        };
+        this.dataDoc.mass2_positionYstart = (this.yMax + this.yMin) / 2;
+        this.dataDoc.mass2_positionXstart = (this.xMin + this.xMax) / 2 + 5;
+        this.dataDoc.mass2_positionY = this.getDisplayYPos((this.yMax + this.yMin) / 2);
+        this.dataDoc.mass2_positionX = (this.xMin + this.xMax) / 2 + 5;
+        this.dataDoc.mass2_forcesUpdated = JSON.stringify([gravityForce2, tensionForce2]);
+        this.dataDoc.mass2_forcesStart = JSON.stringify([gravityForce2, tensionForce2]);
+        this._simReset++;
+    };
+
+    public static parseJSON(json: string) {
+        return !json ? [] : (JSON.parse(json) as IForce[]);
+    }
+
+    // Handle force change in review mode
+    updateReviewModeValues = () => {
+        const forceOfGravityReview: IForce = {
+            description: 'Gravity',
+            magnitude: NumCast(this.dataDoc.review_GravityMagnitude),
+            directionInDegrees: NumCast(this.dataDoc.review_GravityAngle),
+        };
+        const normalForceReview: IForce = {
+            description: 'Normal Force',
+            magnitude: NumCast(this.dataDoc.review_NormalMagnitude),
+            directionInDegrees: NumCast(this.dataDoc.review_NormalAngle),
+        };
+        const staticFrictionForceReview: IForce = {
+            description: 'Static Friction Force',
+            magnitude: NumCast(this.dataDoc.review_StaticMagnitude),
+            directionInDegrees: NumCast(this.dataDoc.review_StaticAngle),
+        };
+        this.dataDoc.mass1_forcesStart = JSON.stringify([forceOfGravityReview, normalForceReview, staticFrictionForceReview]);
+        this.dataDoc.mass1_forcesUpdated = JSON.stringify([forceOfGravityReview, normalForceReview, staticFrictionForceReview]);
+    };
+
+    pause = () => (this.dataDoc.simulation_paused = true);
+    componentForces1 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass1_componentForces));
+    setComponentForces1 = (forces: IForce[]) => (this.dataDoc.mass1_componentForces = JSON.stringify(forces));
+    componentForces2 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass2_componentForces));
+    setComponentForces2 = (forces: IForce[]) => (this.dataDoc.mass2_componentForces = JSON.stringify(forces));
+    startForces1 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass1_forcesStart));
+    startForces2 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass2_forcesStart));
+    forcesUpdated1 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass1_forcesUpdated));
+    setForcesUpdated1 = (forces: IForce[]) => (this.dataDoc.mass1_forcesUpdated = JSON.stringify(forces));
+    forcesUpdated2 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass2_forcesUpdated));
+    setForcesUpdated2 = (forces: IForce[]) => (this.dataDoc.mass2_forcesUpdated = JSON.stringify(forces));
+    setPosition1 = (xPos: number | undefined, yPos: number | undefined) => {
+        yPos !== undefined && (this.dataDoc.mass1_positionY = Math.round(yPos * 100) / 100);
+        xPos !== undefined && (this.dataDoc.mass1_positionX = Math.round(xPos * 100) / 100);
+    };
+    setPosition2 = (xPos: number | undefined, yPos: number | undefined) => {
+        yPos !== undefined && (this.dataDoc.mass2_positionY = Math.round(yPos * 100) / 100);
+        xPos !== undefined && (this.dataDoc.mass2_positionX = Math.round(xPos * 100) / 100);
+    };
+    setVelocity1 = (xVel: number | undefined, yVel: number | undefined) => {
+        yVel !== undefined && (this.dataDoc.mass1_velocityY = (-1 * Math.round(yVel * 100)) / 100);
+        xVel !== undefined && (this.dataDoc.mass1_velocityX = Math.round(xVel * 100) / 100);
+    };
+    setVelocity2 = (xVel: number | undefined, yVel: number | undefined) => {
+        yVel !== undefined && (this.dataDoc.mass2_velocityY = (-1 * Math.round(yVel * 100)) / 100);
+        xVel !== undefined && (this.dataDoc.mass2_velocityX = Math.round(xVel * 100) / 100);
+    };
+    setAcceleration1 = (xAccel: number, yAccel: number) => {
+        this.dataDoc.mass1_accelerationY = yAccel;
+        this.dataDoc.mass1_accelerationX = xAccel;
+    };
+    setAcceleration2 = (xAccel: number, yAccel: number) => {
+        this.dataDoc.mass2_accelerationY = yAccel;
+        this.dataDoc.mass2_accelerationX = xAccel;
+    };
+    setPendulumAngle = (angle: number | undefined, length: number | undefined) => {
+        angle !== undefined && (this.dataDoc.pendulum_angle = angle);
+        length !== undefined && (this.dataDoc.pendulum_length = length);
+    };
+    setSpringLength = (length: number) => {
+        this.dataDoc.spring_lengthStart = length;
+    };
+    resetRequest = () => this._simReset;
+    render() {
+        const commonWeightProps = {
+            pause: this.pause,
+            paused: BoolCast(this.dataDoc.simulation_paused),
+            panelWidth: this.props.PanelWidth,
+            panelHeight: this.props.PanelHeight,
+            resetRequest: this.resetRequest,
+            xMax: this.xMax,
+            xMin: this.xMin,
+            yMax: this.yMax,
+            yMin: this.yMin,
+            wallPositions: this.wallPositions,
+            gravity: Math.abs(this.gravity),
+            timestepSize: 0.05,
+            showComponentForces: BoolCast(this.dataDoc.simulation_showComponentForces),
+            coefficientOfKineticFriction: NumCast(this.dataDoc.coefficientOfKineticFriction),
+            elasticCollisions: BoolCast(this.dataDoc.elasticCollisions),
+            simulationMode: this.simulationMode,
+            noMovement: BoolCast(this.dataDoc.noMovement),
+            circularMotionRadius: this.circularMotionRadius,
+            wedgeHeight: this.wedgeHeight,
+            wedgeWidth: this.wedgeWidth,
+            springConstant: this.springConstant,
+            springStartLength: this.springLengthStart,
+            springRestLength: this.springLengthRest,
+            setSpringLength: this.setSpringLength,
+            setPendulumAngle: this.setPendulumAngle,
+            pendulumAngle: this.pendulumAngle,
+            pendulumLength: this.pendulumLength,
+            startPendulumAngle: this.pendulumAngleStart,
+            startPendulumLength: this.pendulumLengthStart,
+            radius: this.mass1Radius,
+            simulationSpeed: NumCast(this.dataDoc.simulation_speed, 2),
+            showAcceleration: BoolCast(this.dataDoc.simulation_showAcceleration),
+            showForceMagnitudes: BoolCast(this.dataDoc.simulation_showForceMagnitudes),
+            showForces: BoolCast(this.dataDoc.simulation_showForces),
+            showVelocity: BoolCast(this.dataDoc.simulation_showVelocity),
+            simulationType: this.simulationType,
+        };
+        return (
+            <div className="physicsSimApp">
+                <div className="mechanicsSimulationContainer">
+                    <div className="mechanicsSimulationContentContainer">
+                        <div className="mechanicsSimulationButtonsAndElements">
+                            <div className="mechanicsSimulationButtons">
+                                {!this.dataDoc.simulation_paused && (
+                                    <div
+                                        style={{
+                                            position: 'fixed',
+                                            left: 0.1 * this.props.PanelWidth() + 'px',
+                                            top: 0.95 * this.props.PanelHeight() + 'px',
+                                            width: 0.5 * this.props.PanelWidth() + 'px',
+                                        }}>
+                                        <LinearProgress />
+                                    </div>
+                                )}
+                            </div>
+                            <div
+                                className="mechanicsSimulationElements"
+                                style={{
+                                    //
+                                    width: '60%',
+                                    height: '100%',
+                                    position: 'absolute',
+                                    background: 'yellow',
+                                }}>
+                                <Weight
+                                    {...commonWeightProps}
+                                    color="red"
+                                    componentForces={this.componentForces1}
+                                    setComponentForces={this.setComponentForces1}
+                                    displayXVelocity={NumCast(this.dataDoc.mass1_velocityX)}
+                                    displayYVelocity={NumCast(this.dataDoc.mass1_velocityY)}
+                                    mass={this.mass1}
+                                    startForces={this.startForces1}
+                                    startPosX={this.mass1PosXStart}
+                                    startPosY={this.mass1PosYStart}
+                                    startVelX={this.mass1VelXStart}
+                                    startVelY={this.mass1VelYStart}
+                                    updateMassPosX={NumCast(this.dataDoc.mass1_xChange)}
+                                    updateMassPosY={NumCast(this.dataDoc.mass1_yChange)}
+                                    forcesUpdated={this.forcesUpdated1}
+                                    setForcesUpdated={this.setForcesUpdated1}
+                                    setPosition={this.setPosition1}
+                                    setVelocity={this.setVelocity1}
+                                    setAcceleration={this.setAcceleration1}
+                                />
+                                {this.simulationType == 'Pulley' && (
+                                    <Weight
+                                        {...commonWeightProps}
+                                        color="green"
+                                        componentForces={this.componentForces2}
+                                        setComponentForces={this.setComponentForces2}
+                                        displayXVelocity={NumCast(this.dataDoc.mass2_velocityX)}
+                                        displayYVelocity={NumCast(this.dataDoc.mass2_velocityY)}
+                                        mass={this.mass2}
+                                        startForces={this.startForces2}
+                                        startPosX={this.mass2PosXStart}
+                                        startPosY={this.mass2PosYStart}
+                                        startVelX={this.mass2VelXStart}
+                                        startVelY={this.mass2VelYStart}
+                                        updateMassPosX={NumCast(this.dataDoc.mass2_xChange)}
+                                        updateMassPosY={NumCast(this.dataDoc.mass2_yChange)}
+                                        forcesUpdated={this.forcesUpdated2}
+                                        setForcesUpdated={this.setForcesUpdated2}
+                                        setPosition={this.setPosition2}
+                                        setVelocity={this.setVelocity2}
+                                        setAcceleration={this.setAcceleration2}
+                                    />
+                                )}
+                            </div>
+                            <div style={{ position: 'absolute', transformOrigin: 'top left', top: 0, left: 0, width: '100%', height: '100%' }}>
+                                {(this.simulationType == 'One Weight' || this.simulationType == 'Inclined Plane') &&
+                                    this.wallPositions?.map((element, index) => <Wall key={index} length={element.length} xPos={element.xPos} yPos={element.yPos} angleInDegrees={element.angleInDegrees} />)}
+                            </div>
+                        </div>
+                    </div>
+                    <div
+                        className="mechanicsSimulationEquationContainer"
+                        onWheel={e => this.props.isContentActive() && e.stopPropagation()}
+                        style={{ overflow: 'auto', height: `${Math.max(1, 800 / this.props.PanelWidth()) * 100}%`, transform: `scale(${Math.min(1, this.props.PanelWidth() / 850)})` }}>
+                        <div className="mechanicsSimulationControls">
+                            <Stack direction="row" spacing={1}>
+                                {this.dataDoc.simulation_paused && this.simulationMode != 'Tutorial' && (
+                                    <IconButton onClick={() => (this.dataDoc.simulation_paused = false)}>
+                                        {/* <PlayArrowIcon /> */}
+                                    </IconButton>
+                                )}
+                                {!this.dataDoc.simulation_paused && this.simulationMode != 'Tutorial' && (
+                                    <IconButton onClick={() => (this.dataDoc.simulation_paused = true)}>
+                                        {/* <PauseIcon /> */}
+                                    </IconButton>
+                                )}
+                                {this.dataDoc.simulation_paused && this.simulationMode != 'Tutorial' && (
+                                    <IconButton onClick={action(() => this._simReset++)}>
+                                        <ReplayIcon />
+                                    </IconButton>
+                                )}
+                            </Stack>
+                            <div className="dropdownMenu">
+                                <select
+                                    value={StrCast(this.simulationType)}
+                                    onChange={event => {
+                                        this.dataDoc.simulation_type = event.target.value;
+                                        this.setupSimulation();
+                                    }}
+                                    style={{ height: '2em', width: '100%', fontSize: '16px' }}>
+                                    <option value="One Weight">Projectile</option>
+                                    <option value="Inclined Plane">Inclined Plane</option>
+                                    <option value="Pendulum">Pendulum</option>
+                                    <option value="Spring">Spring</option>
+                                    <option value="Circular Motion">Circular Motion</option>
+                                    <option value="Pulley">Pulley</option>
+                                    <option value="Suspension">Suspension</option>
+                                </select>
+                            </div>
+                            <div className="dropdownMenu">
+                                <select
+                                    value={this.simulationMode}
+                                    onChange={event => {
+                                        this.dataDoc.simulation_mode = event.target.value;
+                                        this.setupSimulation();
+                                    }}
+                                    style={{ height: '2em', width: '100%', fontSize: '16px' }}>
+                                    <option value="Tutorial">Tutorial Mode</option>
+                                    <option value="Freeform">Freeform Mode</option>
+                                    <option value="Review">Review Mode</option>
+                                </select>
+                            </div>
+                        </div>
+                        {this.simulationMode == 'Review' && this.simulationType != 'Inclined Plane' && (
+                            <div className="wordProblemBox">
+                                <p>
+                                    <>{this.simulationType} review problems in progress!</>
+                                </p>
+                                <hr />
+                            </div>
+                        )}
+                        {this.simulationMode == 'Review' && this.simulationType == 'Inclined Plane' && (
+                            <div>
+                                {!this.dataDoc.hintDialogueOpen && (
+                                    <IconButton
+                                        onClick={() => (this.dataDoc.hintDialogueOpen = true)}
+                                        sx={{
+                                            position: 'fixed',
+                                            left: this.xMax - 50 + 'px',
+                                            top: this.yMin + 14 + 'px',
+                                        }}>
+                                        <QuestionMarkIcon />
+                                    </IconButton>
+                                )}
+                                <Dialog maxWidth={'sm'} fullWidth={true} open={BoolCast(this.dataDoc.hintDialogueOpen)} onClose={() => (this.dataDoc.hintDialogueOpen = false)}>
+                                    <DialogTitle>Hints</DialogTitle>
+                                    <DialogContent>
+                                        {this.selectedQuestion.hints?.map((hint: any, index: number) => (
+                                            <div key={index}>
+                                                <DialogContentText>
+                                                    <details>
+                                                        <summary>
+                                                            <b>
+                                                                Hint {index + 1}: {hint.description}
+                                                            </b>
+                                                        </summary>
+                                                        {hint.content}
+                                                    </details>
+                                                </DialogContentText>
+                                            </div>
+                                        ))}
+                                    </DialogContent>
+                                    <DialogActions>
+                                        <Button onClick={() => (this.dataDoc.hintDialogueOpen = false)}>Close</Button>
+                                    </DialogActions>
+                                </Dialog>
+                                <div className="wordProblemBox">
+                                    <div className="question">
+                                        <p>{this.questionPartOne}</p>
+                                        <p>{this.questionPartTwo}</p>
+                                    </div>
+                                    <div className="answers">
+                                        {this.selectedQuestion.answerParts.includes('force of gravity') && (
+                                            <InputField
+                                                label={<p>Gravity magnitude</p>}
+                                                lowerBound={0}
+                                                dataDoc={this.dataDoc}
+                                                prop="review_GravityMagnitude"
+                                                step={0.1}
+                                                unit={'N'}
+                                                upperBound={50}
+                                                value={NumCast(this.dataDoc.review_GravityMagnitude)}
+                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
+                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('force of gravity')]}
+                                                labelWidth={'7em'}
+                                            />
+                                        )}
+                                        {this.selectedQuestion.answerParts.includes('angle of gravity') && (
+                                            <InputField
+                                                label={<p>Gravity angle</p>}
+                                                lowerBound={0}
+                                                dataDoc={this.dataDoc}
+                                                prop="review_GravityAngle"
+                                                step={1}
+                                                unit={'°'}
+                                                upperBound={360}
+                                                value={NumCast(this.dataDoc.review_GravityAngle)}
+                                                radianEquivalent={true}
+                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
+                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('angle of gravity')]}
+                                                labelWidth={'7em'}
+                                            />
+                                        )}
+                                        {this.selectedQuestion.answerParts.includes('normal force') && (
+                                            <InputField
+                                                label={<p>Normal force magnitude</p>}
+                                                lowerBound={0}
+                                                dataDoc={this.dataDoc}
+                                                prop="review_NormalMagnitude"
+                                                step={0.1}
+                                                unit={'N'}
+                                                upperBound={50}
+                                                value={NumCast(this.dataDoc.review_NormalMagnitude)}
+                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
+                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('normal force')]}
+                                                labelWidth={'7em'}
+                                            />
+                                        )}
+                                        {this.selectedQuestion.answerParts.includes('angle of normal force') && (
+                                            <InputField
+                                                label={<p>Normal force angle</p>}
+                                                lowerBound={0}
+                                                dataDoc={this.dataDoc}
+                                                prop="review_NormalAngle"
+                                                step={1}
+                                                unit={'°'}
+                                                upperBound={360}
+                                                value={NumCast(this.dataDoc.review_NormalAngle)}
+                                                radianEquivalent={true}
+                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
+                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('angle of normal force')]}
+                                                labelWidth={'7em'}
+                                            />
+                                        )}
+                                        {this.selectedQuestion.answerParts.includes('force of static friction') && (
+                                            <InputField
+                                                label={<p>Static friction magnitude</p>}
+                                                lowerBound={0}
+                                                dataDoc={this.dataDoc}
+                                                prop="review_StaticMagnitude"
+                                                step={0.1}
+                                                unit={'N'}
+                                                upperBound={50}
+                                                value={NumCast(this.dataDoc.review_StaticMagnitude)}
+                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
+                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('force of static friction')]}
+                                                labelWidth={'7em'}
+                                            />
+                                        )}
+                                        {this.selectedQuestion.answerParts.includes('angle of static friction') && (
+                                            <InputField
+                                                label={<p>Static friction angle</p>}
+                                                lowerBound={0}
+                                                dataDoc={this.dataDoc}
+                                                prop="review_StaticAngle"
+                                                step={1}
+                                                unit={'°'}
+                                                upperBound={360}
+                                                value={NumCast(this.dataDoc.review_StaticAngle)}
+                                                radianEquivalent={true}
+                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
+                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('angle of static friction')]}
+                                                labelWidth={'7em'}
+                                            />
+                                        )}
+                                        {this.selectedQuestion.answerParts.includes('coefficient of static friction') && (
+                                            <InputField
+                                                label={
+                                                    <Box>
+                                                        &mu;<sub>s</sub>
+                                                    </Box>
+                                                }
+                                                lowerBound={0}
+                                                dataDoc={this.dataDoc}
+                                                prop="coefficientOfStaticFriction"
+                                                step={0.1}
+                                                unit={''}
+                                                upperBound={1}
+                                                value={NumCast(this.dataDoc.coefficientOfStaticFriction)}
+                                                effect={this.updateReviewForcesBasedOnCoefficient}
+                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
+                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('coefficient of static friction')]}
+                                            />
+                                        )}
+                                        {this.selectedQuestion.answerParts.includes('wedge angle') && (
+                                            <InputField
+                                                label={<Box>&theta;</Box>}
+                                                lowerBound={0}
+                                                dataDoc={this.dataDoc}
+                                                prop="wedge_angle"
+                                                step={1}
+                                                unit={'°'}
+                                                upperBound={49}
+                                                value={this.wedgeAngle}
+                                                effect={(val: number) => {
+                                                    this.changeWedgeBasedOnNewAngle(val);
+                                                    this.updateReviewForcesBasedOnAngle(val);
+                                                }}
+                                                radianEquivalent={true}
+                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
+                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('wedge angle')]}
+                                            />
+                                        )}
+                                    </div>
+                                </div>
+                            </div>
+                        )}
+                        {this.simulationMode == 'Tutorial' && (
+                            <div className="wordProblemBox">
+                                <div className="question">
+                                    <h2>Problem</h2>
+                                    <p>{this.tutorial.question}</p>
+                                </div>
+                                <div
+                                    style={{
+                                        display: 'flex',
+                                        justifyContent: 'spaceBetween',
+                                        width: '100%',
+                                    }}>
+                                    <IconButton
+                                        onClick={() => {
+                                            let step = NumCast(this.dataDoc.tutorial_stepNumber) - 1;
+                                            step = Math.max(step, 0);
+                                            step = Math.min(step, this.tutorial.steps.length - 1);
+                                            this.dataDoc.tutorial_stepNumber = step;
+                                            this.dataDoc.mass1_forcesStart = JSON.stringify(this.tutorial.steps[step].forces);
+                                            this.dataDoc.mass1_forcesUpdated = JSON.stringify(this.tutorial.steps[step].forces);
+                                            this.dataDoc.simulation_showForceMagnitudes = this.tutorial.steps[step].showMagnitude;
+                                        }}
+                                        disabled={this.dataDoc.tutorial_stepNumber == 0}>
+                                        {/* <ArrowLeftIcon /> */}
+                                    </IconButton>
+                                    <div>
+                                        <h3>
+                                            Step {NumCast(this.dataDoc.tutorial_stepNumber) + 1}: {this.tutorial.steps[NumCast(this.dataDoc.tutorial_stepNumber)].description}
+                                        </h3>
+                                        <p>{this.tutorial.steps[NumCast(this.dataDoc.tutorial_stepNumber)].content}</p>
+                                    </div>
+                                    <IconButton
+                                        onClick={() => {
+                                            let step = NumCast(this.dataDoc.tutorial_stepNumber) + 1;
+                                            step = Math.max(step, 0);
+                                            step = Math.min(step, this.tutorial.steps.length - 1);
+                                            this.dataDoc.tutorial_stepNumber = step;
+                                            this.dataDoc.mass1_forcesStart = JSON.stringify(this.tutorial.steps[step].forces);
+                                            this.dataDoc.mass1_forcesUpdated = JSON.stringify(this.tutorial.steps[step].forces);
+                                            this.dataDoc.simulation_showForceMagnitudes = this.tutorial.steps[step].showMagnitude;
+                                        }}
+                                        disabled={this.dataDoc.tutorial_stepNumber === this.tutorial.steps.length - 1}>
+                                        {/* <ArrowRightIcon /> */}
+                                    </IconButton>
+                                </div>
+                                <div>
+                                    {(this.simulationType == 'One Weight' || this.simulationType == 'Inclined Plane' || this.simulationType == 'Pendulum') && <p>Resources</p>}
+                                    {this.simulationType == 'One Weight' && (
+                                        <ul>
+                                            <li>
+                                                <a
+                                                    href="https://www.khanacademy.org/science/physics/one-dimensional-motion"
+                                                    target="_blank"
+                                                    rel="noreferrer"
+                                                    style={{
+                                                        color: 'blue',
+                                                        textDecoration: 'underline',
+                                                    }}>
+                                                    Khan Academy - One Dimensional Motion
+                                                </a>
+                                            </li>
+                                            <li>
+                                                <a
+                                                    href="https://www.khanacademy.org/science/physics/two-dimensional-motion"
+                                                    target="_blank"
+                                                    rel="noreferrer"
+                                                    style={{
+                                                        color: 'blue',
+                                                        textDecoration: 'underline',
+                                                    }}>
+                                                    Khan Academy - Two Dimensional Motion
+                                                </a>
+                                            </li>
+                                        </ul>
+                                    )}
+                                    {this.simulationType == 'Inclined Plane' && (
+                                        <ul>
+                                            <li>
+                                                <a
+                                                    href="https://www.khanacademy.org/science/physics/forces-newtons-laws#normal-contact-force"
+                                                    target="_blank"
+                                                    rel="noreferrer"
+                                                    style={{
+                                                        color: 'blue',
+                                                        textDecoration: 'underline',
+                                                    }}>
+                                                    Khan Academy - Normal Force
+                                                </a>
+                                            </li>
+                                            <li>
+                                                <a
+                                                    href="https://www.khanacademy.org/science/physics/forces-newtons-laws#inclined-planes-friction"
+                                                    target="_blank"
+                                                    rel="noreferrer"
+                                                    style={{
+                                                        color: 'blue',
+                                                        textDecoration: 'underline',
+                                                    }}>
+                                                    Khan Academy - Inclined Planes
+                                                </a>
+                                            </li>
+                                        </ul>
+                                    )}
+                                    {this.simulationType == 'Pendulum' && (
+                                        <ul>
+                                            <li>
+                                                <a
+                                                    href="https://www.khanacademy.org/science/physics/forces-newtons-laws#tension-tutorial"
+                                                    target="_blank"
+                                                    rel="noreferrer"
+                                                    style={{
+                                                        color: 'blue',
+                                                        textDecoration: 'underline',
+                                                    }}>
+                                                    Khan Academy - Tension
+                                                </a>
+                                            </li>
+                                        </ul>
+                                    )}
+                                </div>
+                            </div>
+                        )}
+                        {this.simulationMode == 'Review' && this.simulationType == 'Inclined Plane' && (
+                            <div
+                                style={{
+                                    display: 'flex',
+                                    justifyContent: 'space-between',
+                                    marginTop: '10px',
+                                }}>
+                                <p
+                                    style={{
+                                        color: 'blue',
+                                        textDecoration: 'underline',
+                                        cursor: 'pointer',
+                                    }}
+                                    onClick={() => (this.dataDoc.simulation_mode = 'Tutorial')}>
+                                    {' '}
+                                    Go to walkthrough{' '}
+                                </p>
+                                <div style={{ display: 'flex', flexDirection: 'column' }}>
+                                    <Button
+                                        onClick={action(() => {
+                                            this._simReset++;
+                                            this.checkAnswers();
+                                            this.dataDoc.simulation_showIcon = true;
+                                        })}
+                                        variant="outlined">
+                                        <p>Submit</p>
+                                    </Button>
+                                    <Button
+                                        onClick={() => {
+                                            this.generateNewQuestion();
+                                            this.dataDoc.simulation_showIcon = false;
+                                        }}
+                                        variant="outlined">
+                                        <p>New question</p>
+                                    </Button>
+                                </div>
+                            </div>
+                        )}
+                        {this.simulationMode == 'Freeform' && (
+                            <div className="vars">
+                                <FormControl component="fieldset">
+                                    <FormGroup>
+                                        {this.simulationType == 'One Weight' && (
+                                            <FormControlLabel
+                                                control={<Checkbox checked={BoolCast(this.dataDoc.elasticCollisions)} onChange={() => (this.dataDoc.elasticCollisions = !this.dataDoc.elasticCollisions)} />}
+                                                label="Make collisions elastic"
+                                                labelPlacement="start"
+                                            />
+                                        )}
+                                        <FormControlLabel
+                                            control={<Checkbox checked={BoolCast(this.dataDoc.simulation_showForces)} onChange={() => (this.dataDoc.simulation_showForces = !this.dataDoc.simulation_showForces)} />}
+                                            label="Show force vectors"
+                                            labelPlacement="start"
+                                        />
+                                        {(this.simulationType == 'Inclined Plane' || this.simulationType == 'Pendulum') && (
+                                            <FormControlLabel
+                                                control={<Checkbox checked={BoolCast(this.dataDoc.simulation_showComponentForces)} onChange={() => (this.dataDoc.simulation_showComponentForces = !this.dataDoc.simulation_showComponentForces)} />}
+                                                label="Show component force vectors"
+                                                labelPlacement="start"
+                                            />
+                                        )}
+                                        <FormControlLabel
+                                            control={<Checkbox checked={BoolCast(this.dataDoc.simulation_showAcceleration)} onChange={() => (this.dataDoc.simulation_showAcceleration = !this.dataDoc.simulation_showAcceleration)} />}
+                                            label="Show acceleration vector"
+                                            labelPlacement="start"
+                                        />
+                                        <FormControlLabel
+                                            control={<Checkbox checked={BoolCast(this.dataDoc.simulation_showVelocity)} onChange={() => (this.dataDoc.simulation_showVelocity = !this.dataDoc.simulation_showVelocity)} />}
+                                            label="Show velocity vector"
+                                            labelPlacement="start"
+                                        />
+                                        <InputField label={<Box>Speed</Box>} lowerBound={1} dataDoc={this.dataDoc} prop="simulation_speed" step={1} unit={'x'} upperBound={10} value={NumCast(this.dataDoc.simulation_speed, 2)} labelWidth={'5em'} />
+                                        {this.dataDoc.simulation_paused && this.simulationType != 'Circular Motion' && (
+                                            <InputField
+                                                label={<Box>Gravity</Box>}
+                                                lowerBound={-30}
+                                                dataDoc={this.dataDoc}
+                                                prop="gravity"
+                                                step={0.01}
+                                                unit={'m/s2'}
+                                                upperBound={0}
+                                                value={NumCast(this.dataDoc.simulation_gravity, -9.81)}
+                                                effect={(val: number) => this.setupSimulation()}
+                                                labelWidth={'5em'}
+                                            />
+                                        )}
+                                        {this.dataDoc.simulation_paused && this.simulationType != 'Pulley' && (
+                                            <InputField
+                                                label={<Box>Mass</Box>}
+                                                lowerBound={1}
+                                                dataDoc={this.dataDoc}
+                                                prop="mass1"
+                                                step={0.1}
+                                                unit={'kg'}
+                                                upperBound={5}
+                                                value={this.mass1 ?? 1}
+                                                effect={(val: number) => this.setupSimulation()}
+                                                labelWidth={'5em'}
+                                            />
+                                        )}
+                                        {this.dataDoc.simulation_paused && this.simulationType == 'Pulley' && (
+                                            <InputField
+                                                label={<Box>Red mass</Box>}
+                                                lowerBound={1}
+                                                dataDoc={this.dataDoc}
+                                                prop="mass1"
+                                                step={0.1}
+                                                unit={'kg'}
+                                                upperBound={5}
+                                                value={this.mass1 ?? 1}
+                                                effect={(val: number) => this.setupSimulation()}
+                                                labelWidth={'5em'}
+                                            />
+                                        )}
+                                        {this.dataDoc.simulation_paused && this.simulationType == 'Pulley' && (
+                                            <InputField
+                                                label={<Box>Blue mass</Box>}
+                                                lowerBound={1}
+                                                dataDoc={this.dataDoc}
+                                                prop="mass2"
+                                                step={0.1}
+                                                unit={'kg'}
+                                                upperBound={5}
+                                                value={this.mass2 ?? 1}
+                                                effect={(val: number) => this.setupSimulation()}
+                                                labelWidth={'5em'}
+                                            />
+                                        )}
+                                        {this.dataDoc.simulation_paused && this.simulationType == 'Circular Motion' && (
+                                            <InputField
+                                                label={<Box>Rod length</Box>}
+                                                lowerBound={100}
+                                                dataDoc={this.dataDoc}
+                                                prop="circularMotionRadius"
+                                                step={5}
+                                                unit={'m'}
+                                                upperBound={250}
+                                                value={this.circularMotionRadius}
+                                                effect={(val: number) => this.setupSimulation()}
+                                                labelWidth={'5em'}
+                                            />
+                                        )}
+                                    </FormGroup>
+                                </FormControl>
+                                {this.simulationType == 'Spring' && this.dataDoc.simulation_paused && (
+                                    <div>
+                                        <InputField
+                                            label={<Typography color="inherit">Spring stiffness</Typography>}
+                                            lowerBound={0.1}
+                                            dataDoc={this.dataDoc}
+                                            prop="spring_constant"
+                                            step={1}
+                                            unit={'N/m'}
+                                            upperBound={500}
+                                            value={this.springConstant}
+                                            effect={action(() => this._simReset++)}
+                                            radianEquivalent={false}
+                                            mode={'Freeform'}
+                                            labelWidth={'7em'}
+                                        />
+                                        <InputField
+                                            label={<Typography color="inherit">Rest length</Typography>}
+                                            lowerBound={10}
+                                            dataDoc={this.dataDoc}
+                                            prop="spring_lengthRest"
+                                            step={100}
+                                            unit=""
+                                            upperBound={500}
+                                            value={this.springLengthRest}
+                                            effect={action(() => this._simReset++)}
+                                            radianEquivalent={false}
+                                            mode="Freeform"
+                                            labelWidth={'7em'}
+                                        />
+                                        <InputField
+                                            label={<Typography color="inherit">Starting displacement</Typography>}
+                                            lowerBound={-(this.springLengthRest - 10)}
+                                            dataDoc={this.dataDoc}
+                                            prop=""
+                                            step={10}
+                                            unit=""
+                                            upperBound={this.springLengthRest}
+                                            value={this.springLengthStart - this.springLengthRest}
+                                            effect={action((val: number) => {
+                                                this.dataDoc.mass1_positionYstart = this.springLengthRest + val;
+                                                this.dataDoc.spring_lengthStart = this.springLengthRest + val;
+                                                this._simReset++;
+                                            })}
+                                            radianEquivalent={false}
+                                            mode="Freeform"
+                                            labelWidth={'7em'}
+                                        />
+                                    </div>
+                                )}
+                                {this.simulationType == 'Inclined Plane' && this.dataDoc.simulation_paused && (
+                                    <div>
+                                        <InputField
+                                            label={<Box>&theta;</Box>}
+                                            lowerBound={0}
+                                            dataDoc={this.dataDoc}
+                                            prop="wedge_angle"
+                                            step={1}
+                                            unit={'°'}
+                                            upperBound={49}
+                                            value={this.wedgeAngle}
+                                            effect={action((val: number) => {
+                                                this.changeWedgeBasedOnNewAngle(val);
+                                                this._simReset++;
+                                            })}
+                                            radianEquivalent={true}
+                                            mode={'Freeform'}
+                                            labelWidth={'2em'}
+                                        />
+                                        <InputField
+                                            label={
+                                                <Box>
+                                                    &mu;<sub>s</sub>
+                                                </Box>
+                                            }
+                                            lowerBound={0}
+                                            dataDoc={this.dataDoc}
+                                            prop="coefficientOfStaticFriction"
+                                            step={0.1}
+                                            unit={''}
+                                            upperBound={1}
+                                            value={NumCast(this.dataDoc.coefficientOfStaticFriction) ?? 0}
+                                            effect={action((val: number) => {
+                                                this.updateForcesWithFriction(val);
+                                                if (val < NumCast(this.dataDoc.coefficientOfKineticFriction)) {
+                                                    this.dataDoc.soefficientOfKineticFriction = val;
+                                                }
+                                                this._simReset++;
+                                            })}
+                                            mode={'Freeform'}
+                                            labelWidth={'2em'}
+                                        />
+                                        <InputField
+                                            label={
+                                                <Box>
+                                                    &mu;<sub>k</sub>
+                                                </Box>
+                                            }
+                                            lowerBound={0}
+                                            dataDoc={this.dataDoc}
+                                            prop="coefficientOfKineticFriction"
+                                            step={0.1}
+                                            unit={''}
+                                            upperBound={NumCast(this.dataDoc.coefficientOfStaticFriction)}
+                                            value={NumCast(this.dataDoc.coefficientOfKineticFriction) ?? 0}
+                                            effect={action(() => this._simReset++)}
+                                            mode={'Freeform'}
+                                            labelWidth={'2em'}
+                                        />
+                                    </div>
+                                )}
+                                {this.simulationType == 'Inclined Plane' && !this.dataDoc.simulation_paused && (
+                                    <Typography>
+                                        <>
+                                            &theta;: {Math.round(this.wedgeAngle * 100) / 100}° ≈ {Math.round(((this.wedgeAngle * Math.PI) / 180) * 100) / 100} rad
+                                            <br />
+                                            &mu; <sub>s</sub>: {this.dataDoc.coefficientOfStaticFriction}
+                                            <br />
+                                            &mu; <sub>k</sub>: {this.dataDoc.coefficientOfKineticFriction}
+                                        </>
+                                    </Typography>
+                                )}
+                                {this.simulationType == 'Pendulum' && !this.dataDoc.simulation_paused && (
+                                    <Typography>
+                                        &theta;: {Math.round(this.pendulumAngle * 100) / 100}° ≈ {Math.round(((this.pendulumAngle * Math.PI) / 180) * 100) / 100} rad
+                                    </Typography>
+                                )}
+                                {this.simulationType == 'Pendulum' && this.dataDoc.simulation_paused && (
+                                    <div>
+                                        <InputField
+                                            label={<Box>Angle</Box>}
+                                            lowerBound={0}
+                                            dataDoc={this.dataDoc}
+                                            prop="pendulum_angle"
+                                            step={1}
+                                            unit={'°'}
+                                            upperBound={59}
+                                            value={NumCast(this.dataDoc.pendulum_angle, 30)}
+                                            effect={action(value => {
+                                                this.dataDoc.pendulum_angleStart = value;
+                                                this.dataDoc.pendulum_lengthStart = this.dataDoc.pendulum_length;
+                                                if (this.simulationType == 'Pendulum') {
+                                                    const mag = this.mass1 * Math.abs(this.gravity) * Math.cos((value * Math.PI) / 180);
+
+                                                    const forceOfTension: IForce = {
+                                                        description: 'Tension',
+                                                        magnitude: mag,
+                                                        directionInDegrees: 90 - value,
+                                                    };
+                                                    const gravityParallel: IForce = {
+                                                        description: 'Gravity Parallel Component',
+                                                        magnitude: Math.abs(this.gravity) * Math.cos((value * Math.PI) / 180),
+                                                        directionInDegrees: 270 - value,
+                                                    };
+                                                    const gravityPerpendicular: IForce = {
+                                                        description: 'Gravity Perpendicular Component',
+                                                        magnitude: Math.abs(this.gravity) * Math.sin((value * Math.PI) / 180),
+                                                        directionInDegrees: -value,
+                                                    };
+
+                                                    const length = this.pendulumLength;
+                                                    const x = length * Math.cos(((90 - value) * Math.PI) / 180);
+                                                    const y = length * Math.sin(((90 - value) * Math.PI) / 180);
+                                                    const xPos = this.xMax / 2 - x - NumCast(this.dataDoc.radius);
+                                                    const yPos = y - NumCast(this.dataDoc.radius) - 5;
+                                                    this.dataDoc.mass1_positionXstart = xPos;
+                                                    this.dataDoc.mass1_positionYstart = yPos;
+
+                                                    this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1), forceOfTension]);
+                                                    this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1), forceOfTension]);
+                                                    this.dataDoc.mass1_componentForces = JSON.stringify([forceOfTension, gravityParallel, gravityPerpendicular]);
+                                                    this._simReset++;
+                                                }
+                                            })}
+                                            radianEquivalent={true}
+                                            mode="Freeform"
+                                            labelWidth="5em"
+                                        />
+                                        <InputField
+                                            label={<Box>Rod length</Box>}
+                                            lowerBound={0}
+                                            dataDoc={this.dataDoc}
+                                            prop="pendulum_length"
+                                            step={1}
+                                            unit="m"
+                                            upperBound={400}
+                                            value={Math.round(this.pendulumLength)}
+                                            effect={action(value => {
+                                                if (this.simulationType == 'Pendulum') {
+                                                    this.dataDoc.pendulum_angleStart = this.pendulumAngle;
+                                                    this.dataDoc.pendulum_lengthStart = value;
+                                                    this._simReset++;
+                                                }
+                                            })}
+                                            radianEquivalent={false}
+                                            mode="Freeform"
+                                            labelWidth="5em"
+                                        />
+                                    </div>
+                                )}
+                            </div>
+                        )}
+                        <div className="mechanicsSimulationEquation">
+                            {this.simulationMode == 'Freeform' && (
+                                <table>
+                                    <tbody>
+                                        <tr>
+                                            <td>{this.simulationType == 'Pulley' ? 'Red Weight' : ''}</td>
+                                            <td>X</td>
+                                            <td>Y</td>
+                                        </tr>
+                                        <tr>
+                                            <td
+                                                style={{ cursor: 'help' }}
+                                                // onClick={() => {
+                                                //   window.open(
+                                                //     "https://www.khanacademy.org/science/physics/two-dimensional-motion"
+                                                //   );
+                                                // }}
+                                            >
+                                                <Box>Position</Box>
+                                            </td>
+                                            {(!this.dataDoc.simulation_paused || this.simulationType == 'Inclined Plane' || this.simulationType == 'Circular Motion' || this.simulationType == 'Pulley') && (
+                                                <td style={{ cursor: 'default' }}>
+                                                    <>{this.dataDoc.mass1_positionX} m</>
+                                                </td>
+                                            )}{' '}
+                                            {this.dataDoc.simulation_paused && this.simulationType != 'Inclined Plane' && this.simulationType != 'Circular Motion' && this.simulationType != 'Pulley' && (
+                                                <td
+                                                    style={{
+                                                        cursor: 'default',
+                                                    }}>
+                                                    <InputField
+                                                        lowerBound={this.simulationType == 'Projectile' ? 1 : (this.xMax + this.xMin) / 4 - this.radius - 15}
+                                                        dataDoc={this.dataDoc}
+                                                        prop="mass1_positionX"
+                                                        step={1}
+                                                        unit={'m'}
+                                                        upperBound={this.simulationType == 'Projectile' ? this.xMax - 110 : (3 * (this.xMax + this.xMin)) / 4 - this.radius / 2 - 15}
+                                                        value={NumCast(this.dataDoc.mass1_positionX)}
+                                                        effect={value => {
+                                                            this.dataDoc.mass1_xChange = value;
+                                                            if (this.simulationType == 'Suspension') {
+                                                                let x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200;
+                                                                let x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius;
+                                                                let deltaX1 = value + this.radius - x1rod;
+                                                                let deltaX2 = x2rod - (value + this.radius);
+                                                                let deltaY = this.getYPosFromDisplay(NumCast(this.dataDoc.mass1_positionY)) + this.radius;
+                                                                let dir1T = Math.PI - Math.atan(deltaY / deltaX1);
+                                                                let dir2T = Math.atan(deltaY / deltaX2);
+                                                                let tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T));
+                                                                let tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T);
+                                                                dir1T = (dir1T * 180) / Math.PI;
+                                                                dir2T = (dir2T * 180) / Math.PI;
+                                                                const tensionForce1: IForce = {
+                                                                    description: 'Tension',
+                                                                    magnitude: tensionMag1,
+                                                                    directionInDegrees: dir1T,
+                                                                };
+                                                                const tensionForce2: IForce = {
+                                                                    description: 'Tension',
+                                                                    magnitude: tensionMag2,
+                                                                    directionInDegrees: dir2T,
+                                                                };
+                                                                const gravity = this.gravityForce(this.mass1);
+                                                                this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce1, tensionForce2, gravity]);
+                                                            }
+                                                        }}
+                                                        small={true}
+                                                        mode="Freeform"
+                                                    />
+                                                </td>
+                                            )}{' '}
+                                            {(!this.dataDoc.simulation_paused || this.simulationType == 'Inclined Plane' || this.simulationType == 'Circular Motion' || this.simulationType == 'Pulley') && (
+                                                <td style={{ cursor: 'default' }}>{`${NumCast(this.dataDoc.mass1_positionY)} m`}</td>
+                                            )}{' '}
+                                            {this.dataDoc.simulation_paused && this.simulationType != 'Inclined Plane' && this.simulationType != 'Circular Motion' && this.simulationType != 'Pulley' && (
+                                                <td
+                                                    style={{
+                                                        cursor: 'default',
+                                                    }}>
+                                                    <InputField
+                                                        lowerBound={1}
+                                                        dataDoc={this.dataDoc}
+                                                        prop="mass1_positionY"
+                                                        step={1}
+                                                        unit="m"
+                                                        upperBound={this.yMax - 110}
+                                                        value={NumCast(this.dataDoc.mass1_positionY)}
+                                                        effect={value => {
+                                                            this.dataDoc.mass1_yChange = value;
+                                                            if (this.simulationType == 'Suspension') {
+                                                                let x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200;
+                                                                let x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius;
+                                                                let deltaX1 = NumCast(this.dataDoc.mass1_positionX) + this.radius - x1rod;
+                                                                let deltaX2 = x2rod - (NumCast(this.dataDoc.mass1_positionX) + this.radius);
+                                                                let deltaY = this.getYPosFromDisplay(value) + this.radius;
+                                                                let dir1T = Math.PI - Math.atan(deltaY / deltaX1);
+                                                                let dir2T = Math.atan(deltaY / deltaX2);
+                                                                let tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T));
+                                                                let tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T);
+                                                                dir1T = (dir1T * 180) / Math.PI;
+                                                                dir2T = (dir2T * 180) / Math.PI;
+                                                                const tensionForce1: IForce = {
+                                                                    description: 'Tension',
+                                                                    magnitude: tensionMag1,
+                                                                    directionInDegrees: dir1T,
+                                                                };
+                                                                const tensionForce2: IForce = {
+                                                                    description: 'Tension',
+                                                                    magnitude: tensionMag2,
+                                                                    directionInDegrees: dir2T,
+                                                                };
+                                                                const gravity = this.gravityForce(this.mass1);
+                                                                this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce1, tensionForce2, gravity]);
+                                                            }
+                                                        }}
+                                                        small={true}
+                                                        mode="Freeform"
+                                                    />
+                                                </td>
+                                            )}{' '}
+                                        </tr>
+                                        <tr>
+                                            <td
+                                                style={{ cursor: 'help' }}
+                                                // onClick={() => {
+                                                //   window.open(
+                                                //     "https://www.khanacademy.org/science/physics/two-dimensional-motion"
+                                                //   );
+                                                // }}
+                                            >
+                                                <Box>Velocity</Box>
+                                            </td>
+                                            {(!this.dataDoc.simulation_paused || (this.simulationType != 'One Weight' && this.simulationType != 'Circular Motion')) && (
+                                                <td style={{ cursor: 'default' }}>{`${NumCast(this.dataDoc.mass1_velocityX)} m/s`}</td>
+                                            )}{' '}
+                                            {this.dataDoc.simulation_paused && (this.simulationType == 'One Weight' || this.simulationType == 'Circular Motion') && (
+                                                <td
+                                                    style={{
+                                                        cursor: 'default',
+                                                    }}>
+                                                    <InputField
+                                                        lowerBound={-50}
+                                                        dataDoc={this.dataDoc}
+                                                        prop="mass1_velocityX"
+                                                        step={1}
+                                                        unit={'m/s'}
+                                                        upperBound={50}
+                                                        value={NumCast(this.dataDoc.mass1_velocityX)}
+                                                        effect={action(value => {
+                                                            this.dataDoc.mass1_velocityXstart = value;
+                                                            this._simReset++;
+                                                        })}
+                                                        small={true}
+                                                        mode="Freeform"
+                                                    />
+                                                </td>
+                                            )}{' '}
+                                            {(!this.dataDoc.simulation_paused || this.simulationType != 'One Weight') && (
+                                                <td style={{ cursor: 'default' }}>
+                                                    <>{this.dataDoc.mass1_velocityY} m/s</>
+                                                </td>
+                                            )}{' '}
+                                            {this.dataDoc.simulation_paused && this.simulationType == 'One Weight' && (
+                                                <td
+                                                    style={{
+                                                        cursor: 'default',
+                                                    }}>
+                                                    <InputField
+                                                        lowerBound={-50}
+                                                        dataDoc={this.dataDoc}
+                                                        prop="mass1_velocityY"
+                                                        step={1}
+                                                        unit="m/s"
+                                                        upperBound={50}
+                                                        value={NumCast(this.dataDoc.mass1_velocityY)}
+                                                        effect={value => {
+                                                            this.dataDoc.mass1_velocityYstart = -value;
+                                                        }}
+                                                        small={true}
+                                                        mode="Freeform"
+                                                    />
+                                                </td>
+                                            )}{' '}
+                                        </tr>
+                                        <tr>
+                                            <td
+                                                style={{ cursor: 'help' }}
+                                                // onClick={() => {
+                                                //   window.open(
+                                                //     "https://www.khanacademy.org/science/physics/two-dimensional-motion"
+                                                //   );
+                                                // }}
+                                            >
+                                                <Box>Acceleration</Box>
+                                            </td>
+                                            <td style={{ cursor: 'default' }}>
+                                                <>
+                                                    {this.dataDoc.mass1_accelerationX} m/s<sup>2</sup>
+                                                </>
+                                            </td>
+                                            <td style={{ cursor: 'default' }}>
+                                                <>
+                                                    {this.dataDoc.mass1_accelerationY} m/s<sup>2</sup>
+                                                </>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <td>
+                                                <Box>Momentum</Box>
+                                            </td>
+                                            <td>{Math.round(NumCast(this.dataDoc.mass1_velocityX) * this.mass1 * 10) / 10} kg*m/s</td>
+                                            <td>{Math.round(NumCast(this.dataDoc.mass1_velocityY) * this.mass1 * 10) / 10} kg*m/s</td>
+                                        </tr>
+                                    </tbody>
+                                </table>
+                            )}
+                            {this.simulationMode == 'Freeform' && this.simulationType == 'Pulley' && (
+                                <table>
+                                    <tbody>
+                                        <tr>
+                                            <td>Blue Weight</td>
+                                            <td>X</td>
+                                            <td>Y</td>
+                                        </tr>
+                                        <tr>
+                                            <td>
+                                                <Box>Position</Box>
+                                            </td>
+                                            <td style={{ cursor: 'default' }}>{`${this.dataDoc.mass2_positionX} m`}</td>
+                                            <td style={{ cursor: 'default' }}>{`${this.dataDoc.mass2_positionY} m`}</td>
+                                        </tr>
+                                        <tr>
+                                            <td>
+                                                <Box>Velocity</Box>
+                                            </td>
+                                            <td style={{ cursor: 'default' }}>{`${this.dataDoc.mass2_positionX} m/s`}</td>
+                                            <td style={{ cursor: 'default' }}>{`${this.dataDoc.mass2_positionY} m/s`}</td>
+                                        </tr>
+                                        <tr>
+                                            <td>
+                                                <Box>Acceleration</Box>
+                                            </td>
+                                            <td style={{ cursor: 'default' }}>
+                                                <>
+                                                    {this.dataDoc.mass2_accelerationX} m/s<sup>2</sup>
+                                                </>
+                                            </td>
+                                            <td style={{ cursor: 'default' }}>
+                                                <>
+                                                    {this.dataDoc.mass2_accelerationY} m/s<sup>2</sup>
+                                                </>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <td>
+                                                <Box>Momentum</Box>
+                                            </td>
+                                            <td>{Math.round(NumCast(this.dataDoc.mass2_velocityX) * this.mass1 * 10) / 10} kg*m/s</td>
+                                            <td>{Math.round(NumCast(this.dataDoc.mass2_velocityY) * this.mass1 * 10) / 10} kg*m/s</td>
+                                        </tr>
+                                    </tbody>
+                                </table>
+                            )}
+                        </div>
+                        {this.simulationType != 'Pendulum' && this.simulationType != 'Spring' && (
+                            <div>
+                                <p>Kinematic Equations</p>
+                                <ul>
+                                    <li>
+                                        Position: x<sub>1</sub>=x<sub>0</sub>+v<sub>0</sub>t+
+                                        <sup>1</sup>&frasl;
+                                        <sub>2</sub>at
+                                        <sup>2</sup>
+                                    </li>
+                                    <li>
+                                        Velocity: v<sub>1</sub>=v<sub>0</sub>+at
+                                    </li>
+                                    <li>Acceleration: a = F/m</li>
+                                </ul>
+                            </div>
+                        )}
+                        {this.simulationType == 'Spring' && (
+                            <div>
+                                <p>Harmonic Motion Equations: Spring</p>
+                                <ul>
+                                    <li>
+                                        Spring force: F<sub>s</sub>=kd
+                                    </li>
+                                    <li>
+                                        Spring period: T<sub>s</sub>=2&pi;&#8730;<sup>m</sup>&frasl;
+                                        <sub>k</sub>
+                                    </li>
+                                    <li>Equilibrium displacement for vertical spring: d = mg/k</li>
+                                    <li>
+                                        Elastic potential energy: U<sub>s</sub>=<sup>1</sup>&frasl;
+                                        <sub>2</sub>kd<sup>2</sup>
+                                    </li>
+                                    <ul>
+                                        <li>Maximum when system is at maximum displacement, 0 when system is at 0 displacement</li>
+                                    </ul>
+                                    <li>
+                                        Translational kinetic energy: K=<sup>1</sup>&frasl;
+                                        <sub>2</sub>mv<sup>2</sup>
+                                    </li>
+                                    <ul>
+                                        <li>Maximum when system is at maximum/minimum velocity (at 0 displacement), 0 when velocity is 0 (at maximum displacement)</li>
+                                    </ul>
+                                </ul>
+                            </div>
+                        )}
+                        {this.simulationType == 'Pendulum' && (
+                            <div>
+                                <p>Harmonic Motion Equations: Pendulum</p>
+                                <ul>
+                                    <li>
+                                        Pendulum period: T<sub>p</sub>=2&pi;&#8730;<sup>l</sup>&frasl;
+                                        <sub>g</sub>
+                                    </li>
+                                </ul>
+                            </div>
+                        )}
+                    </div>
+                </div>
+                <div
+                    style={{
+                        position: 'fixed',
+                        top: this.yMax - 120 + 20 + 'px',
+                        left: this.xMin + 90 - 80 + 'px',
+                        zIndex: -10000,
+                    }}>
+                    <svg width={100 + 'px'} height={100 + 'px'}>
+                        <defs>
+                            <marker id="miniArrow" markerWidth="20" markerHeight="20" refX="0" refY="3" orient="auto" markerUnits="strokeWidth">
+                                <path d="M0,0 L0,6 L9,3 z" fill={'#000000'} />
+                            </marker>
+                        </defs>
+                        <line x1={20} y1={70} x2={70} y2={70} stroke={'#000000'} strokeWidth="2" markerEnd="url(#miniArrow)" />
+                        <line x1={20} y1={70} x2={20} y2={20} stroke={'#000000'} strokeWidth="2" markerEnd="url(#miniArrow)" />
+                    </svg>
+                    <p
+                        style={{
+                            position: 'fixed',
+                            top: this.yMax - 120 + 40 + 'px',
+                            left: this.xMin + 90 - 80 + 'px',
+                        }}>
+                        {this.simulationType == 'Circular Motion' ? 'Z' : 'Y'}
+                    </p>
+                    <p
+                        style={{
+                            position: 'fixed',
+                            top: this.yMax - 120 + 80 + 'px',
+                            left: this.xMin + 90 - 40 + 'px',
+                        }}>
+                        X
+                    </p>
+                </div>
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx
new file mode 100644
index 000000000..d595a499e
--- /dev/null
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx
@@ -0,0 +1,179 @@
+import { TextField, InputAdornment } from '@mui/material';
+import { Doc } from '../../../../fields/Doc';
+import React = require('react');
+import TaskAltIcon from '@mui/icons-material/TaskAlt';
+import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
+import { isNumber } from 'lodash';
+export interface IInputProps {
+    label?: JSX.Element;
+    lowerBound: number;
+    dataDoc: Doc;
+    prop: string;
+    step: number;
+    unit: string;
+    upperBound: number;
+    value: number | string | Array<number | string>;
+    correctValue?: number;
+    showIcon?: boolean;
+    effect?: (val: number) => any;
+    radianEquivalent?: boolean;
+    small?: boolean;
+    mode?: string;
+    update?: boolean;
+    labelWidth?: string;
+}
+
+interface IState {
+    tempValue: string | number | (string | number)[];
+    tempRadianValue: number;
+    width: string;
+    margin: string;
+}
+
+export default class InputField extends React.Component<IInputProps, IState> {
+    constructor(props: any) {
+        super(props);
+        this.state = {
+            tempValue: this.props.mode != 'Freeform' && !this.props.showIcon ? 0 : this.props.value,
+            tempRadianValue: this.props.mode != 'Freeform' && !this.props.showIcon ? 0 : (Number(this.props.value) * Math.PI) / 180,
+            width: this.props.small ? '6em' : '7em',
+            margin: this.props.small ? '0px' : '10px',
+        };
+    }
+
+    epsilon: number = 0.01;
+
+    componentDidMount(): void {
+        this.setState({ tempValue: Number(this.props.value) });
+    }
+
+    componentDidUpdate(prevProps: Readonly<IInputProps>, prevState: Readonly<IState>, snapshot?: any): void {
+        if (prevProps.value != this.props.value && isNumber(this.props.value) && !isNaN(this.props.value)) {
+            if (this.props.mode == 'Freeform') {
+                if (isNumber(this.state.tempValue) && Math.abs(this.state.tempValue - Number(this.props.value)) > 1) {
+                    this.setState({ tempValue: Number(this.props.value) });
+                }
+            }
+        }
+        if (prevProps.update != this.props.update) {
+            this.externalUpdate();
+        }
+    }
+
+    externalUpdate = () => {
+        this.setState({ tempValue: Number(this.props.value) });
+        this.setState({ tempRadianValue: (Number(this.props.value) * Math.PI) / 180 });
+    };
+
+    onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+        let value = event.target.value == '' ? 0 : Number(event.target.value);
+        if (value > this.props.upperBound) {
+            value = this.props.upperBound;
+        } else if (value < this.props.lowerBound) {
+            value = this.props.lowerBound;
+        }
+        if (this.props.prop != '') {
+            this.props.dataDoc[this.props.prop] = value;
+        }
+        this.setState({ tempValue: event.target.value == '' ? event.target.value : value });
+        this.setState({ tempRadianValue: (value * Math.PI) / 180 });
+        if (this.props.effect) {
+            this.props.effect(value);
+        }
+    };
+
+    onChangeRadianValue = (event: React.ChangeEvent<HTMLInputElement>) => {
+        let value = event.target.value === '' ? 0 : Number(event.target.value);
+        if (value > 2 * Math.PI) {
+            value = 2 * Math.PI;
+        } else if (value < 0) {
+            value = 0;
+        }
+        if (this.props.prop != '') {
+            this.props.dataDoc[this.props.prop] = (value * 180) / Math.PI;
+        }
+        this.setState({ tempValue: (value * 180) / Math.PI });
+        this.setState({ tempRadianValue: value });
+        if (this.props.effect) {
+            this.props.effect((value * 180) / Math.PI);
+        }
+    };
+
+    render() {
+        return (
+            <div
+                style={{
+                    display: 'flex',
+                    lineHeight: '1.5',
+                    textAlign: 'right',
+                    alignItems: 'center',
+                }}>
+                {this.props.label && (
+                    <div
+                        style={{
+                            marginTop: '0.3em',
+                            marginBottom: '-0.5em',
+                            width: this.props.labelWidth ?? '2em',
+                        }}>
+                        {this.props.label}
+                    </div>
+                )}
+                <TextField
+                    type="number"
+                    variant="standard"
+                    value={this.state.tempValue}
+                    onChange={this.onChange}
+                    sx={{
+                        height: '1em',
+                        width: this.state.width,
+                        marginLeft: this.state.margin,
+                        zIndex: 'modal',
+                    }}
+                    inputProps={{
+                        step: this.props.step,
+                        min: this.props.lowerBound,
+                        max: this.props.upperBound,
+                        type: 'number',
+                    }}
+                    InputProps={{
+                        startAdornment: (
+                            <InputAdornment position="start">
+                                {Math.abs(Number(this.props.value) - (this.props.correctValue ?? 0)) < this.epsilon && this.props.showIcon && <TaskAltIcon color={'success'} />}
+                                {Math.abs(Number(this.props.value) - (this.props.correctValue ?? 0)) >= this.epsilon && this.props.showIcon && <ErrorOutlineIcon color={'error'} />}
+                            </InputAdornment>
+                        ),
+                        endAdornment: <InputAdornment position="end">{this.props.unit}</InputAdornment>,
+                    }}
+                />
+                {this.props.radianEquivalent && (
+                    <div style={{ marginTop: '0.3em', marginBottom: '-0.5em', width: '1em' }}>
+                        <p>≈</p>
+                    </div>
+                )}
+                {this.props.radianEquivalent && (
+                    <TextField
+                        type="number"
+                        variant="standard"
+                        value={this.state.tempRadianValue}
+                        onChange={this.onChangeRadianValue}
+                        sx={{
+                            height: '1em',
+                            width: this.state.width,
+                            marginLeft: this.state.margin,
+                            zIndex: 'modal',
+                        }}
+                        inputProps={{
+                            step: Math.PI / 8,
+                            min: 0,
+                            max: 2 * Math.PI,
+                            type: 'number',
+                        }}
+                        InputProps={{
+                            endAdornment: <InputAdornment position="end">rad</InputAdornment>,
+                        }}
+                    />
+                )}
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationQuestions.json b/src/client/views/nodes/PhysicsBox/PhysicsSimulationQuestions.json
new file mode 100644
index 000000000..cc79f7aad
--- /dev/null
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationQuestions.json
@@ -0,0 +1,161 @@
+{
+  "inclinePlane": [
+    {
+      "questionSetup": [
+        "There is a 1kg weight on an inclined plane. The plane is at a ",
+        " angle from the ground. The system is in equilibrium (the net force on the weight is 0)."
+      ],
+      "variablesForQuestionSetup": ["theta - max 45"],
+      "question": "What are the magnitudes and directions of the forces acting on the weight?",
+      "answerParts": [
+        "force of gravity",
+        "angle of gravity",
+        "normal force",
+        "angle of normal force",
+        "force of static friction",
+        "angle of static friction"
+      ],
+      "answerSolutionDescriptions": [
+        "9.81",
+        "270",
+        "solve normal force magnitude from wedge angle",
+        "solve normal force angle from wedge angle",
+        "solve static force magnitude from wedge angle given equilibrium",
+        "solve static force angle from wedge angle given equilibrium"
+      ],
+      "goal": "noMovement",
+      "hints": [
+        {
+          "description": "Direction of Force of Gravity",
+          "content": "The force of gravity acts in the negative y direction: 3π/2 rad."
+        },
+        {
+          "description": "Direction of Normal Force",
+          "content": "The normal force acts in the direction perpendicular to the incline plane: π/2-θ rad, where θ is the angle of the incline plane."
+        },
+        {
+          "description": "Direction of Force of Friction",
+          "content": "The force of friction acts in the direction along the incline plane: π-θ rad, where θ is the angle of the incline plane."
+        },
+        {
+          "description": "Magnitude of Force of Gravity",
+          "content": "The magnitude of the force of gravity is approximately 9.81."
+        },
+        {
+          "description": "Magnitude of Normal Force",
+          "content": "The magnitude of the normal force is equal to m*g*cos(θ), where θ is the angle of the incline plane."
+        },
+        {
+          "description": "Net Force in Equilibrium",
+          "content": "For the system to be in equilibrium, the sum of the x components of all forces must equal 0, and the sum of the y components of all forces must equal 0."
+        },
+        {
+          "description": "X Component of Normal Force",
+          "content": "The x component of the normal force is equal to m*g*cos(θ)*cos(π/2-θ), where θ is the angle of the incline plane."
+        },
+        {
+          "description": "X Component of Force of Friction",
+          "content": "Since the net force in the x direction must be 0, we know the magnitude of the x component of the friction force is m*g*cos(θ)*cos(π/2-θ)."
+        },
+        {
+          "description": "Y Component of Normal Force",
+          "content": "The y component of the normal force is equal to m*g*cos(θ)*sin(π/2-θ), where θ is the angle of the incline plane. The y component of gravity is equal to m*g"
+        },
+        {
+          "description": "Y Component of Force of Friction",
+          "content": "Since the net force in the x direction must be 0, we know the magnitude of the y component of the friction force is m*g-m*g*cos(θ)*sin(π/2-θ)."
+        },
+        {
+          "description": "Magnitude of Force of Friction",
+          "content": "Combining the x and y components of the friction force, we get the magnitude of the friction force is equal to sqrt((m*g*cos(θ)*cos(π/2-θ))^2 + (m*g-m*g*cos(θ)*sin(π/2-θ))^2)."
+        }
+      ]
+    },
+    {
+      "questionSetup": [
+        "There is a 1kg weight on an inclined plane. The plane is at a ",
+        " angle from the ground. The system is in equilibrium (the net force on the weight is 0)."
+      ],
+      "variablesForQuestionSetup": ["theta - max 45"],
+      "question": "What is the minimum coefficient of static friction?",
+      "answerParts": ["coefficient of static friction"],
+      "answerSolutionDescriptions": [
+        "solve minimum static coefficient from wedge angle given equilibrium"
+      ],
+      "goal": "noMovement",
+      "hints": [
+        {
+          "description": "Net Force in Equilibrium",
+          "content": "If the system is in equilibrium, the sum of the x components of all forces must equal 0. In this system, the normal force and force of static friction have non-zero x components."
+        },
+        {
+          "description": "X Component of Normal Force",
+          "content": "The x component of the normal force is equal to m*g*cos(θ)*cos(π/2-θ), where θ is the angle of the incline plane."
+        },
+        {
+          "description": "X Component of Force of Friction",
+          "content": "The x component of the force of static friction is equal to μ*m*g*cos(θ)*cos(π-θ), where θ is the angle of the incline plane."
+        },
+        {
+          "description": "Equation to Solve for Minimum Coefficient of Static Friction",
+          "content": "Since the net force in the x direction must be 0, we can solve the equation 0=m*g*cos(θ)*cos(π/2-θ)+μ*m*g*cos(θ)*cos(π-θ) for μ to find the minimum coefficient of static friction such that the system stays in equilibrium."
+        }
+      ]
+    },
+    {
+      "questionSetup": [
+        "There is a 1kg weight on an inclined plane. The coefficient of static friction is ",
+        ". The system is in equilibrium (the net force on the weight is 0)."
+      ],
+      "variablesForQuestionSetup": ["coefficient of static friction"],
+      "question": "What is the maximum angle of the plane from the ground?",
+      "answerParts": ["wedge angle"],
+      "answerSolutionDescriptions": [
+        "solve maximum wedge angle from coefficient of static friction given equilibrium"
+      ],
+      "goal": "noMovement",
+      "hints": [
+        {
+          "description": "Net Force in Equilibrium",
+          "content": "If the system is in equilibrium, the sum of the x components of all forces must equal 0. In this system, the normal force and force of static friction have non-zero x components."
+        },
+        {
+          "description": "X Component of Normal Force",
+          "content": "The x component of the normal force is equal to m*g*cos(θ)*cos(π/2-θ), where θ is the angle of the incline plane."
+        },
+        {
+          "description": "X Component of Force of Friction",
+          "content": "The x component of the force of static friction is equal to μ*m*g*cos(θ)*cos(π-θ), where θ is the angle of the incline plane."
+        },
+        {
+          "description": "Equation to Solve for Maximum Wedge Angle",
+          "content": "Since the net force in the x direction must be 0, we can solve the equation 0=m*g*cos(θ)*cos(π/2-θ)+μ*m*g*cos(θ)*cos(π-θ) for θ to find the maximum wedge angle such that the system stays in equilibrium."
+        },
+        {
+          "description": "Simplifying Equation to Solve for Maximum Wedge Angle",
+          "content": "Simplifying 0=m*g*cos(θ)*cos(π/2-θ)+μ*m*g*cos(θ)*cos(π-θ), we get cos(π/2-θ)=-μ*cos(π-θ)."
+        },
+        {
+          "description": "Simplifying Equation to Solve for Maximum Wedge Angle",
+          "content": "The cosine subtraction formula states that cos(A-B)=cos(A)*cos(B)+sin(A)sin(B)."
+        },
+        {
+          "description": "Simplifying Equation to Solve for Maximum Wedge Angle",
+          "content": "Applying the cosine subtraction formula to cos(π/2-θ)=-μ*cos(π-θ), we get cos(π/2)*cos(θ)+sin(π/2)*sin(θ)=-μ*(cos(π)cos(θ)+sin(π)sin(θ))."
+        },
+        {
+          "description": "Simplifying Equation to Solve for Maximum Wedge Angle",
+          "content": "Simplifying cos(π/2)*cos(θ)-sin(π/2)*sin(θ)=-μ*(cos(π)cos(θ)-sin(π)sin(θ)), we get -sin(θ)=-μ*(-cos(θ))."
+        },
+        {
+          "description": "Simplifying Equation to Solve for Maximum Wedge Angle",
+          "content": "Simplifying -sin(θ)=-μ*(-cos(θ)), we get tan(θ)=-μ."
+        },
+        {
+          "description": "Simplifying Equation to Solve for Maximum Wedge Angle",
+          "content": "Solving for θ, we get θ = atan(μ)."
+        }
+      ]
+    }
+  ]
+}
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationTutorial.json b/src/client/views/nodes/PhysicsBox/PhysicsSimulationTutorial.json
new file mode 100644
index 000000000..3015deaa4
--- /dev/null
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationTutorial.json
@@ -0,0 +1,600 @@
+{
+  "freeWeight": {
+    "question": "A 1kg weight is at rest on the ground. What are the magnitude and directions of the forces acting on the weight?",
+    "steps": [
+      {
+        "description": "Forces",
+        "content": "There are two forces acting on the weight: the force of gravity and the normal force.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          },
+          {
+            "description": "Normal Force",
+            "magnitude": 9.81,
+            "directionInDegrees": 90,
+            "component": false
+          }
+        ],
+        "showMagnitude": false
+      },
+      {
+        "description": "Force of Gravity",
+        "content": "The force of gravity acts in the negative y direction: 3π/2 rad. It has magnitude equal to m*g. We can approximate g as 9.81.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "Normal Force",
+        "content": "The normal force acts in the positive y direction: π/2 rad. It has magnitude equal to m*g. We can approximate g as 9.81.",
+        "forces": [
+          {
+            "description": "Normal Force",
+            "magnitude": 9.81,
+            "directionInDegrees": 90,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "All Forces",
+        "content": "Combining all of the forces, we get the following free body diagram.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          },
+          {
+            "description": "Normal Force",
+            "magnitude": 9.81,
+            "directionInDegrees": 90,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      }
+    ]
+  },
+  "pendulum": {
+    "question": "A 1kg weight on a 300m rod of negligible mass is released from an angle 30 degrees offset from equilibrium. What are the magnitude and directions of the forces acting on the weight immediately after release? (Ignore air resistance)",
+    "steps": [
+      {
+        "description": "Forces",
+        "content": "There are two force acting on the weight: the force of gravity and the force of tension.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          },
+          {
+            "description": "Tension",
+            "magnitude": 8.5,
+            "directionInDegrees": 60,
+            "component": false
+          }
+        ],
+        "showMagnitude": false
+      },
+      {
+        "description": "Force of Gravity",
+        "content": "The force of gravity acts in the negative y direction: 3π/2 rad. It has magnitude equal to m*g. We can approximate g as 9.81.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "Tension",
+        "content": "The force of tension acts along the direction of the rod. The rod is 30 degrees offset from equilibrium, so the direction along the rod is 90-30=60 degrees. The tension force has two components—the component creating the centripetal force and the component canceling out the parallel component of gravity. The weight has just been released, so it has velocity 0, meaning the centripetal force is 0. Thus, the tension force only acts to cancel out the parallel component of gravity. Thus, the magnitude of tension is m*g*sin(60°)",
+        "forces": [
+          {
+            "description": "Tension",
+            "magnitude": 8.5,
+            "directionInDegrees": 60,
+            "component": false
+          },
+          {
+            "description": "Gravity - Parallel Component",
+            "magnitude": 8.5,
+            "directionInDegrees": 240,
+            "component": true
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "All Forces",
+        "content": "Combining all of the forces, we get the following free body diagram.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          },
+          {
+            "description": "Tension",
+            "magnitude": 8.5,
+            "directionInDegrees": 60,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      }
+    ]
+  },
+  "inclinePlane": {
+    "question": "There is a 1kg weight on an inclined plane. The plane is at an angle θ from the ground, and has a coefficient of static friction μ. The system is in equilibrium (the net force on the weight is 0). What are the magnitudes and directions of the forces acting on the weight?",
+    "steps": [
+      {
+        "description": "Forces",
+        "content": "There are three forces acting on the weight: the force of gravity, the normal force, and the force of static friction.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          },
+          {
+            "description": "Normal Force",
+            "magnitude": 8.817,
+            "directionInDegrees": 64,
+            "component": false
+          },
+          {
+            "description": "Friction Force",
+            "magnitude": 4.3,
+            "directionInDegrees": 154,
+            "component": false
+          }
+        ],
+        "showMagnitude": false
+      },
+      {
+        "description": "Force of Gravity",
+        "content": "The force of gravity acts in the negative y direction: 3π/2 rad. It has magnitude equal to m*g. We can approximate g as 9.81.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "Normal Force",
+        "content": "The normal force acts in the direction perpendicular to the incline plane: π/2-θ rad, where θ is the angle of the incline plane. The magnitude of the normal force is equal to m*g*cos(θ).",
+        "forces": [
+          {
+            "description": "Normal Force",
+            "magnitude": 8.817,
+            "directionInDegrees": 64,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "Force of Static Friction",
+        "content": "The force of static friction acts in the direction along the incline plane: π-θ rad, where θ is the angle of the incline plane. We can use the knowledge that the system is in equilibrium to solve for the magnitude of the force of static friction.",
+        "forces": [
+          {
+            "description": "Friction Force",
+            "magnitude": 4.3,
+            "directionInDegrees": 154,
+            "component": false
+          }
+        ],
+        "showMagnitude": false
+      },
+      {
+        "description": "Net X Force in Equilibrium",
+        "content": "For the system to be in equilibrium, the sum of the x components of all forces must equal 0. The x component of the normal force is equal to m*g*cos(θ)*cos(π/2-θ), where θ is the angle of the incline plane. The x component of gravity is equal to 0. Since the net force in the x direction must be 0, we know the magnitude of the x component of the friction force is m*g*cos(θ)*cos(π/2-θ).",
+        "forces": [
+          {
+            "description": "Normal Force - X Component",
+            "magnitude": 3.87,
+            "directionInDegrees": 0,
+            "component": true
+          },
+          {
+            "description": "Friction Force - X Component",
+            "magnitude": 3.87,
+            "directionInDegrees": 180,
+            "component": true
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "Net Y Force Normal Force",
+        "content": "For the system to be in equilibrium, the sum of the y components of all forces must equal 0. The y component of the normal force is equal to m*g*cos(θ)*sin(π/2-θ), where θ is the angle of the incline plane. The y component of gravity is equal to m*g. Since the net force in the x direction must be 0, we know the magnitude of the y component of the friction force is m*g-m*g*cos(θ)*sin(π/2-θ).",
+        "forces": [
+          {
+            "description": "Normal Force - Y Component ",
+            "magnitude": 7.92,
+            "directionInDegrees": 90,
+            "component": true
+          },
+          {
+            "description": "Gravity - Y Component ",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": true
+          },
+          {
+            "description": "Friction Force - Y Component ",
+            "magnitude": 1.89,
+            "directionInDegrees": 90,
+            "component": true
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "Magnitude of Force of Friction",
+        "content": "Combining the x and y components of the friction force, we get the magnitude of the friction force is equal to sqrt((m*g*cos(θ)*cos(π/2-θ))^2 + (m*g*cos(θ)*sin(π/2-θ)-m*g)^2).",
+        "forces": [
+          {
+            "description": "Friction Force",
+            "magnitude": 4.3,
+            "directionInDegrees": 154,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "All Forces",
+        "content": "Combining all of the forces, we get the following free body diagram.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          },
+          {
+            "description": "Normal Force",
+            "magnitude": 8.817,
+            "directionInDegrees": 64,
+            "component": false
+          },
+          {
+            "description": "Friction Force",
+            "magnitude": 4.3,
+            "directionInDegrees": 154,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      }
+    ]
+  },
+  "spring": {
+    "question": "A 1kg weight is on a spring of negligible mass with rest length 200m and spring constant 0.5. What is the equilibrium spring length?",
+    "steps": [
+      {
+        "description": "Forces",
+        "content": "We can start by solving for the forces acting on the weight at any given point in time. There are two forces potentially acting on the weight: the force of gravity and the spring force. In equilibrium, these forces will be perfectly balanced.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          },
+          {
+            "description": "Spring Force",
+            "magnitude": 9.81,
+            "directionInDegrees": 90,
+            "component": false
+          }
+        ],
+        "showMagnitude": false
+      },
+      {
+        "description": "Force of Gravity",
+        "content": "The force of gravity acts in the negative y direction: 3π/2 rad. It has magnitude equal to m*g. We can approximate g as 9.81.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "Spring Force",
+        "content": "The spring force acts in the negative y direction (3π/2 rad) if the spring is compressed. The spring force acts in the positive y direction (π/2 rad) if the spring is extended. Because the forces are perfectly balanced and gravity acts in the negative y direction, the spring force must act in the positive y direction and have the same magnitude as the force og gravity, m*g. We can approximate g as 9.81.",
+        "forces": [
+          {
+            "description": "Spring Force",
+            "magnitude": 9.81,
+            "directionInDegrees": 90,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "Spring Force",
+        "content": "We can use the spring force equation, Fs=kd to solve for the displacement such that Fs=mg. Setting them equal, we get mg=kd. Plugging in for the known values of m,g, and k, we get 1*9.81=0.5d. Solving for d, we get d=19.62 as the equilibrium starting displacement",
+        "forces": [
+          {
+            "description": "Spring Force",
+            "magnitude": 9.81,
+            "directionInDegrees": 90,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      }
+    ]
+  },
+  "circular": {
+    "question": "A 1kg weight is attached to a 100m rod of negligible mass. The weight is undergoing uniform circular motion with tangential velocity 40 m/s. What are the magnitude and directions of the forces acting on the weight? (Ignore air resistance)",
+    "steps": [
+      {
+        "description": "Forces",
+        "content": "There is one force acting on the weight: the centripetal force.",
+        "forces": [
+          {
+            "description": "Centripetal Force",
+            "magnitude": 16,
+            "directionInDegrees": 90,
+            "component": false
+          }
+        ],
+        "showMagnitude": false
+      },
+      {
+        "description": "Centripetal Force",
+        "content": "The centripetal force is always directed toward the center of the circle. The formula for solving for the magnitude of centripetal force for an object undergoing uniform circular motion is Fc=mv^2 / r. Plugging in for known values, we get Fc=1*(40^2)/100. Solving for this, we get Fc=16",
+        "forces": [
+          {
+            "description": "Centripetal Force",
+            "magnitude": 16,
+            "directionInDegrees": 90,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      }
+    ]
+  },
+  "pulley": {
+    "question": "A 1kg red weight is attached to a simple pulley with a rope of negligible mass. A 1.5kg blue weight is attached to the other end of the simple pulley. What are the forces acting on the red weight?",
+    "steps": [
+      {
+        "description": "Forces",
+        "content": "There are two force acting on the red weight: the force of gravity and the force of tension.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          },
+          {
+            "description": "Tension",
+            "magnitude": 11.77,
+            "directionInDegrees": 90,
+            "component": false
+          }
+        ],
+        "showMagnitude": false
+      },
+      {
+        "description": "Gravity",
+        "content": "The force of gravity acts in the negative y direction: 3π/2 rad. It has magnitude equal to m*g. We can approximate g as 9.81.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "Tension",
+        "content": "The force of tension acts in the positive y direction: π/2 rad. We know that the acceleration in a simple pulley system is (mass  2 - mass 1) * acceleration due to gravity / (mass 1 + mass 2) = (1.5-1) * 9.81 / (1.5+1) = 1.962 m/s^2. Because the acceleration is caused by the force of gravity and force of tension, we can solve for the force of tension acting on the weight as mass 1 * (a + acceleration due to gravity) = 1 * (1.962+9.81) = 11.77.",
+        "forces": [
+          {
+            "description": "Tension",
+            "magnitude": 11.77,
+            "directionInDegrees": 90,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "All Forces",
+        "content": "Combining all of the forces, we get the following free body diagram.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          },
+          {
+            "description": "Tension",
+            "magnitude": 11.77,
+            "directionInDegrees": 90,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      }
+    ]
+  },
+  "suspension": {
+    "question": "A 1kg weight is attached to two rods hanging from 45° angles from the ceiling. The system is in equilibrium, i.e. the weight does not move. What are the magnitudes and directions of the forces acting on the weight?",
+    "steps": [
+      {
+        "description": "Forces",
+        "content": "There are three force acting on the red weight: the force of gravity, the force of tension from the left rod, and the force of tension from the right rod.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          },
+          {
+            "description": "Left Tension",
+            "magnitude": 6.94,
+            "directionInDegrees": 135,
+            "component": false
+          },
+          {
+            "description": "Right Tension",
+            "magnitude": 6.94,
+            "directionInDegrees": 45,
+            "component": false
+          }
+        ],
+        "showMagnitude": false
+      },
+      {
+      "description": "Force X Components",
+        "content": "There are two forces with x components to consider: the tension from the left rod and the tension from the right rod. These must cancel each other out so that the net x force is 0.",
+        "forces": [
+          {
+            "description": "Left Tension X Component",
+            "magnitude": 4.907,
+            "directionInDegrees": 180,
+            "component": true
+          },
+          {
+            "description": "Right Tension X Component",
+            "magnitude": 4.907,
+            "directionInDegrees": 0,
+            "component": true
+          }
+        ],
+        "showMagnitude": false
+      }, {
+      "description": "Force Y Components",
+      "content": "There are three forces with y components to consider: the tension from the left rod, the tension from the right rod, and the force of gravity.",
+      "forces": [
+        {
+          "description": "Left Tension Y Component",
+          "magnitude": 4.907,
+          "directionInDegrees": 90,
+          "component": true
+        },
+        {
+          "description": "Right Tension Y Component",
+          "magnitude": 4.907,
+          "directionInDegrees": 90,
+          "component": true
+        },
+        {
+          "description": "Gravity Y Component",
+          "magnitude": 9.81,
+          "directionInDegrees": 270,
+          "component": true
+        }
+      ],
+      "showMagnitude": false
+      }, {
+      "description": "Force Y Components",
+        "content": "The y components of forces must cancel each other out so that the net y force is 0. Thus, gravity = left tension y component + right tension y component. Because the x components of tension are the same and the angles of each rod are the same, the y components must be the same. Thus, the y component for each force of tension must be 9.81/2.",
+        "forces": [
+          {
+            "description": "Left Tension Y Component",
+            "magnitude": 4.907,
+            "directionInDegrees": 90,
+            "component": true
+          },
+          {
+            "description": "Right Tension Y Component",
+            "magnitude": 4.907,
+            "directionInDegrees": 90,
+            "component": true
+          },
+          {
+            "description": "Gravity Y Component",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": true
+          }
+        ],
+        "showMagnitude": true
+      }, {
+      "description": "Tension",
+        "content": "Now that we know the y component of tension for each rod is 4.907, we can solve for the full force of tension as 4.907 = T * sin(45°) -> T = 6.94.",
+        "forces": [
+          {
+            "description": "Left Tension",
+            "magnitude": 6.94,
+            "directionInDegrees": 135,
+            "component": false
+          },
+          {
+            "description": "Right Tension",
+            "magnitude": 6.94,
+            "directionInDegrees": 45,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      },
+      {
+        "description": "All Forces",
+        "content": "Combining all of the forces, we get the following free body diagram.",
+        "forces": [
+          {
+            "description": "Gravity",
+            "magnitude": 9.81,
+            "directionInDegrees": 270,
+            "component": false
+          },
+          {
+            "description": "Left Tension",
+            "magnitude": 6.94,
+            "directionInDegrees": 135,
+            "component": false
+          },
+          {
+            "description": "Right Tension",
+            "magnitude": 6.94,
+            "directionInDegrees": 45,
+            "component": false
+          }
+        ],
+        "showMagnitude": true
+      }
+    ]
+  }
+}
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx
new file mode 100644
index 000000000..8cc1d0fbf
--- /dev/null
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx
@@ -0,0 +1,34 @@
+import React = require('react');
+
+export interface Force {
+  magnitude: number;
+  directionInDegrees: number;
+}
+export interface IWallProps {
+  length: number;
+  xPos: number;
+  yPos: number;
+  angleInDegrees: number;
+}
+
+export default class Wall extends React.Component<IWallProps> {
+
+  constructor(props: any) {
+    super(props)
+  }
+
+  wallStyle = {
+    width: this.props.angleInDegrees == 0 ? this.props.length + "%" : "5px",
+    height: this.props.angleInDegrees == 0 ? "5px" : this.props.length + "%",
+    position: "absolute" as "absolute",
+    left: this.props.xPos + "%",
+    top: this.props.yPos + "%",
+    backgroundColor: "#6c7b8b",
+    margin: 0,
+    padding: 0,
+  };
+
+  render () {
+    return (<div style={this.wallStyle}></div>);
+  }
+};
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
new file mode 100644
index 000000000..2165c8ba9
--- /dev/null
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
@@ -0,0 +1,990 @@
+import { computed, IReactionDisposer, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import './PhysicsSimulationBox.scss';
+import React = require('react');
+
+interface IWallProps {
+    length: number;
+    xPos: number;
+    yPos: number;
+    angleInDegrees: number;
+}
+interface IForce {
+    description: string;
+    magnitude: number;
+    directionInDegrees: number;
+}
+export interface IWeightProps {
+    pause: () => void;
+    panelWidth: () => number;
+    panelHeight: () => number;
+    resetRequest: () => number;
+    circularMotionRadius: number;
+    coefficientOfKineticFriction: number;
+    color: string;
+    componentForces: () => IForce[];
+    setComponentForces: (x: IForce[]) => {};
+    displayXVelocity: number;
+    displayYVelocity: number;
+    elasticCollisions: boolean;
+    gravity: number;
+    mass: number;
+    simulationMode: string;
+    noMovement: boolean;
+    paused: boolean;
+    pendulumAngle: number;
+    pendulumLength: number;
+    radius: number;
+    showAcceleration: boolean;
+    showComponentForces: boolean;
+    showForceMagnitudes: boolean;
+    showForces: boolean;
+    showVelocity: boolean;
+    simulationSpeed: number;
+    simulationType: string;
+    springConstant: number;
+    springRestLength: number;
+    springStartLength: number;
+    startForces: () => IForce[];
+    startPendulumAngle: number;
+    startPendulumLength: number;
+    startPosX: number;
+    startPosY: number;
+    startVelX: number;
+    startVelY: number;
+    timestepSize: number;
+    updateMassPosX: number;
+    updateMassPosY: number;
+    forcesUpdated: () => IForce[];
+    setForcesUpdated: (x: IForce[]) => {};
+    setPosition: (x: number | undefined, y: number | undefined) => void;
+    setVelocity: (x: number | undefined, y: number | undefined) => void;
+    setAcceleration: (x: number, y: number) => void;
+    setPendulumAngle: (ang: number | undefined, length: number | undefined) => void;
+    setSpringLength: (length: number) => void;
+    wallPositions: IWallProps[];
+    wedgeHeight: number;
+    wedgeWidth: number;
+    xMax: number;
+    xMin: number;
+    yMax: number;
+    yMin: number;
+}
+
+interface IState {
+    angleLabel: number;
+    clickPositionX: number;
+    clickPositionY: number;
+    coordinates: string;
+    dragging: boolean;
+    kineticFriction: boolean;
+    maxPosYConservation: number;
+    timer: number;
+    updatedStartPosX: any;
+    updatedStartPosY: any;
+    xPosition: number;
+    xVelocity: number;
+    yPosition: number;
+    yVelocity: number;
+    xAccel: number;
+    yAccel: number;
+}
+@observer
+export default class Weight extends React.Component<IWeightProps, IState> {
+    constructor(props: any) {
+        super(props);
+        this.state = {
+            angleLabel: 0,
+            clickPositionX: 0,
+            clickPositionY: 0,
+            coordinates: '',
+            dragging: false,
+            kineticFriction: false,
+            maxPosYConservation: 0,
+            timer: 0,
+            updatedStartPosX: this.props.startPosX,
+            updatedStartPosY: this.props.startPosY,
+            xPosition: this.props.startPosX,
+            xVelocity: this.props.startVelX,
+            yPosition: this.props.startPosY,
+            yVelocity: this.props.startVelY,
+            xAccel: 0,
+            yAccel: 0,
+        };
+    }
+
+    _timer: NodeJS.Timeout | undefined;
+    _resetDisposer: IReactionDisposer | undefined;
+
+    componentWillUnmount() {
+        this._timer && clearTimeout(this._timer);
+        this._resetDisposer?.();
+    }
+    componentWillUpdate(nextProps: Readonly<IWeightProps>, nextState: Readonly<IState>, nextContext: any): void {
+        if (nextProps.paused) {
+            this._timer && clearTimeout(this._timer);
+            this._timer = undefined;
+        } else if (this.props.paused) {
+            this._timer && clearTimeout(this._timer);
+            this._timer = setInterval(() => this.setState({ timer: this.state.timer + 1 }), 50);
+        }
+    }
+
+    // Constants
+    @computed get draggable() {
+        return !['Inclined Plane', 'Pendulum'].includes(this.props.simulationType) && this.props.simulationMode === 'Freeform';
+    }
+    @computed get panelHeight() {
+        return Math.max(800, this.props.panelHeight()) + 'px';
+    }
+    @computed get panelWidth() {
+        return Math.max(1000, this.props.panelWidth()) + 'px';
+    }
+
+    @computed get walls() {
+        return ['One Weight', 'Inclined Plane'].includes(this.props.simulationType) ? this.props.wallPositions : [];
+    }
+    epsilon = 0.0001;
+    labelBackgroundColor = `rgba(255,255,255,0.5)`;
+
+    // Variables
+    weightStyle = {
+        alignItems: 'center',
+        backgroundColor: this.props.color,
+        borderColor: 'black',
+        borderRadius: 50 + '%',
+        borderStyle: 'solid',
+        display: 'flex',
+        height: 2 * this.props.radius + 'px',
+        justifyContent: 'center',
+        left: this.props.startPosX + 'px',
+        position: 'absolute' as 'absolute',
+        top: this.props.startPosY + 'px',
+        touchAction: 'none',
+        width: 2 * this.props.radius + 'px',
+        zIndex: 5,
+    };
+
+    // Helper function to go between display and real values
+    getDisplayYPos = (yPos: number) => this.props.yMax - yPos - 2 * this.props.radius + 5;
+    gravityForce = (): IForce => ({
+        description: 'Gravity',
+        magnitude: this.props.mass * this.props.gravity,
+        directionInDegrees: 270,
+    });
+    // Update display values when simulation updates
+    setDisplayValues = (xPos: number = this.state.xPosition, yPos: number = this.state.yPosition, xVel: number = this.state.xVelocity, yVel: number = this.state.yVelocity) => {
+        this.props.setPosition(xPos, this.getDisplayYPos(yPos));
+        this.props.setVelocity(xVel, yVel);
+        const xAccel = Math.round(this.getNewAccelerationX(this.props.forcesUpdated()) * 100) / 100;
+        const yAccel = (-1 * Math.round(this.getNewAccelerationY(this.props.forcesUpdated()) * 100)) / 100;
+        this.props.setAcceleration(xAccel, yAccel);
+        this.setState({ xAccel, yAccel });
+    };
+    componentDidMount() {
+        this._resetDisposer = reaction(() => this.props.resetRequest(), this.resetEverything);
+    }
+    componentDidUpdate(prevProps: Readonly<IWeightProps>, prevState: Readonly<IState>, snapshot?: any): void {
+        if (prevProps.simulationType != this.props.simulationType) {
+            this.setState({ xVelocity: this.props.startVelX, yVelocity: this.props.startVelY });
+            this.setDisplayValues();
+        }
+
+        // Change pendulum angle from input field
+        if (prevProps.startPendulumAngle != this.props.startPendulumAngle || prevProps.startPendulumLength !== this.props.startPendulumLength) {
+            const length = this.props.startPendulumLength;
+            const x = length * Math.cos(((90 - this.props.startPendulumAngle) * Math.PI) / 180);
+            const y = length * Math.sin(((90 - this.props.startPendulumAngle) * Math.PI) / 180);
+            const xPosition = this.props.xMax / 2 - x - this.props.radius;
+            const yPosition = y - this.props.radius - 5;
+            this.setState({ xPosition, yPosition, updatedStartPosX: xPosition, updatedStartPosY: yPosition });
+            this.props.setPendulumAngle(this.props.startPendulumAngle, this.props.startPendulumLength);
+        }
+
+        // When display values updated by user, update real value
+        if (prevProps.updateMassPosX !== this.props.updateMassPosX) {
+            const x = Math.min(Math.max(0, this.props.updateMassPosX), this.props.xMax - 2 * this.props.radius);
+            this.setState({ updatedStartPosX: x, xPosition: x });
+            this.props.setPosition(x, undefined);
+        }
+        if (prevProps.updateMassPosY != this.props.updateMassPosY) {
+            const y = Math.min(Math.max(0, this.props.updateMassPosY), this.props.yMax - 2 * this.props.radius);
+            const coordinatePosition = this.getDisplayYPos(y);
+            this.setState({ yPosition: coordinatePosition, updatedStartPosY: coordinatePosition });
+            this.props.setPosition(undefined, this.getDisplayYPos(y));
+
+            if (this.props.displayXVelocity != this.state.xVelocity) {
+                this.setState({ xVelocity: this.props.displayXVelocity });
+                this.props.setVelocity(this.props.displayXVelocity, undefined);
+            }
+
+            if (this.props.displayYVelocity != -this.state.yVelocity) {
+                this.setState({ yVelocity: -this.props.displayYVelocity });
+                this.props.setVelocity(undefined, this.props.displayYVelocity);
+            }
+        }
+
+        // Make sure weight doesn't go above max height
+        if ((prevState.updatedStartPosY != this.state.updatedStartPosY || prevProps.startVelY != this.props.startVelY) && !isNaN(this.state.updatedStartPosY) && !isNaN(this.props.startVelY)) {
+            if (this.props.simulationType == 'One Weight') {
+                let maxYPos = this.state.updatedStartPosY;
+                if (this.props.startVelY != 0) {
+                    maxYPos -= (this.props.startVelY * this.props.startVelY) / (2 * this.props.gravity);
+                }
+                if (maxYPos < 0) maxYPos = 0;
+
+                this.setState({ maxPosYConservation: maxYPos });
+            }
+        }
+
+        // Check for collisions and update
+        if (!this.props.paused && !this.props.noMovement && prevState.timer != this.state.timer) {
+            let collisions = false;
+            if (this.props.simulationType == 'One Weight' || this.props.simulationType == 'Inclined Plane') {
+                const collisionsWithGround = this.checkForCollisionsWithGround();
+                const collisionsWithWalls = this.checkForCollisionsWithWall();
+                collisions = collisionsWithGround || collisionsWithWalls;
+            }
+            if (this.props.simulationType == 'Pulley') {
+                if (this.state.yPosition <= this.props.yMin + 100 || this.state.yPosition >= this.props.yMax - 100) {
+                    collisions = true;
+                }
+            }
+            if (!collisions) this.update();
+
+            this.setDisplayValues();
+        }
+
+        // Convert from static to kinetic friction if/when weight slips on inclined plane
+        if (prevState.xVelocity != this.state.xVelocity) {
+            if (this.props.simulationType == 'Inclined Plane' && Math.abs(this.state.xVelocity) > 0.1 && this.props.simulationMode != 'Review' && !this.state.kineticFriction) {
+                this.setState({ kineticFriction: true });
+                const normalForce: IForce = {
+                    description: 'Normal Force',
+                    magnitude: this.props.mass * this.props.gravity * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)),
+                    directionInDegrees: 180 - 90 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI,
+                };
+                const frictionForce: IForce = {
+                    description: 'Kinetic Friction Force',
+                    magnitude: this.props.mass * this.props.coefficientOfKineticFriction * this.props.gravity * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)),
+                    directionInDegrees: 180 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI,
+                };
+                // reduce magnitude of friction force if necessary such that block cannot slide up plane
+                // prettier-ignore
+                const yForce = - this.props.gravity +
+                        normalForce.magnitude * Math.sin((normalForce.directionInDegrees * Math.PI) / 180) +
+                        frictionForce.magnitude * Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
+                if (yForce > 0) {
+                    frictionForce.magnitude = (-normalForce.magnitude * Math.sin((normalForce.directionInDegrees * Math.PI) / 180) + this.props.gravity) / Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
+                }
+
+                const normalForceComponent: IForce = {
+                    description: 'Normal Force',
+                    magnitude: this.props.mass * this.props.gravity * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)),
+                    directionInDegrees: 180 - 90 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI,
+                };
+                const gravityParallel: IForce = {
+                    description: 'Gravity Parallel Component',
+                    magnitude: this.props.mass * this.props.gravity * Math.sin(Math.PI / 2 - Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)),
+                    directionInDegrees: 180 - 90 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI + 180,
+                };
+                const gravityPerpendicular: IForce = {
+                    description: 'Gravity Perpendicular Component',
+                    magnitude: this.props.mass * this.props.gravity * Math.cos(Math.PI / 2 - Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)),
+                    directionInDegrees: 360 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI,
+                };
+                const kineticFriction = this.props.coefficientOfKineticFriction != 0 ? [frictionForce] : [];
+                this.props.setForcesUpdated([this.gravityForce(), normalForce, ...kineticFriction]);
+                this.props.setComponentForces([normalForceComponent, gravityParallel, gravityPerpendicular, ...kineticFriction]);
+            }
+        }
+
+        // Update x position when start pos x changes
+        if (prevProps.startPosX != this.props.startPosX) {
+            if (this.props.paused && !isNaN(this.props.startPosX)) {
+                this.setState({ xPosition: this.props.startPosX, updatedStartPosX: this.props.startPosX });
+                this.props.setPosition(this.props.startPosX, undefined);
+            }
+        }
+
+        // Update y position when start pos y changes TODO debug
+        if (prevProps.startPosY != this.props.startPosY) {
+            if (this.props.paused && !isNaN(this.props.startPosY)) {
+                this.setState({ yPosition: this.props.startPosY, updatedStartPosY: this.props.startPosY ?? 0 });
+                this.props.setPosition(undefined, this.getDisplayYPos(this.props.startPosY));
+            }
+        }
+
+        // Update wedge coordinates
+        if (!this.state.coordinates || this.props.yMax !== prevProps.yMax || prevProps.wedgeWidth != this.props.wedgeWidth || prevProps.wedgeHeight != this.props.wedgeHeight) {
+            const left = this.props.xMax * 0.25;
+            const coordinatePair1 = Math.round(left) + ',' + this.props.yMax + ' ';
+            const coordinatePair2 = Math.round(left + this.props.wedgeWidth) + ',' + this.props.yMax + ' ';
+            const coordinatePair3 = Math.round(left) + ',' + (this.props.yMax - this.props.wedgeHeight);
+            this.setState({ coordinates: coordinatePair1 + coordinatePair2 + coordinatePair3 });
+        }
+
+        if (this.state.xPosition != prevState.xPosition || this.state.yPosition != prevState.yPosition) {
+            this.weightStyle = {
+                alignItems: 'center',
+                backgroundColor: this.props.color,
+                borderColor: 'black',
+                borderRadius: 50 + '%',
+                borderStyle: 'solid',
+                display: 'flex',
+                height: 2 * this.props.radius + 'px',
+                justifyContent: 'center',
+                left: this.state.xPosition + 'px',
+                position: 'absolute' as 'absolute',
+                top: this.state.yPosition + 'px',
+                touchAction: 'none',
+                width: 2 * this.props.radius + 'px',
+                zIndex: 5,
+            };
+        }
+    }
+
+    // Reset simulation on reset button click
+    resetEverything = () => {
+        this.setState({
+            kineticFriction: false,
+            xPosition: this.state.updatedStartPosX,
+            yPosition: this.state.updatedStartPosY,
+            xVelocity: this.props.startVelX,
+            yVelocity: this.props.startVelY,
+            angleLabel: Math.round(this.props.pendulumAngle * 100) / 100,
+        });
+        this.props.setPendulumAngle(this.props.startPendulumAngle, undefined);
+        this.props.setForcesUpdated(this.props.startForces());
+        this.props.setPosition(this.state.updatedStartPosX, this.state.updatedStartPosY);
+        this.props.setVelocity(this.props.startVelX, this.props.startVelY);
+        this.props.setAcceleration(0, 0);
+        setTimeout(() => this.setState({ timer: this.state.timer + 1 }));
+    };
+
+    // Compute x acceleration from forces, F=ma
+    getNewAccelerationX = (forceList: IForce[]) => {
+        // prettier-ignore
+        return forceList.reduce((newXacc, force) => 
+              newXacc + (force.magnitude * Math.cos((force.directionInDegrees * Math.PI) / 180)) / this.props.mass, 0);
+    };
+
+    // Compute y acceleration from forces, F=ma
+    getNewAccelerationY = (forceList: IForce[]) => {
+        // prettier-ignore
+        return forceList.reduce((newYacc, force) => 
+              newYacc + (-1 * (force.magnitude * Math.sin((force.directionInDegrees * Math.PI) / 180))) / this.props.mass, 0);
+    };
+
+    // Compute uniform circular motion forces given x, y positions
+    getNewCircularMotionForces = (xPos: number, yPos: number): IForce[] => {
+        const deltaX = (this.props.xMin + this.props.xMax) / 2 - (xPos + this.props.radius);
+        const deltaY = yPos + this.props.radius - (this.props.yMin + this.props.yMax) / 2;
+        return [
+            {
+                description: 'Centripetal Force',
+                magnitude: (this.props.startVelX ** 2 * this.props.mass) / this.props.circularMotionRadius,
+                directionInDegrees: (Math.atan2(deltaY, deltaX) * 180) / Math.PI,
+            },
+        ];
+    };
+
+    // Compute spring forces given y position
+    getNewSpringForces = (yPos: number): IForce[] => {
+        const yPosPlus = yPos - this.props.springRestLength > 0;
+        const yPosMinus = yPos - this.props.springRestLength < 0;
+        return [
+            this.gravityForce(),
+            {
+                description: 'Spring Force',
+                magnitude: this.props.springConstant * (yPos - this.props.springRestLength) * (yPosPlus ? 1 : yPosMinus ? -1 : 0),
+                directionInDegrees: yPosPlus ? 90 : 270,
+            },
+        ];
+    };
+
+    // Compute pendulum forces given position, velocity
+    getNewPendulumForces = (xPos: number, yPos: number, xVel: number, yVel: number): IForce[] => {
+        const x = this.props.xMax / 2 - xPos - this.props.radius;
+        const y = yPos + this.props.radius + 5;
+        const angle = (ang => (ang < 0 ? ang + 180 : ang))((Math.atan(y / x) * 180) / Math.PI);
+
+        let oppositeAngle = 90 - angle;
+        if (oppositeAngle < 0) {
+            oppositeAngle = 90 - (180 - angle);
+        }
+
+        const pendulumLength = Math.sqrt(x * x + y * y);
+        this.props.setPendulumAngle(oppositeAngle, undefined);
+
+        const mag = this.props.mass * this.props.gravity * Math.cos((oppositeAngle * Math.PI) / 180) + (this.props.mass * (xVel * xVel + yVel * yVel)) / pendulumLength;
+
+        return [
+            this.gravityForce(),
+            {
+                description: 'Tension',
+                magnitude: mag,
+                directionInDegrees: angle,
+            },
+        ];
+    };
+
+    // Check for collisions in x direction
+    checkForCollisionsWithWall = () => {
+        let collision = false;
+        if (this.state.xVelocity !== 0) {
+            this.walls
+                .filter(wall => wall.angleInDegrees === 90)
+                .forEach(wall => {
+                    const wallX = (wall.xPos / 100) * this.props.panelWidth();
+                    const minX = this.state.xPosition < wallX && wall.xPos < 0.35;
+                    const maxX = this.state.xPosition + 2 * this.props.radius >= wallX && wall.xPos > 0.35;
+                    if (minX || maxX) {
+                        this.setState({
+                            xPosition: minX ? wallX + 0.01 : wallX - 2 * this.props.radius - 0.01,
+                            xVelocity: this.props.elasticCollisions ? -this.state.xVelocity : 0,
+                        });
+                        collision = true;
+                    }
+                });
+        }
+        return collision;
+    };
+
+    // Check for collisions in y direction
+    checkForCollisionsWithGround = () => {
+        let collision = false;
+        const minY = this.state.yPosition;
+        const maxY = this.state.yPosition + 2 * this.props.radius;
+        if (this.state.yVelocity > 0) {
+            this.walls.forEach(wall => {
+                if (wall.angleInDegrees == 0 && wall.yPos > 0.4) {
+                    const groundY = (wall.yPos / 100) * this.props.panelHeight();
+                    const gravity = this.gravityForce();
+                    if (maxY > groundY) {
+                        this.setState({ yPosition: groundY - 2 * this.props.radius - 0.01 });
+                        if (this.props.elasticCollisions) {
+                            this.setState({ yVelocity: -this.state.yVelocity });
+                        } else {
+                            this.setState({ yVelocity: 0 });
+                            const normalForce: IForce = {
+                                description: 'Normal force',
+                                magnitude: gravity.magnitude,
+                                directionInDegrees: -gravity.directionInDegrees,
+                            };
+                            this.props.setForcesUpdated([gravity, normalForce]);
+                            if (this.props.simulationType === 'Inclined Plane') {
+                                this.props.setComponentForces([gravity, normalForce]);
+                            }
+                        }
+                        collision = true;
+                    }
+                }
+            });
+        }
+        if (this.state.yVelocity < 0) {
+            this.walls.forEach(wall => {
+                if (wall.angleInDegrees == 0 && wall.yPos < 0.4) {
+                    const groundY = (wall.yPos / 100) * this.props.panelHeight();
+                    if (minY < groundY) {
+                        this.setState({
+                            yPosition: groundY + 0.01,
+                            yVelocity: this.props.elasticCollisions ? -this.state.yVelocity : 0,
+                        });
+                        collision = true;
+                    }
+                }
+            });
+        }
+        return collision;
+    };
+
+    // Called at each RK4 step
+    evaluate = (currentXPos: number, currentYPos: number, currentXVel: number, currentYVel: number, currdeltaXPos: number, currdeltaYPos: number, currdeltaXVel: number, currdeltaYVel: number, dt: number) => {
+        const xPos = currentXPos + currdeltaXPos * dt;
+        const yPos = currentYPos + currdeltaYPos * dt;
+        const xVel = currentXVel + currdeltaXVel * dt;
+        const yVel = currentYVel + currdeltaYVel * dt;
+        const forces = this.getForces(xPos, yPos, xVel, yVel);
+        return {
+            xPos,
+            yPos,
+            xVel,
+            yVel,
+            deltaXPos: xVel,
+            deltaYPos: yVel,
+            deltaXVel: this.getNewAccelerationX(forces),
+            deltaYVel: this.getNewAccelerationY(forces),
+        };
+    };
+
+    getForces = (xPos: number, yPos: number, xVel: number, yVel: number) => {
+        // prettier-ignore
+        switch (this.props.simulationType) {
+            case 'Pendulum':        return this.getNewPendulumForces(xPos, yPos, xVel, yVel);
+            case 'Spring'  :        return this.getNewSpringForces(yPos);
+            case 'Circular Motion': return this.getNewCircularMotionForces(xPos, yPos);
+            default:                return this.props.forcesUpdated();
+        }
+    };
+
+    // Update position, velocity using RK4 method
+    update = () => {
+        const startXVel = this.state.xVelocity;
+        const startYVel = this.state.yVelocity;
+        let xPos = this.state.xPosition;
+        let yPos = this.state.yPosition;
+        let xVel = this.state.xVelocity;
+        let yVel = this.state.yVelocity;
+        const forces = this.getForces(xPos, yPos, xVel, yVel);
+        const xAcc = this.getNewAccelerationX(forces);
+        const yAcc = this.getNewAccelerationY(forces);
+        const coeff = (this.props.timestepSize * 1.0) / 6.0;
+        for (let i = 0; i < this.props.simulationSpeed; i++) {
+            const k1 = this.evaluate(xPos, yPos, xVel, yVel, xVel, yVel, xAcc, yAcc, 0);
+            const k2 = this.evaluate(xPos, yPos, xVel, yVel, k1.deltaXPos, k1.deltaYPos, k1.deltaXVel, k1.deltaYVel, this.props.timestepSize * 0.5);
+            const k3 = this.evaluate(xPos, yPos, xVel, yVel, k2.deltaXPos, k2.deltaYPos, k2.deltaXVel, k2.deltaYVel, this.props.timestepSize * 0.5);
+            const k4 = this.evaluate(xPos, yPos, xVel, yVel, k3.deltaXPos, k3.deltaYPos, k3.deltaXVel, k3.deltaYVel, this.props.timestepSize);
+
+            xVel += coeff * (k1.deltaXVel + 2 * (k2.deltaXVel + k3.deltaXVel) + k4.deltaXVel);
+            yVel += coeff * (k1.deltaYVel + 2 * (k2.deltaYVel + k3.deltaYVel) + k4.deltaYVel);
+            xPos += coeff * (k1.deltaXPos + 2 * (k2.deltaXPos + k3.deltaXPos) + k4.deltaXPos);
+            yPos += coeff * (k1.deltaYPos + 2 * (k2.deltaYPos + k3.deltaYPos) + k4.deltaYPos);
+        }
+        // make sure harmonic motion maintained and errors don't propagate
+        switch (this.props.simulationType) {
+            case 'Spring':
+                const equilibriumPos = this.props.springRestLength + (this.props.mass * this.props.gravity) / this.props.springConstant;
+                const amplitude = Math.abs(equilibriumPos - this.props.springStartLength);
+                if (startYVel < 0 && yVel > 0 && yPos < this.props.springRestLength) {
+                    yPos = equilibriumPos - amplitude;
+                } else if (startYVel > 0 && yVel < 0 && yPos > this.props.springRestLength) {
+                    yPos = equilibriumPos + amplitude;
+                }
+                break;
+            case 'Pendulum':
+                const startX = this.state.updatedStartPosX;
+                if (startXVel <= 0 && xVel > 0) {
+                    xPos = this.state.updatedStartPosX;
+                    if (this.state.updatedStartPosX > this.props.xMax / 2) {
+                        xPos = this.props.xMax / 2 + (this.props.xMax / 2 - startX) - 2 * this.props.radius;
+                    }
+                    yPos = this.props.startPosY;
+                } else if (startXVel >= 0 && xVel < 0) {
+                    xPos = this.state.updatedStartPosX;
+                    if (this.state.updatedStartPosX < this.props.xMax / 2) {
+                        xPos = this.props.xMax / 2 + (this.props.xMax / 2 - startX) - 2 * this.props.radius;
+                    }
+                    yPos = this.props.startPosY;
+                }
+                break;
+            case 'One Weight':
+                if (yPos < this.state.maxPosYConservation) {
+                    yPos = this.state.maxPosYConservation;
+                }
+        }
+        this.setState({ xVelocity: xVel, yVelocity: yVel, xPosition: xPos, yPosition: yPos });
+
+        const forcesn = this.getForces(xPos, yPos, xVel, yVel);
+        this.props.setForcesUpdated(forcesn);
+
+        // set component forces if they change
+        if (this.props.simulationType == 'Pendulum') {
+            const x = this.props.xMax / 2 - xPos - this.props.radius;
+            const y = yPos + this.props.radius + 5;
+            let angle = (Math.atan(y / x) * 180) / Math.PI;
+            if (angle < 0) {
+                angle += 180;
+            }
+            let oppositeAngle = 90 - angle;
+            if (oppositeAngle < 0) {
+                oppositeAngle = 90 - (180 - angle);
+            }
+
+            const pendulumLength = Math.sqrt(x * x + y * y);
+
+            const tensionComponent: IForce = {
+                description: 'Tension',
+                magnitude: this.props.mass * this.props.gravity * Math.cos((oppositeAngle * Math.PI) / 180) + (this.props.mass * (xVel * xVel + yVel * yVel)) / pendulumLength,
+                directionInDegrees: angle,
+            };
+            const gravityParallel: IForce = {
+                description: 'Gravity Parallel Component',
+                magnitude: this.props.gravity * Math.cos(((90 - angle) * Math.PI) / 180),
+                directionInDegrees: 270 - (90 - angle),
+            };
+            const gravityPerpendicular: IForce = {
+                description: 'Gravity Perpendicular Component',
+                magnitude: this.props.gravity * Math.sin(((90 - angle) * Math.PI) / 180),
+                directionInDegrees: -(90 - angle),
+            };
+            if (this.props.gravity * Math.sin(((90 - angle) * Math.PI) / 180) < 0) {
+                gravityPerpendicular.magnitude = Math.abs(this.props.gravity * Math.sin(((90 - angle) * Math.PI) / 180));
+                gravityPerpendicular.directionInDegrees = 180 - (90 - angle);
+            }
+            this.props.setComponentForces([tensionComponent, gravityParallel, gravityPerpendicular]);
+        }
+    };
+
+    renderForce = (force: IForce, index: number, asComponent: boolean, color = '#0d0d0d') => {
+        if (force.magnitude < this.epsilon) return;
+
+        const angle = (force.directionInDegrees * Math.PI) / 180;
+        const arrowStartY = this.state.yPosition + this.props.radius - this.props.radius * Math.sin(angle);
+        const arrowStartX = this.state.xPosition + this.props.radius + this.props.radius * Math.cos(angle);
+        const arrowEndY = arrowStartY - Math.abs(force.magnitude) * Math.sin(angle) - this.props.radius * Math.sin(angle);
+        const arrowEndX = arrowStartX + Math.abs(force.magnitude) * Math.cos(angle) + this.props.radius * Math.cos(angle);
+
+        let labelTop = arrowEndY + (force.directionInDegrees >= 0 && force.directionInDegrees < 180 ? 40 : -40);
+        let labelLeft = arrowEndX + (force.directionInDegrees > 90 && force.directionInDegrees < 270 ? -120 : 30);
+
+        labelTop = Math.max(Math.min(labelTop, this.props.yMax + 50), this.props.yMin);
+        labelLeft = Math.max(Math.min(labelLeft, this.props.xMax - 60), this.props.xMin);
+
+        return (
+            <div key={index} style={{ zIndex: 6, position: 'absolute' }}>
+                <div
+                    style={{
+                        pointerEvents: 'none',
+                        position: 'absolute',
+                        left: this.props.xMin,
+                        top: this.props.yMin,
+                    }}>
+                    <svg width={this.props.xMax - this.props.xMin + 'px'} height={this.panelHeight}>
+                        <defs>
+                            <marker id="forceArrow" markerWidth="4" markerHeight="4" refX="0" refY="2" orient="auto" markerUnits="strokeWidth">
+                                <path d="M0,0 L0,4 L4,2 z" fill={color} />
+                            </marker>
+                        </defs>
+                        <line strokeDasharray={asComponent ? '10,10' : undefined} x1={arrowStartX} y1={arrowStartY} x2={arrowEndX} y2={arrowEndY} stroke={color} strokeWidth="5" markerEnd="url(#forceArrow)" />
+                    </svg>
+                </div>
+                <div
+                    style={{
+                        pointerEvents: 'none',
+                        position: 'absolute',
+                        left: labelLeft + 'px',
+                        top: labelTop + 'px',
+                        lineHeight: 1,
+                        backgroundColor: this.labelBackgroundColor,
+                    }}>
+                    <p>{force.description || 'Force'}</p>
+                    {this.props.showForceMagnitudes && <p>{Math.round(100 * force.magnitude) / 100} N</p>}
+                </div>
+            </div>
+        );
+    };
+
+    renderVector = (id: string, magX: number, magY: number, color: string, label: string) => {
+        const mag = Math.sqrt(magX * magX + magY * magY);
+        return (
+            <div className="showvecs" style={{ zIndex: 6 }}>
+                <svg width={this.panelWidth} height={this.panelHeight}>
+                    <defs>
+                        <marker id={id} markerWidth="10" markerHeight="10" refX="0" refY="3" orient="auto" markerUnits="strokeWidth">
+                            <path d="M0,0 L0,6 L9,3 z" fill={color} />
+                        </marker>
+                    </defs>
+                    <line
+                        x1={this.state.xPosition + this.props.radius + (magX / mag) * this.props.radius}
+                        y1={this.state.yPosition + this.props.radius + (magY / mag) * this.props.radius}
+                        x2={this.state.xPosition + this.props.radius + (magX / mag) * this.props.radius + magX}
+                        y2={this.state.yPosition + this.props.radius + (magY / mag) * this.props.radius + magY}
+                        stroke={color}
+                        strokeWidth="5"
+                        markerEnd={`url(#${id})`}
+                    />
+                </svg>
+                <div
+                    style={{
+                        pointerEvents: 'none',
+                        position: 'absolute',
+                        left: this.state.xPosition + this.props.radius + 2 * (magX / mag) * this.props.radius + magX + 'px',
+                        top: this.state.yPosition + this.props.radius + 2 * (magY / mag) * this.props.radius + magY + 'px',
+                        lineHeight: 1,
+                    }}>
+                    <p style={{ background: 'white' }}>{label}</p>
+                </div>
+            </div>
+        );
+    };
+
+    // Render weight, spring, rod(s), vectors
+    render() {
+        return (
+            <div>
+                <div
+                    className="weightContainer"
+                    onPointerDown={e => {
+                        if (this.draggable) {
+                            this.props.pause();
+                            this.setState({
+                                dragging: true,
+                                clickPositionX: e.clientX,
+                                clickPositionY: e.clientY,
+                            });
+                        }
+                    }}
+                    onPointerMove={e => {
+                        if (this.state.dragging) {
+                            let newY = this.state.yPosition + e.clientY - this.state.clickPositionY;
+                            if (newY > this.props.yMax - 2 * this.props.radius - 10) {
+                                newY = this.props.yMax - 2 * this.props.radius - 10;
+                            } else if (newY < 10) {
+                                newY = 10;
+                            }
+
+                            let newX = this.state.xPosition + e.clientX - this.state.clickPositionX;
+                            if (newX > this.props.xMax - 2 * this.props.radius - 10) {
+                                newX = this.props.xMax - 2 * this.props.radius - 10;
+                            } else if (newX < 10) {
+                                newX = 10;
+                            }
+                            if (this.props.simulationType == 'Suspension') {
+                                if (newX < (this.props.xMax + this.props.xMin) / 4 - this.props.radius - 15) {
+                                    newX = (this.props.xMax + this.props.xMin) / 4 - this.props.radius - 15;
+                                } else if (newX > (3 * (this.props.xMax + this.props.xMin)) / 4 - this.props.radius / 2 - 15) {
+                                    newX = (3 * (this.props.xMax + this.props.xMin)) / 4 - this.props.radius / 2 - 15;
+                                }
+                            }
+
+                            this.setState({ yPosition: newY });
+                            this.props.setPosition(undefined, Math.round((this.props.yMax - 2 * this.props.radius - newY + 5) * 100) / 100);
+                            if (this.props.simulationType != 'Pulley') {
+                                this.setState({ xPosition: newX });
+                                this.props.setPosition(newX, undefined);
+                            }
+                            if (this.props.simulationType != 'Suspension') {
+                                if (this.props.simulationType != 'Pulley') {
+                                    this.setState({ updatedStartPosX: newX });
+                                }
+                                this.setState({ updatedStartPosY: newY });
+                            }
+                            this.setState({
+                                clickPositionX: e.clientX,
+                                clickPositionY: e.clientY,
+                            });
+                            this.setDisplayValues();
+                        }
+                    }}
+                    onPointerUp={e => {
+                        if (this.state.dragging) {
+                            if (this.props.simulationType != 'Pendulum' && this.props.simulationType != 'Suspension') {
+                                this.resetEverything();
+                            }
+                            this.setState({ dragging: false });
+                            let newY = this.state.yPosition + e.clientY - this.state.clickPositionY;
+                            if (newY > this.props.yMax - 2 * this.props.radius - 10) {
+                                newY = this.props.yMax - 2 * this.props.radius - 10;
+                            } else if (newY < 10) {
+                                newY = 10;
+                            }
+
+                            let newX = this.state.xPosition + e.clientX - this.state.clickPositionX;
+                            if (newX > this.props.xMax - 2 * this.props.radius - 10) {
+                                newX = this.props.xMax - 2 * this.props.radius - 10;
+                            } else if (newX < 10) {
+                                newX = 10;
+                            }
+                            if (this.props.simulationType == 'Spring') {
+                                this.props.setSpringLength(newY);
+                            }
+                            if (this.props.simulationType == 'Suspension') {
+                                const x1rod = (this.props.xMax + this.props.xMin) / 2 - this.props.radius - this.props.yMin - 200;
+                                const x2rod = (this.props.xMax + this.props.xMin) / 2 + this.props.yMin + 200 + this.props.radius;
+                                const deltaX1 = this.state.xPosition + this.props.radius - x1rod;
+                                const deltaX2 = x2rod - (this.state.xPosition + this.props.radius);
+                                const deltaY = this.state.yPosition + this.props.radius;
+                                const dir1T = Math.PI - Math.atan(deltaY / deltaX1);
+                                const dir2T = Math.atan(deltaY / deltaX2);
+                                const tensionMag2 = (this.props.mass * this.props.gravity) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T));
+                                const tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T);
+                                const tensionForce1: IForce = {
+                                    description: 'Tension',
+                                    magnitude: tensionMag1,
+                                    directionInDegrees: (dir1T * 180) / Math.PI,
+                                };
+                                const tensionForce2: IForce = {
+                                    description: 'Tension',
+                                    magnitude: tensionMag2,
+                                    directionInDegrees: (dir2T * 180) / Math.PI,
+                                };
+                                this.props.setForcesUpdated([tensionForce1, tensionForce2, this.gravityForce()]);
+                            }
+                        }
+                    }}>
+                    <div className="weight" style={this.weightStyle}>
+                        <p className="weightLabel">{this.props.mass} kg</p>
+                    </div>
+                </div>
+                {this.props.simulationType == 'Spring' && (
+                    <div className="spring">
+                        <svg width={this.panelWidth} height={this.panelHeight}>
+                            {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(val => {
+                                const count = 10;
+                                const xPos1 = this.state.xPosition + this.props.radius + (val % 2 === 0 ? -20 : 20);
+                                const xPos2 = this.state.xPosition + this.props.radius + (val === 10 ? 0 : val % 2 === 0 ? 20 : -20);
+                                const yPos1 = (val * this.state.yPosition) / count;
+                                const yPos2 = val === 10 ? this.state.yPosition + this.props.radius : ((val + 1) * this.state.yPosition) / count;
+                                return <line key={val} x1={xPos1} strokeLinecap="round" y1={yPos1} x2={xPos2} y2={yPos2} stroke={'#808080'} strokeWidth="10" />;
+                            })}
+                        </svg>
+                    </div>
+                )}
+
+                {this.props.simulationType == 'Pulley' && (
+                    <div className="rod">
+                        <svg width={this.panelWidth} height={this.panelHeight}>
+                            <line //
+                                x1={this.state.xPosition + this.props.radius}
+                                y1={this.state.yPosition + this.props.radius}
+                                x2={this.state.xPosition + this.props.radius}
+                                y2={this.props.yMin}
+                                stroke={'#deb887'}
+                                strokeWidth="10"
+                            />
+                        </svg>
+                    </div>
+                )}
+                {this.props.simulationType == 'Pulley' && (
+                    <div className="wheel">
+                        <svg width={this.panelWidth} height={this.panelHeight}>
+                            <circle cx={(this.props.xMax + this.props.xMin) / 2} cy={this.props.radius} r={this.props.radius * 1.5} fill={'#808080'} />
+                        </svg>
+                    </div>
+                )}
+                {this.props.simulationType == 'Suspension' && (
+                    <div className="rod">
+                        <svg width={this.panelWidth} height={this.panelHeight}>
+                            <line
+                                x1={this.state.xPosition + this.props.radius}
+                                y1={this.state.yPosition + this.props.radius}
+                                x2={(this.props.xMax + this.props.xMin) / 2 - this.props.radius - this.props.yMin - 200}
+                                y2={this.props.yMin}
+                                stroke={'#deb887'}
+                                strokeWidth="10"
+                            />
+                        </svg>
+                        <p
+                            style={{
+                                position: 'absolute',
+                                left: (this.props.xMax + this.props.xMin) / 2 - this.props.radius - this.props.yMin - 200 + 80 + 'px',
+                                top: 10 + 'px',
+                                backgroundColor: this.labelBackgroundColor,
+                            }}>
+                            {Math.round(
+                                ((Math.atan((this.state.yPosition + this.props.radius) / (this.state.xPosition + this.props.radius - ((this.props.xMax + this.props.xMin) / 2 - this.props.radius - this.props.yMin - 200))) * 180) / Math.PI) * 100
+                            ) / 100}
+                            °
+                        </p>
+                        <div className="rod">
+                            <svg width={this.props.panelWidth() + 'px'} height={this.panelHeight}>
+                                <line
+                                    x1={this.state.xPosition + this.props.radius}
+                                    y1={this.state.yPosition + this.props.radius}
+                                    x2={(this.props.xMax + this.props.xMin) / 2 + this.props.yMin + 200 + this.props.radius}
+                                    y2={this.props.yMin}
+                                    stroke={'#deb887'}
+                                    strokeWidth="10"
+                                />
+                            </svg>
+                        </div>
+                        <p
+                            style={{
+                                position: 'absolute',
+                                left: (this.props.xMax + this.props.xMin) / 2 + this.props.yMin + 200 + this.props.radius - 80 + 'px',
+                                top: 10 + 'px',
+                                backgroundColor: this.labelBackgroundColor,
+                            }}>
+                            {Math.round(
+                                ((Math.atan((this.state.yPosition + this.props.radius) / ((this.props.xMax + this.props.xMin) / 2 + this.props.yMin + 200 + this.props.radius - (this.state.xPosition + this.props.radius))) * 180) / Math.PI) * 100
+                            ) / 100}
+                            °
+                        </p>
+                    </div>
+                )}
+                {this.props.simulationType == 'Circular Motion' && (
+                    <div className="rod">
+                        <svg width={this.panelWidth} height={this.panelHeight}>
+                            <line
+                                x1={this.state.xPosition + this.props.radius}
+                                y1={this.state.yPosition + this.props.radius}
+                                x2={(this.props.xMin + this.props.xMax) / 2}
+                                y2={(this.props.yMin + this.props.yMax) / 2}
+                                stroke={'#deb887'}
+                                strokeWidth="10"
+                            />
+                        </svg>
+                    </div>
+                )}
+                {this.props.simulationType == 'Pendulum' && (
+                    <div className="rod">
+                        <svg width={this.panelWidth} height={this.panelHeight}>
+                            <line x1={this.state.xPosition + this.props.radius} y1={this.state.yPosition + this.props.radius} x2={this.props.xMax / 2} y2={-5} stroke={'#deb887'} strokeWidth="10" />
+                        </svg>
+                        {!this.state.dragging && (
+                            <div>
+                                <p
+                                    style={{
+                                        position: 'absolute',
+                                        zIndex: 5,
+                                        left: this.state.xPosition + 'px',
+                                        top: this.state.yPosition - 70 + 'px',
+                                        backgroundColor: this.labelBackgroundColor,
+                                    }}>
+                                    {Math.round(this.props.pendulumLength)} m
+                                </p>
+                                <p
+                                    style={{
+                                        position: 'absolute',
+                                        left: this.props.xMax / 2 + 'px',
+                                        top: 30 + 'px',
+                                        backgroundColor: this.labelBackgroundColor,
+                                    }}>
+                                    {Math.round(this.props.pendulumAngle * 100) / 100}°
+                                </p>
+                            </div>
+                        )}
+                    </div>
+                )}
+                {this.props.simulationType == 'Inclined Plane' && (
+                    <div>
+                        <div className="wedge">
+                            <svg width={this.panelWidth} height={this.props.yMax + 'px'}>
+                                <polygon points={this.state.coordinates} style={{ fill: 'burlywood' }} />
+                            </svg>
+                        </div>
+                        <p
+                            style={{
+                                position: 'absolute',
+                                left: Math.round(this.props.xMax * 0.25 + this.props.wedgeWidth / 3) + 'px',
+                                top: Math.round(this.props.yMax - 40) + 'px',
+                            }}>
+                            {Math.round(((Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI) * 100) / 100}°
+                        </p>
+                    </div>
+                )}
+                {!this.state.dragging &&
+                    this.props.showAcceleration &&
+                    this.renderVector(
+                        'accArrow',
+                        this.getNewAccelerationX(this.props.forcesUpdated()),
+                        this.getNewAccelerationY(this.props.forcesUpdated()),
+                        'green',
+                        `${Math.round(100 * Math.sqrt(this.state.xAccel * this.state.xAccel + this.state.yAccel * this.state.yAccel)) / 100} m/s^2`
+                    )}
+                {!this.state.dragging &&
+                    this.props.showVelocity &&
+                    this.renderVector(
+                        'velArrow',
+                        this.state.xVelocity,
+                        this.state.yVelocity,
+                        'blue',
+                        `${Math.round(100 * Math.sqrt(this.props.displayXVelocity * this.props.displayXVelocity + this.props.displayYVelocity * this.props.displayYVelocity)) / 100} m/s`
+                    )}
+                {!this.state.dragging && this.props.showComponentForces && this.props.componentForces().map((force, index) => this.renderForce(force, index, true))}
+                {!this.state.dragging && this.props.showForces && this.props.forcesUpdated().map((force, index) => this.renderForce(force, index, false))}
+            </div>
+        );
+    }
+}
diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts
new file mode 100644
index 000000000..65decc147
--- /dev/null
+++ b/src/fields/DocSymbols.ts
@@ -0,0 +1,26 @@
+export const Update = Symbol('DocUpdate');
+export const Self = Symbol('DocSelf');
+export const SelfProxy = Symbol('DocSelfProxy');
+export const FieldKeys = Symbol('DocFieldKeys');
+export const FieldTuples = Symbol('DocFieldTuples');
+export const Width = Symbol('DocWidth');
+export const Height = Symbol('DocHeight');
+export const Animation = Symbol('DocAnimation');
+export const Highlight = Symbol('DocHighlight');
+export const DocData = Symbol('DocData');
+export const DocLayout = Symbol('DocLayout');
+export const DocFields = Symbol('DocFields');
+export const DocCss = Symbol('DocCss');
+export const DocAcl = Symbol('DocAcl');
+export const DirectLinks = Symbol('DocDirectLinks');
+export const AclUnset = Symbol('DocAclUnset');
+export const AclPrivate = Symbol('DocAclOwnerOnly');
+export const AclReadonly = Symbol('DocAclReadOnly');
+export const AclAugment = Symbol('DocAclAugment');
+export const AclSelfEdit = Symbol('DocAclSelfEdit');
+export const AclEdit = Symbol('DocAclEdit');
+export const AclAdmin = Symbol('DocAclAdmin');
+export const UpdatingFromServer = Symbol('DocUpdatingFromServer');
+export const Initializing = Symbol('DocInitializing');
+export const ForceServerWrite = Symbol('DocForceServerWrite');
+export const CachedUpdates = Symbol('DocCachedUpdates');
-- 
cgit v1.2.3-70-g09d2


From 98d40d63fce7e63bce264b07dbb433dfe2b88d0c Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Tue, 15 Aug 2023 14:40:56 -0400
Subject: Freeze

---
 package-lock.json                         | 97 +++++++++++++++++++++++++++++--
 src/client/views/nodes/DocumentView.tsx   |  2 +-
 src/client/views/nodes/MapBox/MapBox.scss |  4 ++
 src/client/views/nodes/MapBox/MapBox.tsx  | 56 ++++++++++++++----
 src/client/views/nodes/trails/PresBox.tsx |  1 +
 5 files changed, 143 insertions(+), 17 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/package-lock.json b/package-lock.json
index a62b3306c..e79d94b3a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6507,9 +6507,9 @@
             }
         },
         "browndash-components": {
-            "version": "0.0.92",
-            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.92.tgz",
-            "integrity": "sha512-eE/6WQNZiLnaXUKyoaMm0PDYjExUsFJ9VTAIIxROpYPosIBKWNZ743xaOfmehib5us9hEXJb0CvUFJQb8rzDVw==",
+            "version": "0.0.93",
+            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.93.tgz",
+            "integrity": "sha512-JL9Hi7Nq+zHZtjLCQSuGhID0Hd7X6sARutyJqbECfsLWACsIC/JfeEQZdUeH5CqO+R7pJKeoWM82AIkWGtN2Xw==",
             "requires": {
                 "@emotion/react": "^11.11.1",
                 "@emotion/styled": "^11.11.0",
@@ -7063,12 +7063,97 @@
                                         "strip-ansi": "^7.0.1"
                                     }
                                 },
+                                "string-width-cjs": {
+                                    "version": "npm:string-width@4.2.3",
+                                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                                    "requires": {
+                                        "emoji-regex": "^8.0.0",
+                                        "is-fullwidth-code-point": "^3.0.0",
+                                        "strip-ansi": "^6.0.1"
+                                    },
+                                    "dependencies": {
+                                        "ansi-regex": {
+                                            "version": "5.0.1",
+                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                                        },
+                                        "emoji-regex": {
+                                            "version": "8.0.0",
+                                            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                                            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+                                        },
+                                        "strip-ansi": {
+                                            "version": "6.0.1",
+                                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                                            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                                            "requires": {
+                                                "ansi-regex": "^5.0.1"
+                                            }
+                                        }
+                                    }
+                                },
                                 "strip-ansi": {
                                     "version": "7.1.0",
                                     "bundled": true,
                                     "requires": {
                                         "ansi-regex": "^6.0.1"
                                     }
+                                },
+                                "strip-ansi-cjs": {
+                                    "version": "npm:strip-ansi@6.0.1",
+                                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                                    "requires": {
+                                        "ansi-regex": "^5.0.1"
+                                    },
+                                    "dependencies": {
+                                        "ansi-regex": {
+                                            "version": "5.0.1",
+                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                                        }
+                                    }
+                                },
+                                "wrap-ansi-cjs": {
+                                    "version": "npm:wrap-ansi@7.0.0",
+                                    "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+                                    "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+                                    "requires": {
+                                        "ansi-styles": "^4.0.0",
+                                        "string-width": "^4.1.0",
+                                        "strip-ansi": "^6.0.0"
+                                    },
+                                    "dependencies": {
+                                        "ansi-regex": {
+                                            "version": "5.0.1",
+                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                                        },
+                                        "emoji-regex": {
+                                            "version": "8.0.0",
+                                            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                                            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+                                        },
+                                        "string-width": {
+                                            "version": "4.2.3",
+                                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                                            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                                            "requires": {
+                                                "emoji-regex": "^8.0.0",
+                                                "is-fullwidth-code-point": "^3.0.0",
+                                                "strip-ansi": "^6.0.1"
+                                            }
+                                        },
+                                        "strip-ansi": {
+                                            "version": "6.0.1",
+                                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                                            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                                            "requires": {
+                                                "ansi-regex": "^5.0.1"
+                                            }
+                                        }
+                                    }
                                 }
                             }
                         },
@@ -8573,7 +8658,7 @@
                             }
                         },
                         "string-width-cjs": {
-                            "version": "npm:string-width@4.2.3",
+                            "version": "npm:string-width-cjs@4.2.3",
                             "bundled": true,
                             "requires": {
                                 "emoji-regex": "^8.0.0",
@@ -8596,7 +8681,7 @@
                             }
                         },
                         "strip-ansi-cjs": {
-                            "version": "npm:strip-ansi@6.0.1",
+                            "version": "npm:strip-ansi-cjs@6.0.1",
                             "bundled": true,
                             "requires": {
                                 "ansi-regex": "^5.0.1"
@@ -8755,7 +8840,7 @@
                             }
                         },
                         "wrap-ansi-cjs": {
-                            "version": "npm:wrap-ansi@7.0.0",
+                            "version": "npm:wrap-ansi-cjs@7.0.0",
                             "bundled": true,
                             "requires": {
                                 "ansi-styles": "^4.0.0",
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index bb9f45bdd..419800ad5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1626,4 +1626,4 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc, linkSour
     });
     embedding && DocServer.UPDATE_SERVER_CACHE(); // if a new embedding was made, update the client's server cache so that it will not come back as a promise
     return links;
-});
+});
\ No newline at end of file
diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index 539c506c7..fcd4fc9be 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -11,6 +11,10 @@
         padding: 12;
         font-size: 17;
     }
+    .mapBox-topbar{
+        display:flex;
+        flex-direction: row;
+    }
 
     .mapBox-overlayButton-sidebar {
         background: #121721;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 15cf22953..283d57bb6 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,7 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { GoogleMapProps, Marker } from '@react-google-maps/api';
 import BingMapsReact from 'bingmaps-react';
-import { EditableText } from 'browndash-components';
+import { EditableText, Toggle } from 'browndash-components';
 import e from 'connect-flash';
 import { truncateSync } from 'fs';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
@@ -65,7 +65,7 @@ const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing
 const script = document.createElement('script');
 script.defer = true;
 script.async = true;
-script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
+script.src = `https://maps.googleapis.com/maps/api/js?key=${bingApiKey}&libraries=places,drawing`;
 document.head.appendChild(script);
 
 /**
@@ -665,8 +665,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Pushpin dblclick
      */
     @action
-    pushpinDblClicked = (pinDoc:Doc,pin:any) => {
-        this.removePushpin(pinDoc,pin);
+    pushpinDblClicked = (pin:any, pinDoc?:Doc) => {
+        if(pinDoc)
+            this.removePushpin(pinDoc,pin);
+        else
+            this._bingMap.current.entities.remove(pin);
     };
     /*
      * Pushpin onclick
@@ -720,7 +723,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (this.placePinOn) {
             this.createPushpin(e.location.latitude, e.location.longitude);
             // this.addAllPins();
-            this.placePinOn = false;
+            // this.placePinOn = false;
         }
     };
 
@@ -779,6 +782,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         var temp = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
             color: 'blue',
         });
+        
+        this.MicrosoftMaps.Events.addHandler(temp, 'dblclick', (e: any) => this.pushpinDblClicked(temp));
         if (temp != this.searched_pin || this.searched_pin == null) {
             this._bingMap.current.entities.push(temp);
             this.searched_pin = temp;
@@ -835,7 +840,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this._bingMap.current.entities.push(pushPin);
 
         this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin,pushPin));
-        this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pin,pushPin));
+        this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pushPin, pin));
     }
 
 
@@ -932,11 +937,42 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
                     {this.annotationLayer}
 
-                    <EditableText setEditing={(isEditing: boolean) => ({})} editing onEdit={(newText: string) => (this.bingSearchBarContents = newText)} placeholder="..." text="Boston, MA" />
-
-                    <input type="button" value="Search" onClick={this.bingSearch} />
+                    <EditableText setEditing={(isEditing: boolean) => ({})} editing onEdit={(newText: string) => (this.bingSearchBarContents = newText)} placeholder="Boston, MA" text="Boston, MA" />
+                    <div className='mapBox-topbar'>
+                        <input type="button" value="Search" onClick={this.bingSearch} />
+
+                        {/* {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />} */}
+                        {/* {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />}
+
+                        <Toggle
+                            onClick={this.togglePlacePin}
+                            onPointerDown={function noRefCheck(){}}
+                            onPointerDownCapture={function noRefCheck(){}}
+                            onPointerMove={function noRefCheck(){}}
+                            onPointerMoveCapture={function noRefCheck(){}}
+                            onPointerUp={function noRefCheck(){}}
+                            text="Place Pin"
+                            toggleType = "checkbox"
+                            toggleStatus= {false}
+                            type="secondary"
+                        /> */}
+
+                        <input type="checkbox"
+                                    id="switch"
+                                    className="checkbox" 
+                                    checked={this.placePinOn}
+                                    onChange={this.togglePlacePin}
+                                    />
+                                        
+                                <label htmlFor="switch"
+                                    className="toggle">
+                                    <p>
+                                    {this.placePinOn ? 'Place Pin ON' : 'Place Pin OFF'}
+                                    </p>
+                                </label>
+                    </div>
+                    
 
-                    {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />}
                     {/* {this.pinIsSelected_TEMPORARY? <input type="button" value="Delete Pin" onClick={this.removeSelectedPin} /> : null} */}
                     <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions}>
                     </BingMapsReact>
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index c0cd3ab70..76f42778b 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -52,6 +52,7 @@ export interface pinDataTypes {
     dataview?: boolean;
     poslayoutview?: boolean;
     dataannos?: boolean;
+    map?: boolean;
 }
 export interface PinProps {
     audioRange?: boolean;
-- 
cgit v1.2.3-70-g09d2


From 0eb9d37a9dd1a6539f331f953d5f20c761d5f940 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 17 Aug 2023 10:00:10 -0400
Subject: fixed brushing of pinned viewports for chrome to not flash. converted
 pres<Property> to config_<property> and treeView<Property> to
 treeView_Property.  fixed text toolbar to show/set text properties based on
 insertion point.

---
 src/client/documents/Documents.ts                  | 101 ++--
 src/client/util/CurrentUserUtils.ts                |  38 +-
 src/client/util/DocumentManager.ts                 |   6 +-
 src/client/views/DashboardView.tsx                 |  15 +-
 src/client/views/DocumentButtonBar.tsx             |   6 +-
 src/client/views/InkStroke.scss                    |   1 +
 src/client/views/InkingStroke.tsx                  |   6 +-
 src/client/views/MainView.tsx                      |   4 +-
 src/client/views/MarqueeAnnotator.tsx              |   2 +-
 src/client/views/PropertiesView.scss               |  88 ++--
 src/client/views/PropertiesView.tsx                |  32 +-
 src/client/views/StyleProvider.tsx                 |   8 +-
 src/client/views/TemplateMenu.tsx                  |   2 +-
 .../views/collections/CollectionTimeView.tsx       |   6 +-
 .../views/collections/CollectionTreeView.tsx       |  22 +-
 src/client/views/collections/CollectionView.tsx    |   2 +-
 src/client/views/collections/TabDocView.tsx        |  45 +-
 src/client/views/collections/TreeView.tsx          |  90 ++--
 .../collectionFreeForm/CollectionFreeFormView.scss |   7 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  46 +-
 src/client/views/linking/LinkMenuItem.tsx          |   2 +-
 src/client/views/nodes/DataVizBox/DataVizBox.tsx   | 111 ++--
 .../nodes/DataVizBox/components/Histogram.tsx      | 534 ++++++++++---------
 .../nodes/DataVizBox/components/LineChart.tsx      | 105 ++--
 .../views/nodes/DataVizBox/components/PieChart.tsx | 401 ++++++++-------
 .../views/nodes/DataVizBox/components/TableBox.tsx |  87 ++--
 src/client/views/nodes/DocumentView.tsx            |  17 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |   4 +-
 src/client/views/nodes/FunctionPlotBox.tsx         |   4 +-
 src/client/views/nodes/ImageBox.tsx                |  14 +-
 src/client/views/nodes/KeyValueBox.tsx             |  17 +-
 src/client/views/nodes/LinkBox.tsx                 |   2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   2 +-
 src/client/views/nodes/MapBox/MapBox2.tsx          |   2 +-
 src/client/views/nodes/PDFBox.tsx                  |   6 +-
 src/client/views/nodes/WebBox.tsx                  |  16 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  29 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    |   2 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     |   2 +-
 src/client/views/nodes/trails/PresBox.tsx          | 567 +++++++++++----------
 src/client/views/nodes/trails/PresElementBox.tsx   |  50 +-
 src/client/views/pdf/Annotation.tsx                |   4 +-
 src/client/views/pdf/PDFViewer.tsx                 |   8 +-
 src/fields/Doc.ts                                  |   8 +-
 src/fields/documentSchemas.ts                      |  10 +-
 45 files changed, 1334 insertions(+), 1197 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5c33e319d..96a983153 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -288,12 +288,12 @@ export class DocumentOptions {
     _isTimelineLabel?: BOOLt = new BoolInfo('is document a timeline label');
     _isLightbox?: BOOLt = new BoolInfo('whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target');
 
-    presPanX?: NUMt = new NumInfo('panX saved as a view spec');
-    presPanY?: NUMt = new NumInfo('panY saved as a view spec');
-    presViewScale?: NUMt = new NumInfo('viewScale saved as a view Spec');
-    presTransition?: NUMt = new NumInfo('the time taken for the transition TO a document');
-    presDuration?: NUMt = new NumInfo('the duration of the slide in presentation view');
-    presZoomText?: BOOLt = new BoolInfo('whether text anchors should shown in a larger box when following links to make them stand out');
+    config_panX?: NUMt = new NumInfo('panX saved as a view spec');
+    config_panY?: NUMt = new NumInfo('panY saved as a view spec');
+    config_viewScale?: NUMt = new NumInfo('viewScale saved as a view Spec');
+    presentation_transition?: NUMt = new NumInfo('the time taken for the transition TO a document');
+    presentation_duration?: NUMt = new NumInfo('the duration of the slide in presentation view');
+    presentation_zoomText?: BOOLt = new BoolInfo('whether text anchors should shown in a larger box when following links to make them stand out');
 
     data?: any;
     data_useCors?: BOOLt = new BoolInfo('whether CORS protocol should be used for web page');
@@ -378,21 +378,21 @@ export class DocumentOptions {
     onDragStart?: ScriptField; //script to execute at start of drag operation --  e.g., when a "creator" button is dragged this script generates a different document to drop
     target?: Doc; // available for use in scripts.  used to provide a document parameter to the script (Note, this is a convenience entry since any field could be used for parameterizing a script)
 
-    treeViewHideTitle?: BOOLt = new BoolInfo('whether to hide the top document title of a tree view');
-    treeViewHideUnrendered?: BOOLt = new BoolInfo("tells tree view not to display documents that have an 'layout_unrendered' tag unless they also have a treeViewFieldKey tag (presBox)");
-    treeViewHideHeaderIfTemplate?: BOOLt = new BoolInfo('whether to hide the header for a document in a tree view only if a childLayoutTemplate is provided (presBox)');
-    treeViewHideHeader?: BOOLt = new BoolInfo('whether to hide the header for a document in a tree view');
-    treeViewHideHeaderFields?: BOOLt = new BoolInfo('whether to hide the drop down options for tree view items.');
-    treeViewChildDoubleClick?: ScriptField; //
-    treeViewOpenIsTransient?: BOOLt = new BoolInfo("ignores the treeViewOpen Doc flag, allowing a treeViewItem's expand/collapse state to be independent of other views of the same document in the same or any other tree view");
-    treeViewOpen?: BOOLt = new BoolInfo('whether this document is expanded in a tree view');
-    treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view
-    treeViewExpandedViewLock?: BOOLt = new BoolInfo('whether the expanded view can be changed');
-    treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked
-    treeViewTruncateTitleWidth?: NUMt = new NumInfo('maximum width of a treew view title before truncation');
-    treeViewHasOverlay?: BOOLt = new BoolInfo('whether the treeview has an overlay for freeform annotations');
-    treeViewType?: string; // whether treeview is a Slide, file system, or (default) collection hierarchy
-    treeViewFreezeChildren?: STRt = new StrInfo('set (add, remove, add|remove) to disable adding, removing or both from collection');
+    treeView_HideTitle?: BOOLt = new BoolInfo('whether to hide the top document title of a tree view');
+    treeView_HideUnrendered?: BOOLt = new BoolInfo("tells tree view not to display documents that have an 'layout_unrendered' tag unless they also have a treeView_FieldKey tag (presBox)");
+    treeView_HideHeaderIfTemplate?: BOOLt = new BoolInfo('whether to hide the header for a document in a tree view only if a childLayoutTemplate is provided (presBox)');
+    treeView_HideHeader?: BOOLt = new BoolInfo('whether to hide the header for a document in a tree view');
+    treeView_HideHeaderFields?: BOOLt = new BoolInfo('whether to hide the drop down options for tree view items.');
+    treeView_ChildDoubleClick?: ScriptField; //
+    treeView_OpenIsTransient?: BOOLt = new BoolInfo("ignores the treeView_Open Doc flag, allowing a treeView_Item's expand/collapse state to be independent of other views of the same document in the same or any other tree view");
+    treeView_Open?: BOOLt = new BoolInfo('whether this document is expanded in a tree view');
+    treeView_ExpandedView?: string; // which field/thing is displayed when this item is opened in tree view
+    treeView_ExpandedViewLock?: BOOLt = new BoolInfo('whether the expanded view can be changed');
+    treeView_Checked?: ScriptField; // script to call when a tree view checkbox is checked
+    treeView_TruncateTitleWidth?: NUMt = new NumInfo('maximum width of a treew view title before truncation');
+    treeView_HasOverlay?: BOOLt = new BoolInfo('whether the treeview has an overlay for freeform annotations');
+    treeView_Type?: string; // whether treeview is a Slide, file system, or (default) collection hierarchy
+    treeView_FreezeChildren?: STRt = new StrInfo('set (add, remove, add|remove) to disable adding, removing or both from collection');
 
     sidebar_color?: string; // background color of text sidebar
     sidebar_collectionType?: string; // collection type of text sidebar
@@ -635,7 +635,7 @@ export namespace Docs {
                 DocumentType.CONFIG,
                 {
                     layout: { view: CollectionView, dataField: defaultDataKey },
-                    options: { layout_hideLinkButton: true, layout_unrendered: true },
+                    options: { config: '', layout_hideLinkButton: true, layout_unrendered: true },
                 },
             ],
             [
@@ -679,7 +679,7 @@ export namespace Docs {
                 DocumentType.DATAVIZ,
                 {
                     layout: { view: DataVizBox, dataField: defaultDataKey },
-                    options: { _layout_fitWidth: true, nativeDimModifiable: true },
+                    options: { dataViz_title: '', _layout_fitWidth: true, nativeDimModifiable: true },
                 },
             ],
             [
@@ -845,7 +845,7 @@ export namespace Docs {
          * only when creating a DockDocument from the current user's already existing
          * main document.
          */
-        function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDoc?: Doc) {
+        function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDoc?: Doc, noView?: boolean) {
             const viewKeys = ['x', 'y', 'isSystem']; // keys that should be addded to the view document even though they don't begin with an "_"
             const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_');
 
@@ -872,29 +872,34 @@ export namespace Docs {
                 dataDoc.proto = proto;
             }
 
-            const viewFirstProps: { [id: string]: any } = { author: Doc.CurrentUserEmail };
-            viewFirstProps['acl-Guest'] = options['_acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View);
-            let viewDoc: Doc;
-            // determines whether viewDoc should be created using placeholder Doc or default
-            if (placeholderDoc) {
-                placeholderDoc._height = options._height !== undefined ? Number(options._height) : undefined;
-                placeholderDoc._width = options._width !== undefined ? Number(options._width) : undefined;
-                viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true);
-                Array.from(Object.keys(placeholderDoc))
-                    .filter(key => key.startsWith('acl'))
-                    .forEach(key => (dataDoc[key] = viewDoc[key] = placeholderDoc[key]));
-            } else {
-                viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
-            }
-            Doc.assign(viewDoc, viewProps, true, true);
-            if (![DocumentType.LINK, DocumentType.CONFIG, DocumentType.LABEL].includes(viewDoc.type as any)) {
-                DocUtils.MakeLinkToActiveAudio(() => viewDoc);
+            if (!noView) {
+                const viewFirstProps: { [id: string]: any } = { author: Doc.CurrentUserEmail };
+                viewFirstProps['acl-Guest'] = options['_acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View);
+                let viewDoc: Doc;
+                // determines whether viewDoc should be created using placeholder Doc or default
+                if (placeholderDoc) {
+                    placeholderDoc._height = options._height !== undefined ? Number(options._height) : undefined;
+                    placeholderDoc._width = options._width !== undefined ? Number(options._width) : undefined;
+                    viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true);
+                    Array.from(Object.keys(placeholderDoc))
+                        .filter(key => key.startsWith('acl'))
+                        .forEach(key => (dataDoc[key] = viewDoc[key] = placeholderDoc[key]));
+                } else {
+                    viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
+                }
+                Doc.assign(viewDoc, viewProps, true, true);
+                if (![DocumentType.LINK, DocumentType.CONFIG, DocumentType.LABEL].includes(viewDoc.type as any)) {
+                    DocUtils.MakeLinkToActiveAudio(() => viewDoc);
+                }
+                updateCachedAcls(dataDoc);
+                updateCachedAcls(viewDoc);
+
+                return viewDoc;
             }
 
             updateCachedAcls(dataDoc);
-            updateCachedAcls(viewDoc);
 
-            return viewDoc;
+            return dataDoc;
         }
 
         export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}, overwriteDoc?: Doc) {
@@ -1065,7 +1070,7 @@ export namespace Docs {
         }
 
         export function ConfigDocument(options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.CONFIG), options?.data, options, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.CONFIG), options?.data, options, id, '', undefined, undefined, true);
         }
 
         export function HTMLMarkerDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -1105,7 +1110,9 @@ export namespace Docs {
         }
 
         export function TreeDocument(documents: Array<Doc>, options: DocumentOptions, id?: string, protoId?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xMargin: 5, _yMargin: 5, ...options, _type_collection: CollectionViewType.Tree }, id, undefined, protoId);
+            const doc = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xMargin: 5, _yMargin: 5, ...options, _type_collection: CollectionViewType.Tree }, id, undefined, protoId);
+            Doc.GetProto(doc).treeView = ''; /// not really needed, but makes keyvalue pane look better
+            return doc;
         }
 
         export function StackingDocument(documents: Array<Doc>, options: DocumentOptions, id?: string, protoId?: string) {
@@ -1163,11 +1170,11 @@ export namespace Docs {
         }
 
         export function DataVizDocument(url: string, options?: DocumentOptions, overwriteDoc?: Doc) {
-            return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', type: 'dataviz',  ...options }, undefined, undefined, undefined, overwriteDoc);
+            return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', type: 'dataviz', ...options }, undefined, undefined, undefined, overwriteDoc);
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
-            const ret = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewFreezeChildren: 'remove|add', ...options, type_collection: CollectionViewType.Docking, dockingConfig: config }, id);
+            const ret = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeView_FreezeChildren: 'remove|add', ...options, type_collection: CollectionViewType.Docking, dockingConfig: config }, id);
             documents.map(c => Doc.SetContainer(c, ret));
             return ret;
         }
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 47cd866cb..af43ef16a 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -278,12 +278,12 @@ export class CurrentUserUtils {
             {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10}, scripts: {onClick: FollowLinkScript()?.script.originalScript ?? ""}},
             {key: "Script",     creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
             {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts),   opts: { _width: 300, _height: 300 }},
-            {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeViewHideUnrendered: true}},
-            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: "embed" as dropActionType, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, layout_boxShadow: "0 0" }},
+            {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeView_HideUnrendered: true}},
+            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: "embed" as dropActionType, treeView_HideTitle: true, _layout_fitWidth:true, _chromeHidden: true, layout_boxShadow: "0 0" }},
             {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
             {key: "Slide",      creator: opts => Docs.Create.TreeDocument([], opts),        opts: { _width: 300, _height: 200, _type_collection: CollectionViewType.Tree, 
-                                                                                                treeViewHasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, 
-                                                                                                dropAction:'move', treeViewType: TreeViewType.outline, 
+                                                                                                treeView_HasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, 
+                                                                                                dropAction:'move', treeView_Type: TreeViewType.outline, 
                                                                                                 backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true 
                                                                                             }, funcs: {title: 'self.text?.Text'}},
             ];
@@ -493,8 +493,8 @@ export class CurrentUserUtils {
         const childContextMenuLabels =  ["Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard", "Reset Dashboard"];// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters
         const childContextMenuIcons = ["chalkboard", "tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
         const reqdOpts:DocumentOptions = {
-            title: "My Dashboards", childHideLinkButton: true, treeViewFreezeChildren: "remove|add", treeViewHideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
-            dropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeViewTruncateTitleWidth: 350, ignoreClick: true, 
+            title: "My Dashboards", childHideLinkButton: true, treeView_FreezeChildren: "remove|add", treeView_HideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
+            dropAction: "same", treeView_Type: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeView_TruncateTitleWidth: 350, ignoreClick: true, 
             layout_headerButton: newDashboardButton, childDragAction: "none",
             _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
             contextMenuLabels:new List<string>(contextMenuLabels),
@@ -522,16 +522,16 @@ export class CurrentUserUtils {
         
         const newFolder = `TreeView_addNewFolder()`;
         const newFolderOpts: DocumentOptions = {
-            _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _width: 30, _height: 30, undoIgnoreFields:new List<string>(['treeViewSortCriterion']),
+            _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _width: 30, _height: 30, undoIgnoreFields:new List<string>(['treeView_SortCriterion']),
             title: "New folder", color: Colors.BLACK, btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true
         };
         const newFolderScript = { onClick: newFolder};
         const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.layout_headerButton), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
         
         const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, 
-            title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, dropAction: 'add', isSystem: true,
-            isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true, 
-            treeViewTruncateTitleWidth: 350, ignoreClick: true, childDragAction: "embed",
+            title: "My Documents", layout_headerButton: newFolderButton, treeView_HideTitle: true, dropAction: 'add', isSystem: true,
+            isFolder: true, treeView_Type: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true, 
+            treeView_TruncateTitleWidth: 350, ignoreClick: true, childDragAction: "embed",
             childContextMenuLabels: new List<string>(["Create new folder"]),
             childContextMenuIcons: new List<string>(["plus"]),
             layout_explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
@@ -548,8 +548,8 @@ export class CurrentUserUtils {
     /// initializes the panel displaying docs that have been recently closed
     static setupRecentlyClosed(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, isFolder: true,
-            title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDragAction: "move", isSystem: true,
-            treeViewTruncateTitleWidth: 350, ignoreClick: true,  layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same",
+            title: "My Recently Closed", childHideLinkButton: true, treeView_HideTitle: true, childDragAction: "move", isSystem: true,
+            treeView_TruncateTitleWidth: 350, ignoreClick: true,  layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same",
             contextMenuLabels: new List<string>(["Empty recently closed"]),
             contextMenuIcons:new List<string>(["trash"]),
             layout_explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
@@ -573,9 +573,9 @@ export class CurrentUserUtils {
         const reqdOpts:DocumentOptions = {
             _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view",
             layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same", ignoreClick: true, isSystem: true,
-            treeViewHideTitle: true, treeViewTruncateTitleWidth: 350
+            treeView_HideTitle: true, treeView_TruncateTitleWidth: 350
         };
-        if (!doc[field]) DocUtils.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" });
+        if (!doc[field]) DocUtils.AssignOpts(doc, {treeView_Open: true, treeView_ExpandedView: "fields" });
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [doc]);
     }
 
@@ -789,19 +789,19 @@ export class CurrentUserUtils {
     static setupSharedDocs(doc: Doc, sharingDocumentId: string) {
         const dblClkScript = "{scriptContext.openLevel(documentView); addDocToList(scriptContext.props.treeView.props.Document, 'viewed', documentView.rootDoc);}";
         
-        const sharedScripts = {  treeViewChildDoubleClick: dblClkScript,  }
+        const sharedScripts = {  treeView_ChildDoubleClick: dblClkScript,  }
         const sharedDocOpts:DocumentOptions = {
             title: "My Shared Docs",
             userColor: "rgb(202, 202, 202)",
-            isFolder:true, undoIgnoreFields:new List<string>(['treeViewSortCriterion']),
+            isFolder:true, undoIgnoreFields:new List<string>(['treeView_SortCriterion']),
             // childContextMenuFilters: new List<ScriptField>([dashboardFilter!,]),
             // childContextMenuScripts: new List<ScriptField>([addToDashboards!,]),
             // childContextMenuLabels: new List<string>(["Add to Dashboards",]),
             // childContextMenuIcons: new List<string>(["user-plus",]),
             "acl-Guest": SharingPermissions.Augment, "_acl-Guest": SharingPermissions.Augment,
             childDragAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
-            // NOTE: treeViewHideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
-            _layout_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
+            // NOTE: treeView_HideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
+            _layout_showTitle: "title", treeView_HideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
             layout_explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
         };
         
@@ -864,7 +864,7 @@ export class CurrentUserUtils {
         doc.userColor ?? (doc.userColor = Colors.LIGHT_GRAY);
         doc.userTheme ?? (doc.userTheme = ColorScheme.Dark);
         doc.filterDocCount = 0;
-        doc.treeViewFreezeChildren = "remove|add";
+        doc.treeView_FreezeChildren = "remove|add";
         doc.activePage = doc.activeDashboard === undefined ? 'home': doc.activePage;
         this.setupLinkDocs(doc, linkDatabaseId);
         this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 42132c2d7..9779639f4 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -315,10 +315,10 @@ export class DocumentManager {
             if (options.toggleTarget && (!options.didMove || docView.rootDoc.hidden)) docView.rootDoc.hidden = !docView.rootDoc.hidden;
             if (options.effect) docView.rootDoc[Animation] = options.effect;
 
-            if (options.zoomTextSelections && Doc.UnhighlightTimer && contextView && viewSpec.textHtml) {
+            if (options.zoomTextSelections && Doc.UnhighlightTimer && contextView && viewSpec.text_html) {
                 // if the docView is a text anchor, the contextView is the PDF/Web/Text doc
-                contextView.htmlOverlayEffect = StrCast(options?.effect?.presEffect, StrCast(options?.effect?.followLinkAnimEffect));
-                contextView.textHtmlOverlay = StrCast(targetDoc.textHtml);
+                contextView.htmlOverlayEffect = StrCast(options?.effect?.presentation_effect, StrCast(options?.effect?.followLinkAnimEffect));
+                contextView.textHtmlOverlay = StrCast(targetDoc.text_html);
                 DocumentManager._overlayViews.add(contextView);
             }
             Doc.AddUnHighlightWatcher(() => {
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 21808d6e0..3e4827c83 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -108,7 +108,14 @@ export class DashboardView extends React.Component {
                 }}>
                 <div className="header">Create New Dashboard</div>
                 <EditableText formLabel="Title" placeholder={placeholder} type={Type.SEC} color={StrCast(Doc.UserDoc().userColor)} setVal={val => this.setNewDashboardName(val as string)} fillWidth />
-                <ColorPicker formLabel="Background" colorPickerType="github" type={Type.TERT} selectedColor={this.newDashboardColor} setSelectedColor={this.setNewDashboardColor} />
+                <ColorPicker
+                    formLabel="Background" //
+                    colorPickerType="github"
+                    type={Type.TERT}
+                    selectedColor={this.newDashboardColor}
+                    setFinalColor={this.setNewDashboardColor}
+                    setSelectedColor={this.setNewDashboardColor}
+                />
                 <div className="button-bar">
                     <Button text="Cancel" color={StrCast(Doc.UserDoc().userColor)} onClick={this.abortCreateNewDashboard} />
                     <Button type={Type.TERT} text="Create" color={StrCast(Doc.UserDoc().userVariantColor)} onClick={() => this.createNewDashboard(this.newDashboardName!, this.newDashboardColor)} />
@@ -437,12 +444,12 @@ export class DashboardView extends React.Component {
             title: 'My Trails',
             _layout_showTitle: 'title',
             _height: 100,
-            treeViewHideTitle: true,
+            treeView_HideTitle: true,
             _layout_fitWidth: true,
             _gridGap: 5,
             _forceActive: true,
             childDragAction: 'embed',
-            treeViewTruncateTitleWidth: 150,
+            treeView_TruncateTitleWidth: 150,
             ignoreClick: true,
             layout_headerButton: myTrailsBtn,
             contextMenuIcons: new List<string>(['plus']),
@@ -454,7 +461,7 @@ export class DashboardView extends React.Component {
             isSystem: true,
             layout_explainer: 'All of the trails that you have created will appear here.',
         };
-        const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' });
+        const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeView_ChildDoubleClick: 'openPresentation(documentView.rootDoc)' });
         dashboardDoc.myTrails = new PrefetchProxy(myTrails);
 
         const contextMenuScripts = [reqdBtnScript.onClick];
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 6f5e9f5c0..345135a1a 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -563,10 +563,10 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         const rootDoc = rootView?.rootDoc;
         if (rootDoc) {
             const anchor = rootView.ComponentView?.getAnchor?.(false) ?? rootDoc;
-            const trail = DocCast(anchor.presTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true);
-            if (trail !== anchor.presTrail) {
+            const trail = DocCast(anchor.presentationTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true);
+            if (trail !== anchor.presentationTrail) {
                 DocUtils.MakeLink(anchor, trail, { link_relationship: 'link trail' });
-                anchor.presTrail = trail;
+                anchor.presentationTrail = trail;
             }
             Doc.ActivePresentation = trail;
             this.props.views().lastElement()?.props.addDocTab(trail, OpenWhere.replaceRight);
diff --git a/src/client/views/InkStroke.scss b/src/client/views/InkStroke.scss
index f504890a5..c672824bf 100644
--- a/src/client/views/InkStroke.scss
+++ b/src/client/views/InkStroke.scss
@@ -17,6 +17,7 @@
     display: flex;
     align-items: center;
     height: 100%;
+    width: 100%;
     transition: inherit;
     .inkStroke {
         mix-blend-mode: multiply;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 93163c1a3..b3647249a 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -91,8 +91,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         const anchor = Docs.Create.ConfigDocument({
             title: 'Ink anchor:' + this.rootDoc.title,
             // set presentation timing for restoring shape
-            presDuration: 1100,
-            presTransition: 1000,
+            presentation_duration: 1100,
+            presentation_transition: 1000,
             annotationOn: this.rootDoc,
         });
         if (anchor) {
@@ -362,7 +362,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
 
     _subContentView: DocComponentView | undefined;
     setSubContentView = (doc: DocComponentView) => (this._subContentView = doc);
-    @computed get fillColor() { 
+    @computed get fillColor() {
         const isInkMask = BoolCast(this.layoutDoc.stroke_isInkMask);
         return isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FillColor) ?? 'transparent';
     }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index dc236b603..e376c4fdf 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -156,8 +156,8 @@ export class MainView extends React.Component {
             DocServer.setLivePlaygroundFields([
                 'dataTransition',
                 'viewTransition',
-                'treeViewOpen',
-                'treeViewExpandedView',
+                'treeView_Open',
+                'treeView_ExpandedView',
                 'carousel_index',
                 'itemIndex', // for changing slides in presentations
                 'layout_sidebarWidthPercent',
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index a4a2c1df9..4aabb83dc 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -201,7 +201,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0);
         textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0);
         // mainAnnoDocProto.text = this._selectionText;
-        textRegionAnnoProto.textInlineAnnotations = new List<Doc>(annoDocs);
+        textRegionAnnoProto.text_inlineAnnotations = new List<Doc>(annoDocs);
         savedAnnoMap.clear();
         return textRegionAnno;
     };
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index b79486167..1a7c9eb64 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -26,36 +26,34 @@
         padding: 5px 10px;
     }
 
-
     .propertiesView-sharing {
         //border-bottom: 1px solid black;
         //padding: 8.5px;
 
+        .propertiesView-buttonContainer {
+            float: right;
+            display: flex;
 
-            .propertiesView-buttonContainer {
-                float: right;
-                display: flex;
-
-                button {
-                    width: 15;
-                    height: 15;
-                    padding: 0;
-                    margin-top: -5;
-                }
+            button {
+                width: 15;
+                height: 15;
+                padding: 0;
+                margin-top: -5;
             }
+        }
 
-            .change-buttons {
-                display: flex;
+        .change-buttons {
+            display: flex;
 
-                button {
-                    width: 5;
-                    height: 5;
-                }
+            button {
+                width: 5;
+                height: 5;
+            }
 
-                input {
-                    width: 100%;
-                }
+            input {
+                width: 100%;
             }
+        }
     }
 
     .propertiesView-acls-checkbox {
@@ -63,11 +61,11 @@
         margin-left: 50px;
     }
 
-    .propertiesView-shareDropDown{
+    .propertiesView-shareDropDown {
         margin-right: 10px;
         min-width: 65px;
-        
-        & .propertiesView-shareDropDownNone{
+
+        & .propertiesView-shareDropDownNone {
             padding: 0px;
             padding-left: 3px;
             padding-right: 3px;
@@ -77,7 +75,7 @@
             border: 1px solid rgb(71, 71, 71);
         }
         & .propertiesView-shareDropDownEdit,
-        .propertiesView-shareDropDownAdmin{
+        .propertiesView-shareDropDownAdmin {
             padding: 0px;
             padding-left: 3px;
             padding-right: 3px;
@@ -86,17 +84,16 @@
             border-radius: 6px;
             border: 1px solid rgb(75, 75, 5);
         }
-        & .propertiesView-shareDropDownAugment{
+        & .propertiesView-shareDropDownAugment {
             padding: 0px;
             padding-left: 3px;
             padding-right: 3px;
             background: rgb(208, 255, 208);
-            color:rgb(19, 80, 19);
+            color: rgb(19, 80, 19);
             border-radius: 6px;
             border: 1px solid rgb(19, 80, 19);
-
         }
-        & .propertiesView-shareDropDownView{
+        & .propertiesView-shareDropDownView {
             padding: 0px;
             padding-left: 3px;
             padding-right: 3px;
@@ -105,7 +102,7 @@
             border-radius: 6px;
             border: 1px solid rgb(25, 25, 101);
         }
-        & .propertiesView-shareDropDownNot-Shared{
+        & .propertiesView-shareDropDownNot-Shared {
             padding: 0px;
             padding-left: 3px;
             padding-right: 3px;
@@ -245,15 +242,12 @@
             cursor: auto;
         }
     }
+}
 
-    }
-
-    .propertiesView-presTrails {
-        //border-bottom: 1px solid black;
-        //padding: 8.5px;
-
-      
-    }
+.propertiesView-presentationTrails {
+    //border-bottom: 1px solid black;
+    //padding: 8.5px;
+}
 
 .inking-button {
     display: flex;
@@ -515,9 +509,9 @@
     }
 }
 
-.propertiesView-wordTitle{
-    color:darkslategray;
-    font-weight:200;
+.propertiesView-wordTitle {
+    color: darkslategray;
+    font-weight: 200;
 }
 
 .editable-title {
@@ -532,15 +526,15 @@
     }
 }
 
-.propertiesView-wordType{
-    color:darkslategray;
-    font-weight:200;
+.propertiesView-wordType {
+    color: darkslategray;
+    font-weight: 200;
 }
 
-.currentType{
+.currentType {
     text-decoration: underline;
     display: flex;
-    align-items:center;
+    align-items: center;
     // border: solid 1px #323232;
     // padding-left: 5px;
     // padding-top: 4px;
@@ -548,8 +542,8 @@
     // height: fit-content;
 }
 
-.currentType-icon{
-    margin-right:5px;
+.currentType-icon {
+    margin-right: 5px;
 }
 
 .properties-flyout {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 872f1c6ab..1b6037ed9 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1700,50 +1700,50 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             </div>
                         </div>
                         {!selectedItem ? null : (
-                            <div className="propertiesView-presTrails">
-                                <div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openPresTransitions = !this.openPresTransitions))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
+                            <div className="propertiesView-presentationTrails">
+                                <div className="propertiesView-presentationTrails-title" onPointerDown={action(() => (this.openPresTransitions = !this.openPresTransitions))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
                                     &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Transitions
-                                    <div className="propertiesView-presTrails-title-icon">
+                                    <div className="propertiesView-presentationTrails-title-icon">
                                         <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg" />
                                     </div>
                                 </div>
-                                {this.openPresTransitions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.transitionDropdown}</div> : null}
+                                {this.openPresTransitions ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.transitionDropdown}</div> : null}
                             </div>
                         )}
                         {!selectedItem ? null : (
-                            <div className="propertiesView-presTrails">
+                            <div className="propertiesView-presentationTrails">
                                 <div
-                                    className="propertiesView-presTrails-title"
+                                    className="propertiesView-presentationTrails-title"
                                     onPointerDown={action(() => (this.openPresVisibilityAndDuration = !this.openPresVisibilityAndDuration))}
                                     style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
                                     &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Visibilty
-                                    <div className="propertiesView-presTrails-title-icon">
+                                    <div className="propertiesView-presentationTrails-title-icon">
                                         <FontAwesomeIcon icon={this.openPresVisibilityAndDuration ? 'caret-down' : 'caret-right'} size="lg" />
                                     </div>
                                 </div>
-                                {this.openPresVisibilityAndDuration ? <div className="propertiesView-presTrails-content">{PresBox.Instance.visibiltyDurationDropdown}</div> : null}
+                                {this.openPresVisibilityAndDuration ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.visibiltyDurationDropdown}</div> : null}
                             </div>
                         )}
                         {!selectedItem ? null : (
-                            <div className="propertiesView-presTrails">
-                                <div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openPresProgressivize = !this.openPresProgressivize))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
+                            <div className="propertiesView-presentationTrails">
+                                <div className="propertiesView-presentationTrails-title" onPointerDown={action(() => (this.openPresProgressivize = !this.openPresProgressivize))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
                                     &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Progressivize
-                                    <div className="propertiesView-presTrails-title-icon">
+                                    <div className="propertiesView-presentationTrails-title-icon">
                                         <FontAwesomeIcon icon={this.openPresProgressivize ? 'caret-down' : 'caret-right'} size="lg" />
                                     </div>
                                 </div>
-                                {this.openPresProgressivize ? <div className="propertiesView-presTrails-content">{PresBox.Instance.progressivizeDropdown}</div> : null}
+                                {this.openPresProgressivize ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.progressivizeDropdown}</div> : null}
                             </div>
                         )}
                         {!selectedItem || (type !== DocumentType.VID && type !== DocumentType.AUDIO) ? null : (
-                            <div className="propertiesView-presTrails">
-                                <div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openSlideOptions = !this.openSlideOptions))} style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}>
+                            <div className="propertiesView-presentationTrails">
+                                <div className="propertiesView-presentationTrails-title" onPointerDown={action(() => (this.openSlideOptions = !this.openSlideOptions))} style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}>
                                     &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={type === DocumentType.AUDIO ? 'file-audio' : 'file-video'} /> &nbsp; {type === DocumentType.AUDIO ? 'Audio Options' : 'Video Options'}
-                                    <div className="propertiesView-presTrails-title-icon">
+                                    <div className="propertiesView-presentationTrails-title-icon">
                                         <FontAwesomeIcon icon={this.openSlideOptions ? 'caret-down' : 'caret-right'} size="lg" />
                                     </div>
                                 </div>
-                                {this.openSlideOptions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.mediaOptionsDropdown}</div> : null}
+                                {this.openSlideOptions ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.mediaOptionsDropdown}</div> : null}
                             </div>
                         )}
                     </div>
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 63ff348e3..a757a0715 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -27,8 +27,8 @@ import './StyleProvider.scss';
 import React = require('react');
 
 export enum StyleProp {
-    TreeViewIcon = 'treeViewIcon',
-    TreeViewSortings = 'treeViewSortings', // options for how to sort tree view items
+    TreeViewIcon = 'treeView_Icon',
+    TreeViewSortings = 'treeView_Sortings', // options for how to sort tree view items
     DocContents = 'docContents', // when specified, the JSX returned will replace the normal rendering of the document view
     Opacity = 'opacity', // opacity of the document view
     BoxShadow = 'boxShadow', // box shadow - used for making collections standout and for showing clusters in free form views
@@ -133,7 +133,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             return undefined;
         case StyleProp.DocContents:return undefined;
         case StyleProp.WidgetColor:return isAnnotated ? Colors.LIGHT_BLUE : darkScheme() ? 'lightgrey' : 'dimgrey';
-        case StyleProp.Opacity:    return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.textInlineAnnotations ? 0 :  Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null));
+        case StyleProp.Opacity:    return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.text_inlineAnnotations ? 0 :  Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null));
         case StyleProp.HideLinkBtn:return props?.hideLinkButton || (!selected && doc?.layout_hideLinkButton);
         case StyleProp.FontSize:   return StrCast(doc?.[fieldKey + 'fontSize'],   StrCast(doc?._text_fontSize,   StrCast(Doc.UserDoc().fontSize)));
         case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(doc?._text_fontFamily, StrCast(Doc.UserDoc().fontFamily)));
@@ -144,7 +144,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.ShowTitle:
             return (
                 (doc &&
-                    !doc.presentationTargetDoc &&
+                    !doc.presentation_targetDoc &&
                     !props?.LayoutTemplateString?.includes(KeyValueBox.name) &&
                     props?.layout_showTitle?.() !== '' &&
                     StrCast(
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 5c2ab3f70..a3884c9eb 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -102,7 +102,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         const templateMenu: Array<JSX.Element> = [];
         this.props.templates?.forEach((checked, template) => templateMenu.push(<TemplateToggle key={template} template={template} checked={checked} toggle={this.toggleTemplate} />));
         templateMenu.push(<OtherToggle key={'default'} name={'Default'} checked={templateName === 'layout'} toggle={this.toggleDefault} />);
-        addedTypes.concat(noteTypes).map(template => (template.treeViewChecked = this.templateIsUsed(firstDoc, template)));
+        addedTypes.concat(noteTypes).map(template => (template.treeView_Checked = this.templateIsUsed(firstDoc, template)));
         this._addedKeys &&
             Array.from(this._addedKeys)
                 .filter(key => !noteTypes.some(nt => nt.title === key))
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 192d48c64..a8f5345b7 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -65,9 +65,9 @@ export class CollectionTimeView extends CollectionSubView() {
     @action
     scrollPreview = (docView: DocumentView, anchor: Doc, focusSpeed: number, options: DocFocusOptions) => {
         // if in preview, then override document's fields with view spec
-        this._focusFilters = StrListCast(anchor.presDocFilters);
-        this._focusRangeFilters = StrListCast(anchor.presPinDocRangeFilters);
-        this._focusPivotField = StrCast(anchor.presPivotField);
+        this._focusFilters = StrListCast(anchor.config_docFilters);
+        this._focusRangeFilters = StrListCast(anchor.config_docRangeFilters);
+        this._focusPivotField = StrCast(anchor.config_pivotField);
         return undefined;
     };
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ea3b5065f..b5c7d3f5d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -66,17 +66,17 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         return this.props.DataDoc || this.doc;
     }
     @computed get treeViewtruncateTitleWidth() {
-        return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth());
+        return NumCast(this.doc.treeView_TruncateTitleWidth, this.panelWidth());
     }
     @computed get treeChildren() {
         TraceMobx();
         return this.props.childDocuments || this.childDocs;
     }
     @computed get outlineMode() {
-        return this.doc.treeViewType === TreeViewType.outline;
+        return this.doc.treeView_Type === TreeViewType.outline;
     }
     @computed get fileSysMode() {
-        return this.doc.treeViewType === TreeViewType.fileSystem;
+        return this.doc.treeView_Type === TreeViewType.fileSystem;
     }
     @computed get dashboardMode() {
         return this.doc === Doc.MyDashboards;
@@ -191,9 +191,9 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
         if (!Doc.noviceMode) {
             const layoutItems: ContextMenuProps[] = [];
-            layoutItems.push({ description: 'Make tree state ' + (this.doc.treeViewOpenIsTransient ? 'persistent' : 'transient'), event: () => (this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient), icon: 'paint-brush' });
-            layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? 'Show' : 'Hide') + ' Header Fields', event: () => (this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields), icon: 'paint-brush' });
-            layoutItems.push({ description: (this.doc.treeViewHideTitle ? 'Show' : 'Hide') + ' Title', event: () => (this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle), icon: 'paint-brush' });
+            layoutItems.push({ description: 'Make tree state ' + (this.doc.treeView_OpenIsTransient ? 'persistent' : 'transient'), event: () => (this.doc.treeView_OpenIsTransient = !this.doc.treeView_OpenIsTransient), icon: 'paint-brush' });
+            layoutItems.push({ description: (this.doc.treeView_HideHeaderFields ? 'Show' : 'Hide') + ' Header Fields', event: () => (this.doc.treeView_HideHeaderFields = !this.doc.treeView_HideHeaderFields), icon: 'paint-brush' });
+            layoutItems.push({ description: (this.doc.treeView_HideTitle ? 'Show' : 'Hide') + ' Title', event: () => (this.doc.treeView_HideTitle = !this.doc.treeView_HideTitle), icon: 'paint-brush' });
             ContextMenu.Instance.addItem({ description: 'Options...', subitems: layoutItems, icon: 'eye' });
             const existingOnClick = ContextMenu.Instance.findByDescription('OnClick...');
             const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
@@ -215,7 +215,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 height={'auto'}
                 GetValue={() => StrCast(this.dataDoc.title)}
                 SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
-                    if (enter && this.props.Document.treeViewType === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
+                    if (enter && this.props.Document.treeView_Type === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
                     this.dataDoc.title = value;
                     return true;
                 })}
@@ -261,7 +261,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         const icons = StrListCast(this.doc.childContextMenuIcons);
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
     };
-    headerFields = () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields);
+    headerFields = () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeView_HideHeaderFields);
     @observable _renderCount = 1;
     @computed get treeViewElements() {
         TraceMobx();
@@ -389,7 +389,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
         const color = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.Color);
         const pointerEvents = () => (this.props.isContentActive() === false ? 'none' : undefined);
-        const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar;
+        const titleBar = this.props.treeViewHideTitle || this.doc.treeView_HideTitle ? null : this.titleBar;
         return (
             <div style={{ display: 'flex', flexDirection: 'column', height: '100%', pointerEvents: 'all' }}>
                 {!this.buttonMenu && !this.noviceExplainer ? null : (
@@ -401,7 +401,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 <div
                     className="collectionTreeView-contents"
                     key="tree"
-                    ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}
+                    ref={r => !this.doc.treeView_HasOverlay && r && this.createTreeDropTarget(r)}
                     style={{
                         ...(!titleBar ? { marginLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
                         color: color(),
@@ -440,7 +440,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1) || 1;
         return (
             <div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
-                {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? (
+                {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeView_HasOverlay ? (
                     <CollectionFreeFormView
                         {...this.props}
                         setContentView={emptyFunction}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index c33519afb..88f892efc 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -41,7 +41,7 @@ interface CollectionViewProps_ extends FieldViewProps {
     isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
     layoutEngine?: () => string;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
-    setBrushViewer?: (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => void;
+    setBrushViewer?: (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => void;
     ignoreUnrendered?: boolean;
 
     // property overrides for child documents
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index d787f5262..ea473d5cf 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -256,46 +256,47 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 return;
             }
             const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false, pinProps);
-            const pinDoc = Doc.MakeDelegate(anchorDoc && anchorDoc !== doc ? anchorDoc : doc);
-            pinDoc.presentationTargetDoc = anchorDoc ?? doc;
+            const pinDoc = anchorDoc?.type === DocumentType.CONFIG ? anchorDoc : Doc.MakeDelegate(anchorDoc && anchorDoc !== doc ? anchorDoc : doc);
+            pinDoc.presentation_targetDoc = anchorDoc ?? doc;
             pinDoc.title = doc.title + ' - Slide';
             pinDoc.data = new List<Doc>(); // the children of the embedding's layout are the presentation slide children.  the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
-            pinDoc.presMovement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom;
-            pinDoc.presDuration = pinDoc.presDuration ?? 1000;
-            pinDoc.groupWithUp = false;
+            pinDoc.presentation_movement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom;
+            pinDoc.presentation_duration = pinDoc.presentation_duration ?? 1000;
+            pinDoc.presentation_groupWithUp = false;
             Doc.SetContainer(pinDoc, curPres);
             // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
-            pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
-            pinDoc.treeViewHeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling.
-            pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
-            pinDoc.treeViewFieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
-            pinDoc.treeViewExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
-            pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
+            pinDoc.treeView = ''; // not really needed, but makes key value pane look better
+            pinDoc.treeView_RenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
+            pinDoc.treeView_HeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling.
+            pinDoc.treeView_ChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
+            pinDoc.treeView_FieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
+            pinDoc.treeView_ExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
+            pinDoc.treeView_HideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
             const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], null);
 
             if (pinProps.pinViewport) PresBox.pinDocView(pinDoc, pinProps, anchorDoc ?? doc);
             if (!pinProps?.audioRange && duration !== undefined) {
                 pinDoc.mediaStart = 'manual';
                 pinDoc.mediaStop = 'manual';
-                pinDoc.presStartTime = NumCast(doc.clipStart);
-                pinDoc.presEndTime = NumCast(doc.clipEnd, duration);
+                pinDoc.config_clipStart = NumCast(doc.clipStart);
+                pinDoc.config_clipEnd = NumCast(doc.clipEnd, duration);
             }
             if (pinProps?.activeFrame !== undefined) {
-                pinDoc.presActiveFrame = pinProps?.activeFrame;
+                pinDoc.config_activeFrame = pinProps?.activeFrame;
                 pinDoc.title = doc.title + ' (move)';
-                pinDoc.presMovement = PresMovement.Pan;
+                pinDoc.presentation_movement = PresMovement.Pan;
             }
             if (pinProps?.currentFrame !== undefined) {
-                pinDoc.presCurrentFrame = pinProps?.currentFrame;
+                pinDoc.config_currentFrame = pinProps?.currentFrame;
                 pinDoc.title = doc.title + ' (move)';
-                pinDoc.presMovement = PresMovement.Pan;
+                pinDoc.presentation_movement = PresMovement.Pan;
             }
             if (pinDoc.stroke_isInkMask) {
-                pinDoc.presHideAfter = true;
-                pinDoc.presHideBefore = true;
-                pinDoc.presMovement = PresMovement.None;
+                pinDoc.presentation_hideAfter = true;
+                pinDoc.presentation_hideBefore = true;
+                pinDoc.presentation_movement = PresMovement.None;
             }
-            if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
+            if (curPres.expandBoolean) pinDoc.presentation_expandInlineButton = true;
             Doc.AddDocToList(curPres, 'data', pinDoc, PresBox.Instance?.sortArray()?.lastElement());
             PresBox.Instance?.clearSelectedArray();
             pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); //Update selected array
@@ -393,7 +394,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     };
 
     getCurrentFrame = () => {
-        return NumCast(Cast(PresBox.Instance.activeItem.presentationTargetDoc, Doc, null)._currentFrame);
+        return NumCast(Cast(PresBox.Instance.activeItem.presentation_targetDoc, Doc, null)._currentFrame);
     };
     static Activate = (tabDoc: Doc) => {
         const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(tab => tab.DashDoc === tabDoc && !tab.contentItem.config.props.keyValue);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 25a547066..27f9ebc49 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -87,8 +87,8 @@ export enum TreeSort {
  * Renders a treeView of a collection of documents
  *
  * special fields:
- * treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden
- * treeViewExpandedView : name of field whose contents are being displayed as the document's subtree
+ * treeView_Open : flag denoting whether the documents sub-tree (contents) is visible or hidden
+ * treeView_ExpandedView : name of field whose contents are being displayed as the document's subtree
  */
 @observer
 export class TreeView extends React.Component<TreeViewProps> {
@@ -104,16 +104,16 @@ export class TreeView extends React.Component<TreeViewProps> {
     private _treedropDisposer?: DragManager.DragDropDisposer;
 
     get treeViewOpenIsTransient() {
-        return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsDataProto(this.doc);
+        return this.props.treeView.doc.treeView_OpenIsTransient || Doc.IsDataProto(this.doc);
     }
     set treeViewOpen(c: boolean) {
         if (this.treeViewOpenIsTransient) this._transientOpenState = c;
         else {
-            this.doc.treeViewOpen = c;
+            this.doc.treeView_Open = c;
             this._transientOpenState = false;
         }
     }
-    @observable _transientOpenState = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
+    @observable _transientOpenState = false; // override of the treeView_Open field allowing the display state to be independent of the document's state
     @observable _editTitle: boolean = false;
     @observable _dref: DocumentView | undefined | null;
     get displayName() {
@@ -132,29 +132,29 @@ export class TreeView extends React.Component<TreeViewProps> {
             ? this.fieldKey
             : Doc.noviceMode
             ? 'layout'
-            : StrCast(this.props.treeView.doc.treeViewExpandedView, 'fields');
+            : StrCast(this.props.treeView.doc.treeView_ExpandedView, 'fields');
     }
 
     @computed get doc() {
         return this.props.document;
     }
     @computed get treeViewOpen() {
-        return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, 'treeViewOpen', 'boolean', true)) || this._transientOpenState;
+        return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, 'treeView_Open', 'boolean', true)) || this._transientOpenState;
     }
     @computed get treeViewExpandedView() {
-        return this.validExpandViewTypes.includes(StrCast(this.doc.treeViewExpandedView)) ? StrCast(this.doc.treeViewExpandedView) : this.defaultExpandedView;
+        return this.validExpandViewTypes.includes(StrCast(this.doc.treeView_ExpandedView)) ? StrCast(this.doc.treeView_ExpandedView) : this.defaultExpandedView;
     }
     @computed get MAX_EMBED_HEIGHT() {
         return NumCast(this.props.treeViewParent.maxEmbedHeight, 200);
     }
     @computed get dataDoc() {
-        return this.props.document.treeViewChildrenOnRoot ? this.doc : this.doc[DocData];
+        return this.props.document.treeView_ChildrenOnRoot ? this.doc : this.doc[DocData];
     }
     @computed get layoutDoc() {
         return Doc.Layout(this.doc);
     }
     @computed get fieldKey() {
-        return StrCast(this.doc._treeViewFieldKey, Doc.LayoutFieldKey(this.doc));
+        return StrCast(this.doc._treeView_FieldKey, Doc.LayoutFieldKey(this.doc));
     }
     @computed get childDocs() {
         return this.childDocList(this.fieldKey);
@@ -308,13 +308,13 @@ export class TreeView extends React.Component<TreeViewProps> {
         const bullet = Docs.Create.TextDocument('', {
             layout: CollectionView.LayoutString('data'),
             title: '-title-',
-            treeViewExpandedViewLock: true,
-            treeViewExpandedView: 'data',
+            treeView_ExpandedViewLock: true,
+            treeView_ExpandedView: 'data',
             _type_collection: CollectionViewType.Tree,
             layout_hideLinkButton: true,
             _layout_showSidebar: true,
             _layout_fitWidth: true,
-            treeViewType: TreeViewType.outline,
+            treeView_Type: TreeViewType.outline,
             x: 0,
             y: 0,
             _xMargin: 0,
@@ -395,7 +395,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         };
         const addDoc = inside ? localAdd : parentAddDoc;
         const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same') && moveDocument;
-        const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeViewFreezeChildren).includes('add')) || forceAdd;
+        const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeView_FreezeChildren).includes('add')) || forceAdd;
         if (canAdd) {
             this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true);
             const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false);
@@ -438,7 +438,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
 
         for (const key of Object.keys(ids).slice().sort()) {
-            if (this.props.skipFields?.includes(key) || key === 'title' || key === 'treeViewOpen') continue;
+            if (this.props.skipFields?.includes(key) || key === 'title' || key === 'treeView_Open') continue;
             const contents = doc[key];
             let contentElement: (JSX.Element | null)[] | JSX.Element = [];
 
@@ -538,7 +538,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const expandKey = this.treeViewExpandedView;
         const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; icon: JSX.Element | string } }) ?? {};
         if (['links', 'annotations', 'embeddings', this.fieldKey].includes(expandKey)) {
-            const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
+            const sorting = StrCast(this.doc.treeView_SortCriterion, TreeSort.None);
             const sortKeys = Object.keys(sortings);
             const curSortIndex = Math.max(
                 0,
@@ -550,7 +550,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
                 // if there's a sort ordering specified that can be modified on drop (eg, zorder can be modified, alphabetical can't),
                 // then the modification would be done here
-                const ordering = StrCast(this.doc.treeViewSortCriterion);
+                const ordering = StrCast(this.doc.treeView_SortCriterion);
                 if (ordering === TreeSort.Zindex) {
                     const docs = TreeView.sortDocs(this.childDocs || ([] as Doc[]), ordering);
                     doc.zIndex = addBefore ? NumCast(addBefore.zIndex) + (before ? -0.5 : 0.5) : 1000;
@@ -590,7 +590,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                                 }}
                                 onClick={undoable(e => {
                                     if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
-                                        !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
+                                        !this.props.treeView.outlineMode && (this.doc.treeView_SortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
                                         e.stopPropagation();
                                     }
                                 }, 'sort order')}
@@ -601,7 +601,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         style={{ cursor: 'inherit' }}
                         key={expandKey + 'more'}
                         title="click to change sort order"
-                        className={''} //this.doc.treeViewHideTitle ? 'no-indent' : ''}
+                        className={''} //this.doc.treeView_HideTitle ? 'no-indent' : ''}
                         onPointerDown={e => {
                             downX = e.clientX;
                             downY = e.clientY;
@@ -609,7 +609,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         }}
                         onClick={undoable(e => {
                             if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
-                                !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
+                                !this.props.treeView.outlineMode && (this.doc.treeView_SortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
                                 e.stopPropagation();
                             }
                         }, 'sort order')}>
@@ -683,7 +683,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 {
                     this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
                     heading: this.props.treeViewParent.title,
-                    checked: this.doc.treeViewChecked === 'check' ? 'x' : this.doc.treeViewChecked === 'x' ? 'remove' : 'check',
+                    checked: this.doc.treeView_Checked === 'check' ? 'x' : this.doc.treeView_Checked === 'x' ? 'remove' : 'check',
                     containingTreeView: this.props.treeView.props.Document,
                 },
                 console.log
@@ -698,7 +698,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         TraceMobx();
         const iconType = this.props.treeView.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ':open' : !this.childDocs.length ? ':empty' : '')) || 'question';
         const color = StrCast(Doc.UserDoc().userColor);
-        const checked = this.onCheckedClick ? this.doc.treeViewChecked ?? 'unchecked' : undefined;
+        const checked = this.onCheckedClick ? this.doc.treeView_Checked ?? 'unchecked' : undefined;
         return (
             <div
                 className={`bullet${this.props.treeView.outlineMode ? '-outline' : ''}`}
@@ -748,9 +748,9 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
     @action
     expandNextviewType = () => {
-        if (this.treeViewOpen && !this.doc.isFolder && !this.props.treeView.outlineMode && !this.doc.treeViewExpandedViewLock) {
+        if (this.treeViewOpen && !this.doc.isFolder && !this.props.treeView.outlineMode && !this.doc.treeView_ExpandedViewLock) {
             const next = (modes: any[]) => modes[(modes.indexOf(StrCast(this.treeViewExpandedView)) + 1) % modes.length];
-            this.doc.treeViewExpandedView = next(this.validExpandViewTypes);
+            this.doc.treeView_ExpandedView = next(this.validExpandViewTypes);
         }
         this.treeViewOpen = true;
     };
@@ -759,7 +759,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get titleButtons() {
         const customHeaderButtons = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Decorations);
         const color = StrCast(Doc.UserDoc().userColor);
-        return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? null : (
+        return this.props.treeViewHideHeaderFields() || this.doc.treeView_HideHeaderFields ? null : (
             <>
                 {customHeaderButtons} {/* e.g.,. hide button is set by dashboardStyleProvider */}
                 <IconButton
@@ -771,7 +771,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         e.stopPropagation();
                     }}
                 />
-                {Doc.noviceMode ? null : this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? null : (
+                {Doc.noviceMode ? null : this.doc.treeView_ExpandedViewLock || Doc.IsSystem(this.doc) ? null : (
                     <span className="collectionTreeView-keyHeader" title="type of expanded data" key={this.treeViewExpandedView} onPointerDown={this.expandNextviewType}>
                         {this.treeViewExpandedView}
                     </span>
@@ -815,7 +815,7 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!);
 
-    onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick, !this.props.treeView.outlineMode ? this._openScript?.() : null);
+    onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeView_ChildDoubleClick, !this.props.treeView.outlineMode ? this._openScript?.() : null);
 
     refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document, {});
     ignoreEvent = (e: any) => {
@@ -861,7 +861,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
     };
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
-        if (this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode) {
+        if (this.doc.treeView_HideHeader || (this.doc.treeView_HideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode) {
             switch (e.key) {
                 case 'Tab':
                     e.stopPropagation?.();
@@ -1068,7 +1068,7 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     // renders the text version of a document as the header.  This is used in the file system mode and in other vanilla tree views.
     @computed get renderTitleAsHeader() {
-        return this.props.treeView.Document.treeViewHideUnrendered && this.doc.layout_unrendered && !this.doc.treeViewFieldKey ? (
+        return this.props.treeView.Document.treeView_HideUnrendered && this.doc.layout_unrendered && !this.doc.treeView_FieldKey ? (
             <div></div>
         ) : (
             <>
@@ -1089,7 +1089,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     };
 
     @computed get renderBorder() {
-        const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
+        const sorting = StrCast(this.doc.treeView_SortCriterion, TreeSort.None);
         const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) ?? {}) as { [key: string]: { color: string; label: string } };
         return (
             <div className={`treeView-border${this.props.treeView.outlineMode ? TreeViewType.outline : ''}`} style={{ borderColor: sortings[sorting]?.color }}>
@@ -1109,7 +1109,7 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     render() {
         TraceMobx();
-        const hideTitle = this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode;
+        const hideTitle = this.doc.treeView_HideHeader || (this.doc.treeView_HideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode;
         return this.props.renderedIds?.indexOf(this.doc[Id]) !== -1 ? (
             '<' + this.doc.title + '>' // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
         ) : (
@@ -1121,9 +1121,9 @@ export class TreeView extends React.Component<TreeViewProps> {
                 // onKeyDown={this.onKeyDown}
             >
                 <li className="collection-child">
-                    {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeViewRenderAsBulletHeader // should test for prop 'treeViewRenderDocWithBulletAsHeader"
+                    {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeView_RenderAsBulletHeader // should test for prop 'treeView_RenderDocWithBulletAsHeader"
                         ? this.renderEmbeddedDocument(false, returnFalse)
-                        : this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.doc.treeViewRenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
+                        : this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.doc.treeView_RenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
                 </li>
             </div>
         );
@@ -1162,7 +1162,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         childDocs: Doc[],
         treeView: CollectionTreeView,
         parentTreeView: CollectionTreeView | TreeView | undefined,
-        treeViewParent: Doc,
+        treeView_Parent: Doc,
         dataDoc: Doc | undefined,
         parentCollectionDoc: Doc | undefined,
         containerPrevSibling: Doc | undefined,
@@ -1176,7 +1176,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         isContentActive: (outsideReaction?: boolean) => boolean,
         panelWidth: () => number,
         renderDepth: number,
-        treeViewHideHeaderFields: () => boolean,
+        treeView_HideHeaderFields: () => boolean,
         renderedIds: string[],
         onCheckedClick: undefined | (() => ScriptField),
         onChildClick: undefined | (() => ScriptField),
@@ -1193,19 +1193,19 @@ export class TreeView extends React.Component<TreeViewProps> {
         hierarchyIndex?: number[],
         renderCount?: number
     ) {
-        const viewSpecScript = Cast(treeViewParent.viewSpecScript, ScriptField);
+        const viewSpecScript = Cast(treeView_Parent.viewSpecScript, ScriptField);
         if (viewSpecScript) {
             childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
         }
 
-        const docs = TreeView.sortDocs(childDocs, StrCast(treeViewParent.treeViewSortCriterion, TreeSort.None));
+        const docs = TreeView.sortDocs(childDocs, StrCast(treeView_Parent.treeView_SortCriterion, TreeSort.None));
         const rowWidth = () => panelWidth() - treeBulletWidth() * (treeView.props.NativeDimScaling?.() || 1);
-        const treeViewRefs = new Map<Doc, TreeView | undefined>();
+        const treeView_Refs = new Map<Doc, TreeView | undefined>();
         return docs
             .filter(child => child instanceof Doc)
             .map((child, i) => {
                 if (renderCount && i > renderCount) return null;
-                const pair = Doc.GetLayoutDataDocPair(treeViewParent, dataDoc, child);
+                const pair = Doc.GetLayoutDataDocPair(treeView_Parent, dataDoc, child);
                 if (!pair.layout || pair.data instanceof Promise) {
                     return null;
                 }
@@ -1218,11 +1218,11 @@ export class TreeView extends React.Component<TreeViewProps> {
                         FormattedTextBox.SelectOnLoad = child[Id];
                         TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
                         Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
-                        newParent.treeViewOpen = true;
+                        newParent.treeView_Open = true;
                         Doc.SetContainer(child, treeView.Document);
                     }
                 };
-                const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
+                const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeView_Refs.get(docs[i - 1]));
                 const outdent = !parentCollectionDoc ? undefined : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined);
                 const addDocument = (doc: Doc | Doc[], annotationKey?: string, relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
                 const childLayout = Doc.Layout(pair.layout);
@@ -1233,10 +1233,10 @@ export class TreeView extends React.Component<TreeViewProps> {
                 return (
                     <TreeView
                         key={child[Id]}
-                        ref={r => treeViewRefs.set(child, r ? r : undefined)}
+                        ref={r => treeView_Refs.set(child, r ? r : undefined)}
                         document={pair.layout}
                         dataDoc={pair.data}
-                        treeViewParent={treeViewParent}
+                        treeViewParent={treeView_Parent}
                         prevSibling={docs[i]}
                         // TODO: [AL] add these
                         hierarchyIndex={hierarchyIndex ? [...hierarchyIndex, i + 1] : undefined}
@@ -1248,7 +1248,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         onCheckedClick={onCheckedClick}
                         onChildClick={onChildClick}
                         renderDepth={renderDepth}
-                        removeDoc={StrCast(treeViewParent.treeViewFreezeChildren).includes('remove') ? undefined : remove}
+                        removeDoc={StrCast(treeView_Parent.treeView_FreezeChildren).includes('remove') ? undefined : remove}
                         addDocument={addDocument}
                         styleProvider={styleProvider}
                         panelWidth={rowWidth}
@@ -1259,7 +1259,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         addDocTab={addDocTab}
                         ScreenToLocalTransform={screenToLocalXf}
                         isContentActive={isContentActive}
-                        treeViewHideHeaderFields={treeViewHideHeaderFields}
+                        treeViewHideHeaderFields={treeView_HideHeaderFields}
                         renderedIds={renderedIds}
                         skipFields={skipFields}
                         firstLevel={firstLevel}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index c6419885b..e4ae251c8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -153,7 +153,12 @@
     overflow-y: auto;
     overflow-x: hidden;
 }
-
+.collectionFreeFormView-brushView {
+    pointer-events: none;
+    position: absolute;
+    transition: opacity 0.5s;
+    z-index: 1000;
+}
 .collectionfreeformview-container {
     // touch action none means that the browser will handle none of the touch actions. this allows us to implement our own actions.
     touch-action: none;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e1455525e..ffcf0999c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -319,7 +319,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         // focus on the document in the collection
         const didMove = !cantTransform && !anchor.z && (panX !== savedState.panX || panY !== savedState.panY || scale !== savedState.scale);
         if (didMove) options.didMove = true;
-        // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
+        // glr: freeform transform speed can be set by adjusting presentation_transition field - needs a way of knowing when presentation is not active...
         if (didMove) {
             const focusTime = options?.instant ? 0 : options.zoomTime ?? 500;
             (options.zoomScale ?? options.willZoomCentered) && scale && (this.Document[this.scaleFieldKey] = scale);
@@ -681,6 +681,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case GestureUtils.Gestures.Stroke:
                 const points = ge.points;
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
+                console.log(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
                 const inkDoc = Docs.Create.InkDocument(
                     ActiveInkColor(),
                     ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale,
@@ -1050,7 +1051,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     onPointerWheel = (e: React.WheelEvent): void => {
         if (this.Document._isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom
         PresBox.Instance?.pauseAutoPres();
-        if (this.layoutDoc._Transform || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
+        if (this.layoutDoc._Transform || this.props.Document.treeView_OutlineMode === TreeViewType.outline) return;
         e.stopPropagation();
         const docHeight = NumCast(this.rootDoc[Doc.LayoutFieldKey(this.rootDoc) + '_nativeHeight'], this.nativeHeight);
         const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this.props.PanelHeight() / this.nativeDimScaling + 1e-4;
@@ -1318,7 +1319,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 layout_showTitle={this.props.childlayout_showTitle}
                 dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
                 pointerEvents={this.pointerEvents}
-                //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.treeViewFreezeChildDimensions)} // bcz: check this
+                //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.treeView_FreezeChildDimensions)} // bcz: check this
             />
         );
     }
@@ -1537,7 +1538,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         // create an anchor that saves information about the current state of the freeform view (pan, zoom, view type)
-        const anchor = Docs.Create.ConfigDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._type_collection), layout_unrendered: true, presTransition: 500, annotationOn: this.rootDoc });
+        const anchor = Docs.Create.ConfigDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._type_collection), layout_unrendered: true, presentation_transition: 500, annotationOn: this.rootDoc });
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: !this.Document._isGroup, type_collection: true, filters: true } }, this.rootDoc);
 
         if (addAsAnnotation) {
@@ -1866,6 +1867,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     showPresPaths = () => (CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.getPaths(this.rootDoc) : null);
 
+    brushedView = () => this._brushedView;
     @computed get marqueeView() {
         TraceMobx();
         return (
@@ -1908,13 +1910,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         </div>
                     ) : null}
                     <CollectionFreeFormViewPannableContents
-                        brushView={this._brushedView}
+                        brushedView={this.brushedView}
                         isAnnotationOverlay={this.isAnnotationOverlay}
                         isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
                         transform={this.contentTransform}
                         zoomScaling={this.zoomScaling}
                         presPaths={this.showPresPaths}
-                        presPinView={BoolCast(this.Document.presPinView)}
+                        presPinView={BoolCast(this.Document.config_pinView)}
                         transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.props.DocumentView?.()?.rootDoc._viewTransition, 'string', null))}
                         viewDefDivClick={this.props.viewDefDivClick}>
                         {this.children}
@@ -1945,19 +1947,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
 
     @action
-    brushView = (viewport: { width: number; height: number; panX: number; panY: number }) => {
-        this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2, opacity: 1 };
+    brushView = (viewport: { width: number; height: number; panX: number; panY: number }, transTime: number) => {
         this._brushtimer1 && clearTimeout(this._brushtimer1);
         this._brushtimer && clearTimeout(this._brushtimer);
+        this._brushedView = { width: 0, height: 0, panX: 0, panY: 0, opacity: 0 };
         this._brushtimer1 = setTimeout(
             action(() => {
-                this._brushedView.opacity = 0;
+                this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2, opacity: 1 };
                 this._brushtimer = setTimeout(
-                    action(() => (this._brushedView = { width: 0, height: 0, panX: 0, panY: 0, opacity: 0 })),
-                    500
+                    action(() => (this._brushedView.opacity = 0)),
+                    2500
                 );
             }),
-            1000
+            transTime + 1
         );
     };
     lightboxPanelWidth = () => Math.max(0, this.props.PanelWidth() - 30);
@@ -2084,7 +2086,7 @@ interface CollectionFreeFormViewPannableContentsProps {
     presPinView?: boolean;
     isAnnotationOverlay: boolean | undefined;
     isAnnotationOverlayScrollable: boolean | undefined;
-    brushView: { panX: number; panY: number; width: number; height: number; opacity: number };
+    brushedView: () => { panX: number; panY: number; width: number; height: number; opacity: number };
 }
 
 @observer
@@ -2156,6 +2158,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
     }
 
     render() {
+        const brushedView = this.props.brushedView();
         return (
             <div
                 className={'collectionfreeformview' + (this.props.viewDefDivClick ? '-viewDef' : '-none')}
@@ -2173,21 +2176,18 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
                     //willChange: "transform"
                 }}>
                 {this.props.children}
-                {!this.props.brushView.width ? null : (
+                {
                     <div
                         className="collectionFreeFormView-brushView"
                         style={{
-                            zIndex: 1000,
-                            opacity: this.props.brushView.opacity,
-                            border: 'orange solid 2px',
-                            position: 'absolute',
-                            transform: `translate(${this.props.brushView.panX}px, ${this.props.brushView.panY}px)`,
-                            width: this.props.brushView.width,
-                            height: this.props.brushView.height,
-                            transition: 'opacity 2s',
+                            opacity: brushedView.opacity,
+                            transform: `translate(${brushedView.panX}px, ${brushedView.panY}px)`,
+                            width: brushedView.width,
+                            height: brushedView.height,
+                            border: `orange solid ${brushedView.width * 0.005}px`,
                         }}
                     />
-                )}
+                }
                 {this.presPaths}
             </div>
         );
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 737d675aa..a81707ea4 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -84,7 +84,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             },
             emptyFunction,
             action(() => {
-                const trail = DocCast(this.props.docView.rootDoc.presTrail);
+                const trail = DocCast(this.props.docView.rootDoc.presentationTrail);
                 if (trail) {
                     Doc.ActivePresentation = trail;
                     DocumentViewInternal.addDocTabFunc(trail, OpenWhere.replaceRight);
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 0cc73f32f..059c39c68 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -20,7 +20,7 @@ export enum DataVizView {
     TABLE = 'table',
     LINECHART = 'lineChart',
     HISTOGRAM = 'histogram',
-    PIECHART = 'pieChart'
+    PIECHART = 'pieChart',
 }
 
 @observer
@@ -39,20 +39,22 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
 
     // current displayed chart type
     @computed get dataVizView(): DataVizView {
-        return StrCast(this.layoutDoc._dataVizView, 'table') as DataVizView;
+        return StrCast(this.layoutDoc._dataViz, 'table') as DataVizView;
     }
 
     @action // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
     restoreView = (data: Doc) => {
-        const changedView = this.dataVizView !== data.presDataVizView && (this.layoutDoc._dataVizView = data.presDataVizView);
-        const changedAxes = this.axes.join('') !== StrListCast(data.presDataVizAxes).join('') && (this.layoutDoc._data_vizAxes = new List<string>(StrListCast(data.presDataVizAxes)));
-        this.layoutDoc.selected = Field.Copy(data.selected);
+        const changedView = this.dataVizView !== data.config_dataViz && (this.layoutDoc._dataViz = data.config_dataViz);
+        const changedAxes = this.axes.join('') !== StrListCast(data.config_dataVizAxes).join('') && (this.layoutDoc._dataViz_axes = new List<string>(StrListCast(data.config_dataVizAxes)));
+        this.layoutDoc.dataViz_selectedRows = Field.Copy(data.dataViz_selectedRows);
         this.layoutDoc.histogramBarColors = Field.Copy(data.histogramBarColors);
         this.layoutDoc.defaultHistogramColor = data.defaultHistogramColor;
         this.layoutDoc.pieSliceColors = Field.Copy(data.pieSliceColors);
         Object.keys(this.layoutDoc).map(key => {
-            if (key.startsWith('histogram-title') || key.startsWith('lineChart-title') || key.startsWith('pieChart-title')){ this.layoutDoc['_'+key] = data[key]; }
-        })
+            if (key.startsWith('histogram_title') || key.startsWith('lineChart_title') || key.startsWith('pieChart_title')) {
+                this.layoutDoc['_' + key] = data[key];
+            }
+        });
         const func = () => this._chartRenderer?.restoreView(data);
         if (changedView || changedAxes) {
             setTimeout(func, 100);
@@ -69,23 +71,25 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                   // this is for when we want the whole doc (so when the chartBox getAnchor returns without a marker)
                   /*put in some options*/
               });
-        anchor.presDataVizView = this.dataVizView;
-        anchor.presDataVizAxes = this.axes.length ? new List<string>(this.axes) : undefined;
-        anchor.selected = Field.Copy(this.layoutDoc.selected);
+        anchor.config_dataViz = this.dataVizView;
+        anchor.config_dataVizAxes = this.axes.length ? new List<string>(this.axes) : undefined;
+        anchor.dataViz_selectedRows = Field.Copy(this.layoutDoc.dataViz_selectedRows);
         anchor.histogramBarColors = Field.Copy(this.layoutDoc.histogramBarColors);
         anchor.defaultHistogramColor = this.layoutDoc.defaultHistogramColor;
         anchor.pieSliceColors = Field.Copy(this.layoutDoc.pieSliceColors);
         Object.keys(this.layoutDoc).map(key => {
-            if (key.startsWith('histogram-title') || key.startsWith('lineChart-title') || key.startsWith('pieChart-title')){ anchor[key] = this.layoutDoc[key]; }
-        })
+            if (key.startsWith('histogram_title') || key.startsWith('lineChart_title') || key.startsWith('pieChart_title')) {
+                anchor[key] = this.layoutDoc[key];
+            }
+        });
         this.addDocument(anchor);
         return anchor;
     };
 
     @computed.struct get axes() {
-        return StrListCast(this.layoutDoc.data_vizAxes);
+        return StrListCast(this.layoutDoc.dataViz_axes);
     }
-    selectAxes = (axes: string[]) => (this.layoutDoc.data_vizAxes = new List<string>(axes));
+    selectAxes = (axes: string[]) => (this.layoutDoc.dataViz_axes = new List<string>(axes));
 
     // toggles for user to decide which chart type to view the data in
     @computed get selectView() {
@@ -94,10 +98,53 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         const margin = { top: 10, right: 25, bottom: 75, left: 45 };
         if (!this.pairs) return 'no data';
         switch (this.dataVizView) {
-            case DataVizView.TABLE:     return <TableBox layoutDoc={this.layoutDoc} pairs={this.pairs} axes={this.axes} height={height} width={width} margin={margin} rootDoc={this.rootDoc} docView={this.props.DocumentView} selectAxes={this.selectAxes}/>;
-            case DataVizView.LINECHART: return <LineChart layoutDoc={this.layoutDoc} ref={r => (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />;
-            case DataVizView.HISTOGRAM: return <Histogram layoutDoc={this.layoutDoc} ref={r => (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />;
-            case DataVizView.PIECHART: return <PieChart layoutDoc={this.layoutDoc} ref={r => (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />;
+            case DataVizView.TABLE:
+                return <TableBox layoutDoc={this.layoutDoc} pairs={this.pairs} axes={this.axes} height={height} width={width} margin={margin} rootDoc={this.rootDoc} docView={this.props.DocumentView} selectAxes={this.selectAxes} />;
+            case DataVizView.LINECHART:
+                return (
+                    <LineChart
+                        layoutDoc={this.layoutDoc}
+                        ref={r => (this._chartRenderer = r ?? undefined)}
+                        height={height}
+                        width={width}
+                        fieldKey={this.fieldKey}
+                        margin={margin}
+                        rootDoc={this.rootDoc}
+                        axes={this.axes}
+                        pairs={this.pairs}
+                        dataDoc={this.dataDoc}
+                    />
+                );
+            case DataVizView.HISTOGRAM:
+                return (
+                    <Histogram
+                        layoutDoc={this.layoutDoc}
+                        ref={r => (this._chartRenderer = r ?? undefined)}
+                        height={height}
+                        width={width}
+                        fieldKey={this.fieldKey}
+                        margin={margin}
+                        rootDoc={this.rootDoc}
+                        axes={this.axes}
+                        pairs={this.pairs}
+                        dataDoc={this.dataDoc}
+                    />
+                );
+            case DataVizView.PIECHART:
+                return (
+                    <PieChart
+                        layoutDoc={this.layoutDoc}
+                        ref={r => (this._chartRenderer = r ?? undefined)}
+                        height={height}
+                        width={width}
+                        fieldKey={this.fieldKey}
+                        margin={margin}
+                        rootDoc={this.rootDoc}
+                        axes={this.axes}
+                        pairs={this.pairs}
+                        dataDoc={this.dataDoc}
+                    />
+                );
         }
     }
 
@@ -118,14 +165,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     }
 
     render() {
-        if (!this.layoutDoc._dataVizView) this.layoutDoc._dataVizView = this.dataVizView;
+        if (!this.layoutDoc._dataViz) this.layoutDoc._dataViz = this.dataVizView;
         return !this.pairs?.length ? (
             // displays how to get data into the DataVizBox if its empty
-            <div className="start-message">
-                To create a DataViz box, either import / drag a CSV file into your canvas
-                or copy a data table and use the command 'ctrl + p' to bring the data table
-                to your canvas.
-            </div>
+            <div className="start-message">To create a DataViz box, either import / drag a CSV file into your canvas or copy a data table and use the command 'ctrl + p' to bring the data table to your canvas.</div>
         ) : (
             <div
                 className="dataViz"
@@ -141,22 +184,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                     )
                 }>
                 <div className={'datatype-button'}>
-                    <Toggle text={"TABLE"} toggleType={ToggleType.BUTTON} type={Type.SEC} color={"black"} 
-                        onClick={e => this.layoutDoc._dataVizView = DataVizView.TABLE} 
-                        toggleStatus={this.layoutDoc._dataVizView == DataVizView.TABLE} 
-                    />
-                    <Toggle text={"LINECHART"} toggleType={ToggleType.BUTTON} type={Type.SEC} color={"black"} 
-                        onClick={e => this.layoutDoc._dataVizView = DataVizView.LINECHART} 
-                        toggleStatus={this.layoutDoc._dataVizView == DataVizView.LINECHART} 
-                    />
-                    <Toggle text={"HISTOGRAM"} toggleType={ToggleType.BUTTON} type={Type.SEC} color={"black"} 
-                        onClick={e => this.layoutDoc._dataVizView = DataVizView.HISTOGRAM} 
-                        toggleStatus={this.layoutDoc._dataVizView == DataVizView.HISTOGRAM} 
-                    />
-                    <Toggle text={"PIE CHART"} toggleType={ToggleType.BUTTON} type={Type.SEC} color={"black"} 
-                        onClick={e => this.layoutDoc._dataVizView = DataVizView.PIECHART} 
-                        toggleStatus={this.layoutDoc._dataVizView == DataVizView.PIECHART} 
-                    />
+                    <Toggle text={'TABLE'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.TABLE)} toggleStatus={this.layoutDoc._dataViz == DataVizView.TABLE} />
+                    <Toggle text={'LINECHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.LINECHART)} toggleStatus={this.layoutDoc._dataViz == DataVizView.LINECHART} />
+                    <Toggle text={'HISTOGRAM'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.HISTOGRAM)} toggleStatus={this.layoutDoc._dataViz == DataVizView.HISTOGRAM} />
+                    <Toggle text={'PIE CHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.PIECHART)} toggleStatus={this.layoutDoc._dataViz == DataVizView.PIECHART} />
                 </div>
                 {this.selectView}
             </div>
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index df6aac6bc..086db2a47 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -1,20 +1,20 @@
-import { observer } from "mobx-react";
-import { Doc, StrListCast } from "../../../../../fields/Doc";
+import { observer } from 'mobx-react';
+import { Doc, StrListCast } from '../../../../../fields/Doc';
 import * as React from 'react';
 import * as d3 from 'd3';
-import { IReactionDisposer, action, computed, observable, reaction } from "mobx";
-import { LinkManager } from "../../../../util/LinkManager";
-import { Cast, DocCast, StrCast} from "../../../../../fields/Types";
-import { PinProps, PresBox } from "../../trails";
-import { Docs } from "../../../../documents/Documents";
-import { List } from "../../../../../fields/List";
+import { IReactionDisposer, action, computed, observable, reaction } from 'mobx';
+import { LinkManager } from '../../../../util/LinkManager';
+import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
+import { PinProps, PresBox } from '../../trails';
+import { Docs } from '../../../../documents/Documents';
+import { List } from '../../../../../fields/List';
 import './Chart.scss';
-import { ColorPicker, EditableText, IconButton, Size, Type } from "browndash-components";
-import { FaFillDrip } from "react-icons/fa";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { listSpec } from "../../../../../fields/Schema";
-import { scaleCreatorNumerical, yAxisCreator } from "../utils/D3Utils";
-import { undoBatch, undoable } from "../../../../util/UndoManager";
+import { ColorPicker, EditableText, IconButton, Size, Type } from 'browndash-components';
+import { FaFillDrip } from 'react-icons/fa';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { listSpec } from '../../../../../fields/Schema';
+import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils';
+import { undoBatch, undoable } from '../../../../util/UndoManager';
 
 export interface HistogramProps {
     rootDoc: Doc;
@@ -35,7 +35,6 @@ export interface HistogramProps {
 
 @observer
 export class Histogram extends React.Component<HistogramProps> {
-
     private _disposers: { [key: string]: IReactionDisposer } = {};
     private _histogramRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _histogramSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
@@ -49,32 +48,37 @@ export class Histogram extends React.Component<HistogramProps> {
 
     // filters all data to just display selected data if brushed (created from an incoming link)
     @computed get _histogramData() {
-        var guids = StrListCast(this.props.layoutDoc.rowGuids);
+        var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
         if (this.props.axes.length < 1) return [];
         if (this.props.axes.length < 2) {
             var ax0 = this.props.axes[0];
-            if (/\d/.test(this.props.pairs[0][ax0])){ this.numericalXData = true }
+            if (/\d/.test(this.props.pairs[0][ax0])) {
+                this.numericalXData = true;
+            }
             return this.props.pairs
-                ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
-                .map(pair => ({ [ax0]: (pair[this.props.axes[0]])}))
-        };
+                ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
+                .map(pair => ({ [ax0]: pair[this.props.axes[0]] }));
+        }
         var ax0 = this.props.axes[0];
         var ax1 = this.props.axes[1];
-        if (/\d/.test(this.props.pairs[0][ax0])) { this.numericalXData = true;}
-        if (/\d/.test(this.props.pairs[0][ax1]) ) { this.numericalYData = true;}
+        if (/\d/.test(this.props.pairs[0][ax0])) {
+            this.numericalXData = true;
+        }
+        if (/\d/.test(this.props.pairs[0][ax1])) {
+            this.numericalYData = true;
+        }
         return this.props.pairs
-            ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
-            .map(pair => ({ [ax0]: (pair[this.props.axes[0]]), [ax1]: (pair[this.props.axes[1]]) }))
+            ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
+            .map(pair => ({ [ax0]: pair[this.props.axes[0]], [ax1]: pair[this.props.axes[1]] }));
     }
 
-    @computed get defaultGraphTitle(){
+    @computed get defaultGraphTitle() {
         var ax0 = this.props.axes[0];
-        var ax1 = (this.props.axes.length>1)? this.props.axes[1] : undefined;
-        if (this.props.axes.length<2 || !ax1 || !/\d/.test(this.props.pairs[0][ax1]) || !this.numericalYData){
-            return ax0 + " Histogram";
-        }
-        else return ax0 + " by " + ax1 + " Histogram";
-    }    
+        var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
+        if (this.props.axes.length < 2 || !ax1 || !/\d/.test(this.props.pairs[0][ax1]) || !this.numericalYData) {
+            return ax0 + ' Histogram';
+        } else return ax0 + ' by ' + ax1 + ' Histogram';
+    }
 
     @computed get incomingLinks() {
         return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
@@ -83,11 +87,11 @@ export class Histogram extends React.Component<HistogramProps> {
     }
 
     @computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } {
-        if (this.numericalXData){
+        if (this.numericalXData) {
             const data = this.data(this._histogramData);
-            return {xMin: Math.min.apply(null, data), xMax: Math.max.apply(null, data), yMin:0, yMax:0}
+            return { xMin: Math.min.apply(null, data), xMax: Math.max.apply(null, data), yMin: 0, yMax: 0 };
         }
-        return {xMin:0, xMax:0, yMin:0, yMax:0}
+        return { xMin: 0, xMax: 0, yMin: 0, yMax: 0 };
     }
 
     componentWillUnmount() {
@@ -97,7 +101,7 @@ export class Histogram extends React.Component<HistogramProps> {
         this._disposers.chartData = reaction(
             () => ({ dataSet: this._histogramData, w: this.width, h: this.height }),
             ({ dataSet, w, h }) => {
-                if (dataSet!.length>0) {
+                if (dataSet!.length > 0) {
                     this.drawChart(dataSet, w, h);
                 }
             },
@@ -113,7 +117,7 @@ export class Histogram extends React.Component<HistogramProps> {
             //
             title: 'histogram doc selection' + this._currSelected,
         });
-        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc);
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
         return anchor;
     };
 
@@ -127,20 +131,22 @@ export class Histogram extends React.Component<HistogramProps> {
 
     // cleans data by converting numerical data to numbers and taking out empty cells
     data = (dataSet: any) => {
-        var validData = dataSet.filter((d: { [x: string]: unknown; }) => {
+        var validData = dataSet.filter((d: { [x: string]: unknown }) => {
             var valid = true;
             Object.keys(dataSet[0]).map(key => {
                 if (!d[key] || Number.isNaN(d[key])) valid = false;
-            })
+            });
             return valid;
-        })
-        var field = dataSet[0]? Object.keys(dataSet[0])[0] : undefined;
-        const data = validData.map((d: { [x: string]: any; }) => {
-            if (this.numericalXData) { return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') }
-            return d[field!]
-        })
+        });
+        var field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
+        const data = validData.map((d: { [x: string]: any }) => {
+            if (this.numericalXData) {
+                return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
+            }
+            return d[field!];
+        });
         return data;
-    }
+    };
 
     // outlines the bar selected / hovered over
     highlightSelectedBar = (changeSelectedVariables: boolean, svg: any, eachRectWidth: any, pointerX: any, xAxisTitle: any, yAxisTitle: any, histDataSet: any) => {
@@ -148,35 +154,31 @@ export class Histogram extends React.Component<HistogramProps> {
         var barCounter = -1;
         const selected = svg.selectAll('.histogram-bar').filter((d: any) => {
             barCounter++; // uses the order of bars and width of each bar to find which one the pointer is over
-            if ((barCounter*eachRectWidth ) <= pointerX && pointerX <= ((barCounter+1)*eachRectWidth)){
-                var showSelected = this.numericalYData? this._histogramData.filter((data: { [x: string]: any; }) => StrCast(data[xAxisTitle]).replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')==d[0])[0] 
-                : histDataSet.filter((data: { [x: string]: any; }) => data[xAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')==d[0])[0];
-                if (this.numericalXData){
+            if (barCounter * eachRectWidth <= pointerX && pointerX <= (barCounter + 1) * eachRectWidth) {
+                var showSelected = this.numericalYData
+                    ? this._histogramData.filter((data: { [x: string]: any }) => StrCast(data[xAxisTitle]).replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') == d[0])[0]
+                    : histDataSet.filter((data: { [x: string]: any }) => data[xAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') == d[0])[0];
+                if (this.numericalXData) {
                     // calculating frequency
-                    if (d[0] && d[1] && d[0]!=d[1]){
-                        showSelected = {[xAxisTitle]: (d3.min(d) + " to " + d3.max(d)), frequency: d.length}
-                    }
-                    else if (!this.numericalYData) showSelected = {[xAxisTitle]: showSelected[xAxisTitle], frequency: d.length}
-                } 
-                if (changeSelectedVariables){
-                    // for when a bar is selected - not just hovered over
-                    sameAsCurrent = this._currSelected? 
-                        (showSelected[xAxisTitle]==this._currSelected![xAxisTitle] 
-                    && showSelected[yAxisTitle]==this._currSelected![yAxisTitle])
-                        : false;
-                    this._currSelected = sameAsCurrent? undefined: showSelected;
-                    this.selectedData = sameAsCurrent? undefined: d;
+                    if (d[0] && d[1] && d[0] != d[1]) {
+                        showSelected = { [xAxisTitle]: d3.min(d) + ' to ' + d3.max(d), frequency: d.length };
+                    } else if (!this.numericalYData) showSelected = { [xAxisTitle]: showSelected[xAxisTitle], frequency: d.length };
                 }
-                else this.hoverOverData = d;
-                return true
+                if (changeSelectedVariables) {
+                    // for when a bar is selected - not just hovered over
+                    sameAsCurrent = this._currSelected ? showSelected[xAxisTitle] == this._currSelected![xAxisTitle] && showSelected[yAxisTitle] == this._currSelected![yAxisTitle] : false;
+                    this._currSelected = sameAsCurrent ? undefined : showSelected;
+                    this.selectedData = sameAsCurrent ? undefined : d;
+                } else this.hoverOverData = d;
+                return true;
             }
             return false;
         });
-        if (changeSelectedVariables){
+        if (changeSelectedVariables) {
             if (sameAsCurrent!) this.curBarSelected = undefined;
             else this.curBarSelected = selected;
         }
-    }
+    };
 
     // draws the histogram
     drawChart = (dataSet: any, width: number, height: number) => {
@@ -184,306 +186,338 @@ export class Histogram extends React.Component<HistogramProps> {
         d3.select(this._histogramRef.current).select('.tooltip').remove();
 
         var data = this.data(dataSet);
-        var xAxisTitle = Object.keys(dataSet[0])[0]
+        var xAxisTitle = Object.keys(dataSet[0])[0];
         var yAxisTitle = this.numericalYData ? Object.keys(dataSet[0])[1] : 'frequency';
-        let uniqueArr: unknown[] = [...new Set(data)]
-        var numBins = (this.numericalXData && Number.isInteger(data[0]))? (this.rangeVals.xMax! - this.rangeVals.xMin! ) : uniqueArr.length
-        var translateXAxis = !this.numericalXData || numBins<this.maxBins ?  width/(numBins+1)/2 : 0;
-        if (numBins>this.maxBins) numBins = this.maxBins;
-        var startingPoint = this.numericalXData? this.rangeVals.xMin! : 0;
-        var endingPoint = this.numericalXData? this.rangeVals.xMax! : numBins;
+        let uniqueArr: unknown[] = [...new Set(data)];
+        var numBins = this.numericalXData && Number.isInteger(data[0]) ? this.rangeVals.xMax! - this.rangeVals.xMin! : uniqueArr.length;
+        var translateXAxis = !this.numericalXData || numBins < this.maxBins ? width / (numBins + 1) / 2 : 0;
+        if (numBins > this.maxBins) numBins = this.maxBins;
+        var startingPoint = this.numericalXData ? this.rangeVals.xMin! : 0;
+        var endingPoint = this.numericalXData ? this.rangeVals.xMax! : numBins;
 
         // converts data into Objects
-        var histDataSet = dataSet.filter((d: { [x: string]: unknown; }) => {
+        var histDataSet = dataSet.filter((d: { [x: string]: unknown }) => {
             var valid = true;
             Object.keys(dataSet[0]).map(key => {
                 if (!d[key] || Number.isNaN(d[key])) valid = false;
-            })
+            });
             return valid;
         });
         if (!this.numericalXData) {
-            var histStringDataSet: { [x: string]: unknown; }[] = [];
-            if (this.numericalYData){
-                for (let i=0; i<dataSet.length; i++){
-                    histStringDataSet.push({[yAxisTitle]: dataSet[i][yAxisTitle], [xAxisTitle]: dataSet[i][xAxisTitle]})
+            var histStringDataSet: { [x: string]: unknown }[] = [];
+            if (this.numericalYData) {
+                for (let i = 0; i < dataSet.length; i++) {
+                    histStringDataSet.push({ [yAxisTitle]: dataSet[i][yAxisTitle], [xAxisTitle]: dataSet[i][xAxisTitle] });
                 }
-            }
-            else{
-                for (let i=0; i<uniqueArr.length; i++){
-                    histStringDataSet.push({[yAxisTitle]: 0, [xAxisTitle]: uniqueArr[i]})
+            } else {
+                for (let i = 0; i < uniqueArr.length; i++) {
+                    histStringDataSet.push({ [yAxisTitle]: 0, [xAxisTitle]: uniqueArr[i] });
                 }
-                for (let i=0; i<data.length; i++){
-                    let barData = histStringDataSet.filter(each => each[xAxisTitle]==data[i])
-                    histStringDataSet.filter(each => each[xAxisTitle]==data[i])[0][yAxisTitle] = Number(barData[0][yAxisTitle]) + 1;
+                for (let i = 0; i < data.length; i++) {
+                    let barData = histStringDataSet.filter(each => each[xAxisTitle] == data[i]);
+                    histStringDataSet.filter(each => each[xAxisTitle] == data[i])[0][yAxisTitle] = Number(barData[0][yAxisTitle]) + 1;
                 }
             }
-            histDataSet = histStringDataSet
+            histDataSet = histStringDataSet;
         }
 
         // initial graph and binning data for histogram
         var svg = (this._histogramSvg = d3
             .select(this._histogramRef.current)
-            .append("svg")
-                .attr("class", "graph")
-                .attr("width", width + this.props.margin.right + this.props.margin.left)
-                .attr("height", height + this.props.margin.top + this.props.margin.bottom)
-            .append("g")
-                .attr("transform",
-                      "translate(" + this.props.margin.left + "," + this.props.margin.top + ")"));
-        var x = d3.scaleLinear()
-                .domain(this.numericalXData? [startingPoint!, endingPoint!] : [0, numBins])
-                .range([0, width ]);
-        var histogram = d3.histogram()
-            .value(function(d) {return d})
+            .append('svg')
+            .attr('class', 'graph')
+            .attr('width', width + this.props.margin.right + this.props.margin.left)
+            .attr('height', height + this.props.margin.top + this.props.margin.bottom)
+            .append('g')
+            .attr('transform', 'translate(' + this.props.margin.left + ',' + this.props.margin.top + ')'));
+        var x = d3
+            .scaleLinear()
+            .domain(this.numericalXData ? [startingPoint!, endingPoint!] : [0, numBins])
+            .range([0, width]);
+        var histogram = d3
+            .histogram()
+            .value(function (d) {
+                return d;
+            })
             .domain([startingPoint!, endingPoint!])
-            .thresholds(x.ticks(numBins))
-        var bins = histogram(data)
-        var eachRectWidth = width/(bins.length)
-        var graphStartingPoint = (bins[0].x1 && bins[1])? bins[0].x1! - (bins[1].x1! - bins[1].x0!) : 0;
+            .thresholds(x.ticks(numBins));
+        var bins = histogram(data);
+        var eachRectWidth = width / bins.length;
+        var graphStartingPoint = bins[0].x1 && bins[1] ? bins[0].x1! - (bins[1].x1! - bins[1].x0!) : 0;
         bins[0].x0 = graphStartingPoint;
-        x = x.domain([graphStartingPoint, endingPoint])
-             .range([0, Number.isInteger(this.rangeVals.xMin!)? (width-eachRectWidth) : width ])
+        x = x.domain([graphStartingPoint, endingPoint]).range([0, Number.isInteger(this.rangeVals.xMin!) ? width - eachRectWidth : width]);
         var xAxis;
 
         // more calculations based on bins
         // x-axis
-        if (!this.numericalXData) { // reorganize to match data if the data is strings rather than numbers
+        if (!this.numericalXData) {
+            // reorganize to match data if the data is strings rather than numbers
             // uniqueArr.sort()
-            histDataSet.sort()
-            for (let i=0; i<data.length; i++){
-                var index = 0
-                for (let j=0; j<uniqueArr.length; j++){
-                    if (uniqueArr[j] == data[i]){
+            histDataSet.sort();
+            for (let i = 0; i < data.length; i++) {
+                var index = 0;
+                for (let j = 0; j < uniqueArr.length; j++) {
+                    if (uniqueArr[j] == data[i]) {
                         index = j;
                     }
                 }
-                if (bins[index]) bins[index].push(data[i])
+                if (bins[index]) bins[index].push(data[i]);
             }
             bins.pop();
-            eachRectWidth = width/(bins.length)
-            bins.forEach(d => d.x0 = d.x0!)
-            xAxis = d3.axisBottom(x)
-                .ticks(bins.length>1? bins.length-1: 1)
-                .tickFormat( i => uniqueArr[i.valueOf()] as string)
-                .tickPadding(10)
-            x.range([0, width-eachRectWidth])
-            x.domain([0, bins.length-1])
+            eachRectWidth = width / bins.length;
+            bins.forEach(d => (d.x0 = d.x0!));
+            xAxis = d3
+                .axisBottom(x)
+                .ticks(bins.length > 1 ? bins.length - 1 : 1)
+                .tickFormat(i => uniqueArr[i.valueOf()] as string)
+                .tickPadding(10);
+            x.range([0, width - eachRectWidth]);
+            x.domain([0, bins.length - 1]);
             translateXAxis = eachRectWidth / 2;
-        }
-        else {
+        } else {
             var allSame = true;
-            for (var i=0; i<bins.length; i++){
-                if (bins[i] && bins[i][0]){
+            for (var i = 0; i < bins.length; i++) {
+                if (bins[i] && bins[i][0]) {
                     var compare = bins[i][0];
-                    for (let j=1; j<bins[i].length; j++){
+                    for (let j = 1; j < bins[i].length; j++) {
                         if (bins[i][j] != compare) allSame = false;
                     }
                 }
             }
-            if (allSame){ 
+            if (allSame) {
                 translateXAxis = eachRectWidth / 2;
-                eachRectWidth = width/(bins.length)
-            }
-            else {
-                eachRectWidth = width/(bins.length+1)
-                var tickDiff = (bins.length>=2? (bins[bins.length-2].x1!-bins[bins.length-2].x0!): 0)
+                eachRectWidth = width / bins.length;
+            } else {
+                eachRectWidth = width / (bins.length + 1);
+                var tickDiff = bins.length >= 2 ? bins[bins.length - 2].x1! - bins[bins.length - 2].x0! : 0;
                 var curDomain = x.domain();
-                x.domain([curDomain[0], curDomain[0] + tickDiff*bins.length])
+                x.domain([curDomain[0], curDomain[0] + tickDiff * bins.length]);
             }
 
-            xAxis = d3.axisBottom(x)
-                .ticks(bins.length-1)
-            x.range([0, width-eachRectWidth])
+            xAxis = d3.axisBottom(x).ticks(bins.length - 1);
+            x.range([0, width - eachRectWidth]);
         }
         // y-axis
-        const maxFrequency = this.numericalYData? d3.max(histDataSet, function(d: any) { 
-            return d[yAxisTitle]? Number(d[yAxisTitle]!.replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) : 0})
-                    : d3.max(bins, function(d) { return d.length; })
-        var y = d3.scaleLinear()
-            .range([height, 0]);
-            y.domain([0, +maxFrequency!]);
-        var yAxis = d3.axisLeft(y)
-            .ticks(maxFrequency!)
-        if (this.numericalYData){
+        const maxFrequency = this.numericalYData
+            ? d3.max(histDataSet, function (d: any) {
+                  return d[yAxisTitle] ? Number(d[yAxisTitle]!.replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) : 0;
+              })
+            : d3.max(bins, function (d) {
+                  return d.length;
+              });
+        var y = d3.scaleLinear().range([height, 0]);
+        y.domain([0, +maxFrequency!]);
+        var yAxis = d3.axisLeft(y).ticks(maxFrequency!);
+        if (this.numericalYData) {
             const yScale = scaleCreatorNumerical(0, Number(maxFrequency), height, 0);
             yAxisCreator(svg.append('g'), width, yScale);
+        } else {
+            svg.append('g').call(yAxis);
         }
-        else{
-            svg.append("g")
-            .call(yAxis);
-        }
-        svg.append("g")
-            .attr("transform", "translate(" + translateXAxis + ", " + height + ")")
-            .call(xAxis)
+        svg.append('g')
+            .attr('transform', 'translate(' + translateXAxis + ', ' + height + ')')
+            .call(xAxis);
 
         // click/hover
         const onPointClick = action((e: any) => this.highlightSelectedBar(true, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet));
         const onHover = action((e: any) => {
-            const selected = this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet)
+            const selected = this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet);
             updateHighlights();
-        })
+        });
         const mouseOut = action((e: any) => {
             this.hoverOverData = undefined;
             updateHighlights();
-        })
+        });
         const updateHighlights = () => {
             const hoverOverBar = this.hoverOverData;
             const selectedData = this.selectedData;
-            svg.selectAll('rect').attr("class", function(d: any) { return ((hoverOverBar && hoverOverBar[0]==d[0]) || selectedData && selectedData[0]==d[0])? 'histogram-bar hover' : 'histogram-bar'; })
-        }
-        svg.on('click', onPointClick)
-            .on('mouseover', onHover)
-            .on('mouseout', mouseOut)
+            svg.selectAll('rect').attr('class', function (d: any) {
+                return (hoverOverBar && hoverOverBar[0] == d[0]) || (selectedData && selectedData[0] == d[0]) ? 'histogram-bar hover' : 'histogram-bar';
+            });
+        };
+        svg.on('click', onPointClick).on('mouseover', onHover).on('mouseout', mouseOut);
 
         // axis titles
-        svg.append("text")
-            .attr("transform", "translate(" + (width/2) + " ," + (height+40) + ")")
-            .style("text-anchor", "middle")
+        svg.append('text')
+            .attr('transform', 'translate(' + width / 2 + ' ,' + (height + 40) + ')')
+            .style('text-anchor', 'middle')
             .text(xAxisTitle);
-        svg.append("text")
-            .attr("transform", "rotate(-90)" + " " + "translate( 0, " + -10 + ")")
-            .attr("x", -(height/2))
-            .attr("y", -20)
-            .style("text-anchor", "middle")
+        svg.append('text')
+            .attr('transform', 'rotate(-90)' + ' ' + 'translate( 0, ' + -10 + ')')
+            .attr('x', -(height / 2))
+            .attr('y', -20)
+            .style('text-anchor', 'middle')
             .text(yAxisTitle);
-        d3.format('.0f')
+        d3.format('.0f');
 
         // draw bars
         var selected = this.selectedData;
-        svg.selectAll("rect")
+        svg.selectAll('rect')
             .data(bins)
             .enter()
-            .append("rect")
-              .attr("transform", this.numericalYData?
-                    function (d) {
-                        var eachData = histDataSet.filter((data: { [x: string]: number; }) => {return data[xAxisTitle]==d[0]}) 
-                        var length = eachData.length? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
-                        return "translate(" + x(d.x0!) + "," + y(length) + ")";
-                    }
-                    : function(d) { return "translate(" + x(d.x0!) + "," + y(d.length) + ")"; })
-              .attr("height", this.numericalYData? 
-                    function(d) {
-                        var eachData = histDataSet.filter((data: { [x: string]: number; }) => {return data[xAxisTitle]==d[0]}) 
-                        var length = eachData.length? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
-                        return height-y(length)}
-                    : function(d) { 
-                        return height - y(d.length)})
-              .attr("width", eachRectWidth)
-              .attr("class", selected? 
-                    function(d) {
-                        return (selected && selected[0]==d[0])? 'histogram-bar hover' : 'histogram-bar';
-                    }: function(d) {return 'histogram-bar'})
-              .attr("fill", (d)=>{ 
+            .append('rect')
+            .attr(
+                'transform',
+                this.numericalYData
+                    ? function (d) {
+                          var eachData = histDataSet.filter((data: { [x: string]: number }) => {
+                              return data[xAxisTitle] == d[0];
+                          });
+                          var length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
+                          return 'translate(' + x(d.x0!) + ',' + y(length) + ')';
+                      }
+                    : function (d) {
+                          return 'translate(' + x(d.x0!) + ',' + y(d.length) + ')';
+                      }
+            )
+            .attr(
+                'height',
+                this.numericalYData
+                    ? function (d) {
+                          var eachData = histDataSet.filter((data: { [x: string]: number }) => {
+                              return data[xAxisTitle] == d[0];
+                          });
+                          var length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
+                          return height - y(length);
+                      }
+                    : function (d) {
+                          return height - y(d.length);
+                      }
+            )
+            .attr('width', eachRectWidth)
+            .attr(
+                'class',
+                selected
+                    ? function (d) {
+                          return selected && selected[0] == d[0] ? 'histogram-bar hover' : 'histogram-bar';
+                      }
+                    : function (d) {
+                          return 'histogram-bar';
+                      }
+            )
+            .attr('fill', d => {
                 var barColor;
                 var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
-                barColors.map(each => { 
-                    if (d[0] && d[0].toString() && each[0]==d[0].toString()) barColor = each[1]; 
+                barColors.map(each => {
+                    if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1];
                     else {
-                        var range = StrCast(each[0]).split(" to ");
-                        if (Number(range[0])<=d[0] && d[0]<=Number(range[1])) barColor = each[1];
+                        var range = StrCast(each[0]).split(' to ');
+                        if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
                     }
                 });
-                return barColor? StrCast(barColor) : StrCast(this.props.layoutDoc.defaultHistogramColor)})
+                return barColor ? StrCast(barColor) : StrCast(this.props.layoutDoc.defaultHistogramColor);
+            });
     };
 
     @action changeSelectedColor = (color: string) => {
-        this.curBarSelected.attr("fill", color);
-        var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))
+        this.curBarSelected.attr('fill', color);
+        var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
 
-        const barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec("string"), null);
-        barColors.map(each => { if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1) });
+        const barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec('string'), null);
+        barColors.map(each => {
+            if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1);
+        });
         barColors.push(StrCast(barName + '::' + color));
     };
-    
-    @action eraseSelectedColor= () => {
-        this.curBarSelected.attr("fill", this.props.layoutDoc.defaultHistogramColor);
-        var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))
 
-        const barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec("string"), null);
-        barColors.map(each => { if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1) });
+    @action eraseSelectedColor = () => {
+        this.curBarSelected.attr('fill', this.props.layoutDoc.defaultHistogramColor);
+        var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
+
+        const barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec('string'), null);
+        barColors.map(each => {
+            if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1);
+        });
     };
 
     render() {
-        this._histogramData
-        var curSelectedBarName;
-        var titleAccessor: any='';
-        if (this.props.axes.length==2) titleAccessor = 'histogram-title-'+this.props.axes[0]+'-'+this.props.axes[1];
-        else if (this.props.axes.length>0) titleAccessor = 'histogram-title-'+this.props.axes[0];
+        this._histogramData;
+        var curSelectedBarName = '';
+        var titleAccessor: any = '';
+        if (this.props.axes.length == 2) titleAccessor = 'dataViz_title_histogram_' + this.props.axes[0] + '-' + this.props.axes[1];
+        else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_histogram_' + this.props.axes[0];
         if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
         if (!this.props.layoutDoc.defaultHistogramColor) this.props.layoutDoc.defaultHistogramColor = '#69b3a2';
         if (!this.props.layoutDoc.histogramBarColors) this.props.layoutDoc.histogramBarColors = new List<string>();
         var selected: string;
-        if (this._currSelected){
-            curSelectedBarName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))
+        if (this._currSelected) {
+            curSelectedBarName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
             selected = '{ ';
             Object.keys(this._currSelected).map(key => {
-                key!=''? selected += key + ': ' + this._currSelected[key] + ', ': '';
-            })
-            selected = selected.substring(0, selected.length-2);
+                key != '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : '';
+            });
+            selected = selected.substring(0, selected.length - 2);
             selected += ' }';
-        }
-        else selected = 'none';
+        } else selected = 'none';
         var selectedBarColor;
         var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
-        barColors.map(each => {if (each[0]==curSelectedBarName!) selectedBarColor = each[1]});
+        barColors.map(each => {
+            if (each[0] == curSelectedBarName!) selectedBarColor = each[1];
+        });
 
         this.componentDidMount();
 
-        if (this._histogramData.length>0){
-            return (
-                this.props.axes.length >= 1 ? (
-                <div className="chart-container" >
-                    <div className="graph-title"> 
+        if (this._histogramData.length > 0) {
+            return this.props.axes.length >= 1 ? (
+                <div className="chart-container">
+                    <div className="graph-title">
                         <EditableText
                             val={StrCast(this.props.layoutDoc[titleAccessor])}
-                            setVal={undoable (action(val => this.props.layoutDoc[titleAccessor] = val as string), "Change Graph Title")}
-                            color={"black"}
+                            setVal={undoable(
+                                action(val => (this.props.layoutDoc[titleAccessor] = val as string)),
+                                'Change Graph Title'
+                            )}
+                            color={'black'}
                             size={Size.LARGE}
                             fillWidth
                         />
-                        &nbsp; &nbsp; 
+                        &nbsp; &nbsp;
                         <ColorPicker
-                            tooltip={'Change Default Bar Color'} 
-                            type={Type.SEC} 
-                            icon={<FaFillDrip/>}
+                            tooltip={'Change Default Bar Color'}
+                            type={Type.SEC}
+                            icon={<FaFillDrip />}
                             selectedColor={StrCast(this.props.layoutDoc.defaultHistogramColor)}
-                            setSelectedColor={undoable (color => this.props.layoutDoc.defaultHistogramColor = color, "Change Default Bar Color")}
+                            setFinalColor={undoable(color => (this.props.layoutDoc.defaultHistogramColor = color), 'Change Default Bar Color')}
+                            setSelectedColor={undoable(color => (this.props.layoutDoc.defaultHistogramColor = color), 'Change Default Bar Color')}
                             size={Size.XSMALL}
                         />
                     </div>
                     <div ref={this._histogramRef} />
-                    {selected != 'none' ? 
+                    {selected != 'none' ? (
                         <div className={'selected-data'}>
                             Selected: {selected}
                             &nbsp; &nbsp;
                             <ColorPicker
-                                tooltip={'Change Bar Color'} 
-                                type={Type.SEC} 
-                                icon={<FaFillDrip/>}
-                                selectedColor={selectedBarColor? selectedBarColor : this.curBarSelected.attr("fill")}
-                                setSelectedColor={undoable (color => this.changeSelectedColor(color), "Change Selected Bar Color")}
+                                tooltip={'Change Bar Color'}
+                                type={Type.SEC}
+                                icon={<FaFillDrip />}
+                                selectedColor={selectedBarColor ? selectedBarColor : this.curBarSelected.attr('fill')}
+                                setFinalColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Bar Color')}
+                                setSelectedColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Bar Color')}
                                 size={Size.XSMALL}
                             />
                             &nbsp;
                             <IconButton
-                                icon={<FontAwesomeIcon icon={'eraser'} />} 
-                                size={Size.XSMALL} 
-                                color={'black'} 
+                                icon={<FontAwesomeIcon icon={'eraser'} />}
+                                size={Size.XSMALL}
+                                color={'black'}
                                 type={Type.SEC}
                                 tooltip={'Revert to the default bar color'}
-                                onClick={undoable (action(() => this.eraseSelectedColor()), "Change Selected Bar Color")}
+                                onClick={undoable(
+                                    action(() => this.eraseSelectedColor()),
+                                    'Change Selected Bar Color'
+                                )}
                             />
                         </div>
-                    : null}
+                    ) : null}
                 </div>
-                ) : <span className="chart-container"> {'first use table view to select a column to graph'}</span>
+            ) : (
+                <span className="chart-container"> {'first use table view to select a column to graph'}</span>
+            );
+        } else
+            return (
+                // when it is a brushed table and the incoming table doesn't have any rows selected
+                <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
             );
-        }
-        else return (
-            // when it is a brushed table and the incoming table doesn't have any rows selected
-            <div className='chart-container'>
-                Selected rows of data from the incoming DataVizBox to display.
-            </div>
-        )
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 8bace941f..8e549adbe 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -50,20 +50,21 @@ export class LineChart extends React.Component<LineChartProps> {
     // TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates
 
     @computed get _lineChartData() {
-        var guids = StrListCast(this.props.layoutDoc.rowGuids);
+        var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
         if (this.props.axes.length <= 1) return [];
         return this.props.pairs
-            ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
+            ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
             .map(pair => ({ x: Number(pair[this.props.axes[0]]), y: Number(pair[this.props.axes[1]]) }))
             .sort((a, b) => (a.x < b.x ? -1 : 1));
     }
-    @computed get graphTitle(){
-        return this.props.axes[1] + " vs. " + this.props.axes[0] + " Line Chart";
-    }  
+    @computed get graphTitle() {
+        return this.props.axes[1] + ' vs. ' + this.props.axes[0] + ' Line Chart';
+    }
     @computed get incomingLinks() {
         return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
             .filter(link => {
-                return link.link_anchor_1 == this.props.rootDoc.draggedFrom}) // get links where this chart doc is the target of the link
+                return link.link_anchor_1 == this.props.rootDoc.draggedFrom;
+            }) // get links where this chart doc is the target of the link
             .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
     }
     @computed get incomingSelected() {
@@ -158,7 +159,7 @@ export class LineChart extends React.Component<LineChartProps> {
 
     @action
     restoreView = (data: Doc) => {
-        const coords = Cast(data.presDataVizSelection, listSpec('number'), null);
+        const coords = Cast(data.config_dataVizSelection, listSpec('number'), null);
         if (coords?.length > 1 && (this._currSelected?.x !== coords[0] || this._currSelected?.y !== coords[1])) {
             this.setCurrSelected(coords[0], coords[1]);
             return true;
@@ -176,8 +177,8 @@ export class LineChart extends React.Component<LineChartProps> {
             //
             title: 'line doc selection' + this._currSelected?.x,
         });
-        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc);
-        anchor.presDataVizSelection = this._currSelected ? new List<number>([this._currSelected.x, this._currSelected.y]) : undefined;
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
+        anchor.config_dataVizSelection = this._currSelected ? new List<number>([this._currSelected.x, this._currSelected.y]) : undefined;
         return anchor;
     };
 
@@ -189,14 +190,13 @@ export class LineChart extends React.Component<LineChartProps> {
         return this.props.width - this.props.margin.left - this.props.margin.right;
     }
 
-    @computed get defaultGraphTitle(){
+    @computed get defaultGraphTitle() {
         var ax0 = this.props.axes[0];
-        var ax1 = (this.props.axes.length>1)? this.props.axes[1] : undefined;
-        if (this.props.axes.length<2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1){
-            return ax0 + " Line Chart";
-        }
-        else return ax1 + " by " + ax0 + " Line Chart";
-    } 
+        var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
+        if (this.props.axes.length < 2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1) {
+            return ax0 + ' Line Chart';
+        } else return ax1 + ' by ' + ax0 + ' Line Chart';
+    }
 
     setupTooltip() {
         return d3
@@ -215,7 +215,7 @@ export class LineChart extends React.Component<LineChartProps> {
     @action
     setCurrSelected(x?: number, y?: number) {
         // TODO: nda - get rid of svg element in the list?
-        if (this._currSelected && this._currSelected.x==x && this._currSelected.y==y) this._currSelected = undefined;
+        if (this._currSelected && this._currSelected.x == x && this._currSelected.y == y) this._currSelected = undefined;
         else this._currSelected = x !== undefined && y !== undefined ? { x, y } : undefined;
         this.props.pairs.forEach(pair => pair[this.props.axes[0]] === x && pair[this.props.axes[1]] === y && (pair.selected = true));
     }
@@ -256,7 +256,7 @@ export class LineChart extends React.Component<LineChartProps> {
         const svg = (this._lineChartSvg = d3
             .select(this._lineChartRef.current)
             .append('svg')
-            .attr("class", "graph")
+            .attr('class', 'graph')
             .attr('width', `${width + margin.left + margin.right}`)
             .attr('height', `${height + margin.top + margin.bottom}`)
             .append('g')
@@ -271,13 +271,13 @@ export class LineChart extends React.Component<LineChartProps> {
         // get valid data points
         const data = dataSet[0];
         const lineGen = createLineGenerator(xScale, yScale);
-        var validData = data.filter((d => {
+        var validData = data.filter(d => {
             var valid = true;
             Object.keys(data[0]).map(key => {
                 if (!d[key] || Number.isNaN(d[key])) valid = false;
-            })
+            });
             return valid;
-        }))
+        });
         // draw the plot line
         drawLine(svg.append('path'), validData, lineGen);
         // draw the datapoint circle
@@ -321,17 +321,17 @@ export class LineChart extends React.Component<LineChartProps> {
             .on('click', onPointClick);
 
         // axis titles
-        svg.append("text")
-            .attr("transform", "translate(" + (width/2) + " ," + (height+40) + ")")
-            .style("text-anchor", "middle")
+        svg.append('text')
+            .attr('transform', 'translate(' + width / 2 + ' ,' + (height + 40) + ')')
+            .style('text-anchor', 'middle')
             .text(this.props.axes[0]);
-        svg.append("text")
-            .attr("transform", "rotate(-90)" + " " + "translate( 0, " + -10 + ")")
-            .attr("x", -(height/2))
-            .attr("y", -20)
-            .attr("height", 20)
-            .attr("width", 20)
-            .style("text-anchor", "middle")
+        svg.append('text')
+            .attr('transform', 'rotate(-90)' + ' ' + 'translate( 0, ' + -10 + ')')
+            .attr('x', -(height / 2))
+            .attr('y', -20)
+            .attr('height', 20)
+            .attr('width', 20)
+            .style('text-anchor', 'middle')
             .text(this.props.axes[1]);
     };
 
@@ -348,42 +348,41 @@ export class LineChart extends React.Component<LineChartProps> {
         tooltip
             .html(() => `<b>(${d0.x},${d0.y})</b>`) // text content for tooltip
             .style('pointer-events', 'none')
-            .style('transform', `translate(${xScale(d0.x)-this.width}px,${yScale(d0.y)}px)`);
+            .style('transform', `translate(${xScale(d0.x) - this.width}px,${yScale(d0.y)}px)`);
     }
 
     render() {
         this.componentDidMount();
-        var titleAccessor:any = '';
-        if (this.props.axes.length==2) titleAccessor = 'lineChart-title-'+this.props.axes[0]+'-'+this.props.axes[1];
-        else if (this.props.axes.length>0) titleAccessor = 'lineChart-title-'+this.props.axes[0];
+        var titleAccessor: any = '';
+        if (this.props.axes.length == 2) titleAccessor = 'dataViz_title_lineChart_' + this.props.axes[0] + '-' + this.props.axes[1];
+        else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_lineChart_' + this.props.axes[0];
         if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
         const selectedPt = this._currSelected ? `{ ${this.props.axes[0]}: ${this._currSelected.x} ${this.props.axes[1]}: ${this._currSelected.y} }` : 'none';
-        if (this._lineChartData.length>0){
-            return (
-                this.props.axes.length>=2 && /\d/.test(this.props.pairs[0][this.props.axes[0]]) && /\d/.test(this.props.pairs[0][this.props.axes[1]]) ? (
-                <div className="chart-container" >
-                    <div className="graph-title"> 
+        if (this._lineChartData.length > 0) {
+            return this.props.axes.length >= 2 && /\d/.test(this.props.pairs[0][this.props.axes[0]]) && /\d/.test(this.props.pairs[0][this.props.axes[1]]) ? (
+                <div className="chart-container">
+                    <div className="graph-title">
                         <EditableText
                             val={StrCast(this.props.layoutDoc[titleAccessor])}
-                            setVal={undoable (action(val => this.props.layoutDoc[titleAccessor] = val as string), "Change Graph Title")}
-                            color={"black"}
+                            setVal={undoable(
+                                action(val => (this.props.layoutDoc[titleAccessor] = val as string)),
+                                'Change Graph Title'
+                            )}
+                            color={'black'}
                             size={Size.LARGE}
                             fillWidth
                         />
                     </div>
                     <div ref={this._lineChartRef} />
-                    {selectedPt!='none'? 
-                        <div className={'selected-data'}> {`Selected: ${selectedPt}`}</div>
-                    : null}
+                    {selectedPt != 'none' ? <div className={'selected-data'}> {`Selected: ${selectedPt}`}</div> : null}
                 </div>
-                ) : <span className="chart-container"> {'first use table view to select two numerical axes to plot'}</span>
+            ) : (
+                <span className="chart-container"> {'first use table view to select two numerical axes to plot'}</span>
+            );
+        } else
+            return (
+                // when it is a brushed table and the incoming table doesn't have any rows selected
+                <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
             );
-        }
-        else return (
-            // when it is a brushed table and the incoming table doesn't have any rows selected
-            <div className='chart-container'>
-                Selected rows of data from the incoming DataVizBox to display.
-            </div>
-        )
     }
 }
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index 0c54d0a4e..fe77293f0 100644
--- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
@@ -1,18 +1,18 @@
-import { observer } from "mobx-react";
-import { Doc, StrListCast } from "../../../../../fields/Doc";
+import { observer } from 'mobx-react';
+import { Doc, StrListCast } from '../../../../../fields/Doc';
 import * as React from 'react';
 import * as d3 from 'd3';
-import { IReactionDisposer, action, computed, observable, reaction } from "mobx";
-import { LinkManager } from "../../../../util/LinkManager";
-import { Cast, DocCast, StrCast} from "../../../../../fields/Types";
-import { PinProps, PresBox } from "../../trails";
-import { Docs } from "../../../../documents/Documents";
-import { List } from "../../../../../fields/List";
+import { IReactionDisposer, action, computed, observable, reaction } from 'mobx';
+import { LinkManager } from '../../../../util/LinkManager';
+import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
+import { PinProps, PresBox } from '../../trails';
+import { Docs } from '../../../../documents/Documents';
+import { List } from '../../../../../fields/List';
 import './Chart.scss';
-import { ColorPicker, EditableText, Size, Type } from "browndash-components";
-import { FaFillDrip } from "react-icons/fa";
-import { listSpec } from "../../../../../fields/Schema";
-import { undoable } from "../../../../util/UndoManager";
+import { ColorPicker, EditableText, Size, Type } from 'browndash-components';
+import { FaFillDrip } from 'react-icons/fa';
+import { listSpec } from '../../../../../fields/Schema';
+import { undoable } from '../../../../util/UndoManager';
 
 export interface PieChartProps {
     rootDoc: Doc;
@@ -33,7 +33,6 @@ export interface PieChartProps {
 
 @observer
 export class PieChart extends React.Component<PieChartProps> {
-
     private _disposers: { [key: string]: IReactionDisposer } = {};
     private _piechartRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _piechartSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
@@ -45,31 +44,34 @@ export class PieChart extends React.Component<PieChartProps> {
 
     // filters all data to just display selected data if brushed (created from an incoming link)
     @computed get _piechartData() {
-        var guids = StrListCast(this.props.layoutDoc.rowGuids);
+        var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
         if (this.props.axes.length < 1) return [];
         if (this.props.axes.length < 2) {
             var ax0 = this.props.axes[0];
-            if (/\d/.test(this.props.pairs[0][ax0])){ this.byCategory = false }
+            if (/\d/.test(this.props.pairs[0][ax0])) {
+                this.byCategory = false;
+            }
             return this.props.pairs
-                ?.filter(pair => (!this.incomingLinks.length ? true :  this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
-                .map(pair => ({ [ax0]: (pair[this.props.axes[0]])}))
-        };
+                ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
+                .map(pair => ({ [ax0]: pair[this.props.axes[0]] }));
+        }
         var ax0 = this.props.axes[0];
         var ax1 = this.props.axes[1];
-        if (/\d/.test(this.props.pairs[0][ax0])) { this.byCategory = false; }
+        if (/\d/.test(this.props.pairs[0][ax0])) {
+            this.byCategory = false;
+        }
         return this.props.pairs
-            ?.filter(pair => (!this.incomingLinks.length ? true :  this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
-            .map(pair => ({ [ax0]: (pair[this.props.axes[0]]), [ax1]: (pair[this.props.axes[1]]) }))
+            ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
+            .map(pair => ({ [ax0]: pair[this.props.axes[0]], [ax1]: pair[this.props.axes[1]] }));
     }
 
-    @computed get defaultGraphTitle(){
+    @computed get defaultGraphTitle() {
         var ax0 = this.props.axes[0];
-        var ax1 = (this.props.axes.length>1)? this.props.axes[1] : undefined;
-        if (this.props.axes.length<2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1){
-            return ax0 + " Pie Chart";
-        }
-        else return ax1 + " by " + ax0 + " Pie Chart";
-    }    
+        var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
+        if (this.props.axes.length < 2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1) {
+            return ax0 + ' Pie Chart';
+        } else return ax1 + ' by ' + ax0 + ' Pie Chart';
+    }
 
     @computed get incomingLinks() {
         return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
@@ -84,7 +86,7 @@ export class PieChart extends React.Component<PieChartProps> {
         this._disposers.chartData = reaction(
             () => ({ dataSet: this._piechartData, w: this.width, h: this.height }),
             ({ dataSet, w, h }) => {
-                if (dataSet!.length>0) {
+                if (dataSet!.length > 0) {
                     this.drawChart(dataSet, w, h);
                 }
             },
@@ -100,7 +102,7 @@ export class PieChart extends React.Component<PieChartProps> {
             //
             title: 'piechart doc selection' + this._currSelected,
         });
-        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc);
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
         return anchor;
     };
 
@@ -114,167 +116,177 @@ export class PieChart extends React.Component<PieChartProps> {
 
     // cleans data by converting numerical data to numbers and taking out empty cells
     data = (dataSet: any) => {
-        var validData = dataSet.filter((d: { [x: string]: unknown; }) => {
+        var validData = dataSet.filter((d: { [x: string]: unknown }) => {
             var valid = true;
             Object.keys(dataSet[0]).map(key => {
                 if (!d[key] || Number.isNaN(d[key])) valid = false;
-            })
+            });
             return valid;
-        })
-        var field = dataSet[0]? Object.keys(dataSet[0])[0] : undefined;
-        const data = validData.map((d: { [x: string]: any; }) => {
-            if (!this.byCategory) { return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') }
-            return d[field!]
-        })
+        });
+        var field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
+        const data = validData.map((d: { [x: string]: any }) => {
+            if (!this.byCategory) {
+                return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
+            }
+            return d[field!];
+        });
         return data;
-    }
+    };
 
     // outlines the slice selected / hovered over
     highlightSelectedSlice = (changeSelectedVariables: boolean, svg: any, arc: any, radius: any, pointer: any, pieDataSet: any) => {
-            var index = -1;
-            var sameAsCurrent: boolean;
-            const selected = svg.selectAll('.slice').filter((d: any) => {
-                index++;
-                var p1 = [0,0]; // center of pie
-                var p3 = [arc.centroid(d)[0]*2, arc.centroid(d)[1]*2]; // outward peak of arc
-                var p2 = [radius*Math.sin(d.startAngle), -radius*Math.cos(d.startAngle)]; // start of arc
-                var p4 = [radius*Math.sin(d.endAngle), -radius*Math.cos(d.endAngle)]; // end of arc
+        var index = -1;
+        var sameAsCurrent: boolean;
+        const selected = svg.selectAll('.slice').filter((d: any) => {
+            index++;
+            var p1 = [0, 0]; // center of pie
+            var p3 = [arc.centroid(d)[0] * 2, arc.centroid(d)[1] * 2]; // outward peak of arc
+            var p2 = [radius * Math.sin(d.startAngle), -radius * Math.cos(d.startAngle)]; // start of arc
+            var p4 = [radius * Math.sin(d.endAngle), -radius * Math.cos(d.endAngle)]; // end of arc
 
-                // draw an imaginary horizontal line from the pointer to see how many times it crosses a slice edge
-                var lineCrossCount = 0;
-                // if for all 4 lines
-                if (Math.min(p1[1], p2[1])<=pointer[1] && pointer[1]<=Math.max(p1[1], p2[1])){ // within y bounds
-                    if (pointer[0] <= (pointer[1]-p1[1])*(p2[0]-p1[0])/(p2[1]-p1[1])+p1[0]) lineCrossCount++; } // intercepts x
-                if (Math.min(p2[1], p3[1])<=pointer[1] && pointer[1]<=Math.max(p2[1], p3[1])){
-                    if (pointer[0] <= (pointer[1]-p2[1])*(p3[0]-p2[0])/(p3[1]-p2[1])+p2[0]) lineCrossCount++; }
-                if (Math.min(p3[1], p4[1])<=pointer[1] && pointer[1]<=Math.max(p3[1], p4[1])){
-                    if (pointer[0] <= (pointer[1]-p3[1])*(p4[0]-p3[0])/(p4[1]-p3[1])+p3[0]) lineCrossCount++; }
-                if (Math.min(p4[1], p1[1])<=pointer[1] && pointer[1]<=Math.max(p4[1], p1[1])){
-                    if (pointer[0] <= (pointer[1]-p4[1])*(p1[0]-p4[0])/(p1[1]-p4[1])+p4[0]) lineCrossCount++; }
-                if (lineCrossCount % 2 != 0) { // inside the slice of it crosses an odd number of edges
-                    var showSelected = this.byCategory? pieDataSet[index] : this._piechartData[index];
-                    if (changeSelectedVariables){
-                        // for when a bar is selected - not just hovered over
-                        sameAsCurrent = (this._currSelected)? 
-                            (showSelected[Object.keys(showSelected)[0]]==this._currSelected![Object.keys(showSelected)[0]] 
-                        && showSelected[Object.keys(showSelected)[1]]==this._currSelected![Object.keys(showSelected)[1]])
-                            : 
-                            this._currSelected===showSelected;
-                        this._currSelected = sameAsCurrent? undefined: showSelected;
-                        this.selectedData = sameAsCurrent? undefined: d;
-                    }
-                    else this.hoverOverData = d;
-                    return true;
-                }
-                return false;
-            });
-            if (changeSelectedVariables){
-                if (sameAsCurrent!) this.curSliceSelected = undefined;
-                else  this.curSliceSelected = selected;
+            // draw an imaginary horizontal line from the pointer to see how many times it crosses a slice edge
+            var lineCrossCount = 0;
+            // if for all 4 lines
+            if (Math.min(p1[1], p2[1]) <= pointer[1] && pointer[1] <= Math.max(p1[1], p2[1])) {
+                // within y bounds
+                if (pointer[0] <= ((pointer[1] - p1[1]) * (p2[0] - p1[0])) / (p2[1] - p1[1]) + p1[0]) lineCrossCount++;
+            } // intercepts x
+            if (Math.min(p2[1], p3[1]) <= pointer[1] && pointer[1] <= Math.max(p2[1], p3[1])) {
+                if (pointer[0] <= ((pointer[1] - p2[1]) * (p3[0] - p2[0])) / (p3[1] - p2[1]) + p2[0]) lineCrossCount++;
             }
-    }
+            if (Math.min(p3[1], p4[1]) <= pointer[1] && pointer[1] <= Math.max(p3[1], p4[1])) {
+                if (pointer[0] <= ((pointer[1] - p3[1]) * (p4[0] - p3[0])) / (p4[1] - p3[1]) + p3[0]) lineCrossCount++;
+            }
+            if (Math.min(p4[1], p1[1]) <= pointer[1] && pointer[1] <= Math.max(p4[1], p1[1])) {
+                if (pointer[0] <= ((pointer[1] - p4[1]) * (p1[0] - p4[0])) / (p1[1] - p4[1]) + p4[0]) lineCrossCount++;
+            }
+            if (lineCrossCount % 2 != 0) {
+                // inside the slice of it crosses an odd number of edges
+                var showSelected = this.byCategory ? pieDataSet[index] : this._piechartData[index];
+                if (changeSelectedVariables) {
+                    // for when a bar is selected - not just hovered over
+                    sameAsCurrent = this._currSelected
+                        ? showSelected[Object.keys(showSelected)[0]] == this._currSelected![Object.keys(showSelected)[0]] && showSelected[Object.keys(showSelected)[1]] == this._currSelected![Object.keys(showSelected)[1]]
+                        : this._currSelected === showSelected;
+                    this._currSelected = sameAsCurrent ? undefined : showSelected;
+                    this.selectedData = sameAsCurrent ? undefined : d;
+                } else this.hoverOverData = d;
+                return true;
+            }
+            return false;
+        });
+        if (changeSelectedVariables) {
+            if (sameAsCurrent!) this.curSliceSelected = undefined;
+            else this.curSliceSelected = selected;
+        }
+    };
 
     // draws the pie chart
     drawChart = (dataSet: any, width: number, height: number) => {
         d3.select(this._piechartRef.current).select('svg').remove();
         d3.select(this._piechartRef.current).select('.tooltip').remove();
-        
-        var percentField = Object.keys(dataSet[0])[0]
-        var descriptionField = Object.keys(dataSet[0])[1]!
-        var radius = Math.min(width, height-this.props.margin.top-this.props.margin.bottom) /2
+
+        var percentField = Object.keys(dataSet[0])[0];
+        var descriptionField = Object.keys(dataSet[0])[1]!;
+        var radius = Math.min(width, height - this.props.margin.top - this.props.margin.bottom) / 2;
 
         // converts data into Objects
         var data = this.data(dataSet);
-        var pieDataSet = dataSet.filter((d: { [x: string]: unknown; }) => {
+        var pieDataSet = dataSet.filter((d: { [x: string]: unknown }) => {
             var valid = true;
             Object.keys(dataSet[0]).map(key => {
                 if (!d[key] || Number.isNaN(d[key])) valid = false;
-            })
+            });
             return valid;
         });
-        if (this.byCategory){
-            let uniqueCategories = [...new Set(data)]
+        if (this.byCategory) {
+            let uniqueCategories = [...new Set(data)];
             var pieStringDataSet: { frequency: number }[] = [];
-            for (let i=0; i<uniqueCategories.length; i++){
-                pieStringDataSet.push({frequency: 0, [percentField]: uniqueCategories[i]})
+            for (let i = 0; i < uniqueCategories.length; i++) {
+                pieStringDataSet.push({ frequency: 0, [percentField]: uniqueCategories[i] });
             }
-            for (let i=0; i<data.length; i++){
-                let sliceData = pieStringDataSet.filter((each: any) => each[percentField]==data[i])
+            for (let i = 0; i < data.length; i++) {
+                let sliceData = pieStringDataSet.filter((each: any) => each[percentField] == data[i]);
                 sliceData[0].frequency = sliceData[0].frequency + 1;
             }
-            pieDataSet = pieStringDataSet
-            percentField = Object.keys(pieDataSet[0])[0]
-            descriptionField = Object.keys(pieDataSet[0])[1]!
-            data = this.data(pieStringDataSet)
+            pieDataSet = pieStringDataSet;
+            percentField = Object.keys(pieDataSet[0])[0];
+            descriptionField = Object.keys(pieDataSet[0])[1]!;
+            data = this.data(pieStringDataSet);
         }
-        var trackDuplicates : {[key: string]: any} = {};
-        data.forEach((eachData: any) => !trackDuplicates[eachData]? trackDuplicates[eachData] = 0: null)
+        var trackDuplicates: { [key: string]: any } = {};
+        data.forEach((eachData: any) => (!trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null));
 
         // initial chart
         var svg = (this._piechartSvg = d3
             .select(this._piechartRef.current)
-            .append("svg")
-                .attr("class", "graph")
-                .attr("width", width + this.props.margin.right + this.props.margin.left)
-                .attr("height", height + this.props.margin.top + this.props.margin.bottom)
-            .append("g"));
-        let g = svg.append("g")
-            .attr("transform",
-                    "translate(" + (width/2 + this.props.margin.left) + "," + height/2 + ")");
+            .append('svg')
+            .attr('class', 'graph')
+            .attr('width', width + this.props.margin.right + this.props.margin.left)
+            .attr('height', height + this.props.margin.top + this.props.margin.bottom)
+            .append('g'));
+        let g = svg.append('g').attr('transform', 'translate(' + (width / 2 + this.props.margin.left) + ',' + height / 2 + ')');
         var pie = d3.pie();
-        var arc = d3.arc()
-            .innerRadius(0)
-            .outerRadius(radius);
+        var arc = d3.arc().innerRadius(0).outerRadius(radius);
 
         // click/hover
         const onPointClick = action((e: any) => this.highlightSelectedSlice(true, svg, arc, radius, d3.pointer(e), pieDataSet));
         const onHover = action((e: any) => {
-            const selected = this.highlightSelectedSlice(false, svg, arc, radius, d3.pointer(e), pieDataSet)
+            const selected = this.highlightSelectedSlice(false, svg, arc, radius, d3.pointer(e), pieDataSet);
             updateHighlights();
-        })
+        });
         const mouseOut = action((e: any) => {
             this.hoverOverData = undefined;
             updateHighlights();
-        })
+        });
         const updateHighlights = () => {
             const hoverOverSlice = this.hoverOverData;
             const selectedData = this.selectedData;
-            svg.selectAll('path').attr("class", function(d: any) { 
-                return ((selectedData && d.startAngle==selectedData.startAngle && d.endAngle==selectedData.endAngle)
-                    || ((hoverOverSlice && d.startAngle==hoverOverSlice.startAngle && d.endAngle==hoverOverSlice.endAngle)))? 'slice hover' : 'slice'; })
-        }
+            svg.selectAll('path').attr('class', function (d: any) {
+                return (selectedData && d.startAngle == selectedData.startAngle && d.endAngle == selectedData.endAngle) || (hoverOverSlice && d.startAngle == hoverOverSlice.startAngle && d.endAngle == hoverOverSlice.endAngle)
+                    ? 'slice hover'
+                    : 'slice';
+            });
+        };
 
         // drawing the slices
         var selected = this.selectedData;
-        var arcs = g.selectAll("arc")
-            .data(pie(data))
-            .enter()
-            .append("g")
-        arcs.append("path")
-            .attr("fill", (d, i)=>{ 
-                var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number; }; }) => {
+        var arcs = g.selectAll('arc').data(pie(data)).enter().append('g');
+        arcs.append('path')
+            .attr('fill', (d, i) => {
+                var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number } }) => {
                     try {
-                        return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))==Number(d.data)
+                        return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) == Number(d.data);
                     } catch (error) {
-                        return each[percentField]==d.data
-                    }})
+                        return each[percentField] == d.data;
+                    }
+                });
                 var dataPoint;
-                if (possibleDataPoints.length==1) dataPoint = possibleDataPoints[0];
-                else{
-                    dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]]
+                if (possibleDataPoints.length == 1) dataPoint = possibleDataPoints[0];
+                else {
+                    dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]];
                     trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1;
                 }
                 var sliceColor;
-                if (dataPoint){
+                if (dataPoint) {
                     var accessByName = dataPoint[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
                     var sliceColors = StrListCast(this.props.layoutDoc.pieSliceColors).map(each => each.split('::'));
-                    sliceColors.map(each => {if (each[0]==StrCast(accessByName)) sliceColor = each[1]});
+                    sliceColors.map(each => {
+                        if (each[0] == StrCast(accessByName)) sliceColor = each[1];
+                    });
                 }
-                return sliceColor? StrCast(sliceColor) : d3.schemeSet3[i]? d3.schemeSet3[i]: d3.schemeSet3[i%(d3.schemeSet3.length)] })
-                .attr("class", selected? 
-                function(d) {
-                    return (selected && d.startAngle==selected.startAngle && d.endAngle==selected.endAngle)? 'slice hover' : 'slice';
-                }: function(d) {return 'slice'})
+                return sliceColor ? StrCast(sliceColor) : d3.schemeSet3[i] ? d3.schemeSet3[i] : d3.schemeSet3[i % d3.schemeSet3.length];
+            })
+            .attr(
+                'class',
+                selected
+                    ? function (d) {
+                          return selected && d.startAngle == selected.startAngle && d.endAngle == selected.endAngle ? 'slice hover' : 'slice';
+                      }
+                    : function (d) {
+                          return 'slice';
+                      }
+            )
             .attr('d', arc)
             .on('click', onPointClick)
             .on('mouseover', onHover)
@@ -282,99 +294,106 @@ export class PieChart extends React.Component<PieChartProps> {
 
         // adding labels
         trackDuplicates = {};
-        data.forEach((eachData: any) => !trackDuplicates[eachData]? trackDuplicates[eachData] = 0: null)
-        arcs.append("text")
-            .attr("transform",function(d){ 
-                var centroid = arc.centroid(d as unknown as d3.DefaultArcObject)
-                var heightOffset = (centroid[1]/radius) * Math.abs(centroid[1])
-                return "translate("+ (centroid[0]+centroid[0]/(radius*.02)) + "," + (centroid[1]+heightOffset)  + ")"; })
-            .attr("text-anchor", "middle")
-            .text(function(d){ 
-                var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number; }; }) => {
+        data.forEach((eachData: any) => (!trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null));
+        arcs.append('text')
+            .attr('transform', function (d) {
+                var centroid = arc.centroid(d as unknown as d3.DefaultArcObject);
+                var heightOffset = (centroid[1] / radius) * Math.abs(centroid[1]);
+                return 'translate(' + (centroid[0] + centroid[0] / (radius * 0.02)) + ',' + (centroid[1] + heightOffset) + ')';
+            })
+            .attr('text-anchor', 'middle')
+            .text(function (d) {
+                var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number } }) => {
                     try {
-                        return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))==Number(d.data)
+                        return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) == Number(d.data);
                     } catch (error) {
-                        return each[percentField]==d.data
-                    }})
+                        return each[percentField] == d.data;
+                    }
+                });
                 var dataPoint;
-                if (possibleDataPoints.length==1) dataPoint = possibleDataPoints[0];
-                else{
-                    dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]]
+                if (possibleDataPoints.length == 1) dataPoint = possibleDataPoints[0];
+                else {
+                    dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]];
                     trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1;
                 }
-                return dataPoint? dataPoint[percentField]! + (!descriptionField? '' : (' - ' + dataPoint[descriptionField]))! : ''})
+                return dataPoint ? dataPoint[percentField]! + (!descriptionField ? '' : ' - ' + dataPoint[descriptionField])! : '';
+            });
     };
 
     @action changeSelectedColor = (color: string) => {
-        this.curSliceSelected.attr("fill", color);
-        var sliceName = this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')
-        
-        const sliceColors = Cast(this.props.layoutDoc.pieSliceColors, listSpec("string"), null);
-        sliceColors.map(each => { if (each.split('::')[0] == sliceName) sliceColors.splice(sliceColors.indexOf(each), 1) });
+        this.curSliceSelected.attr('fill', color);
+        var sliceName = this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
+
+        const sliceColors = Cast(this.props.layoutDoc.pieSliceColors, listSpec('string'), null);
+        sliceColors.map(each => {
+            if (each.split('::')[0] == sliceName) sliceColors.splice(sliceColors.indexOf(each), 1);
+        });
         sliceColors.push(StrCast(sliceName + '::' + color));
     };
 
     render() {
         this.componentDidMount();
-        var titleAccessor: any='';
-        if (this.props.axes.length==2) titleAccessor = 'pieChart-title-'+this.props.axes[0]+'-'+this.props.axes[1];
-        else if (this.props.axes.length>0) titleAccessor = 'pieChart-title-'+this.props.axes[0];
+        var titleAccessor: any = '';
+        if (this.props.axes.length == 2) titleAccessor = 'dataViz_title_pieChart_' + this.props.axes[0] + '-' + this.props.axes[1];
+        else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_pieChart_' + this.props.axes[0];
         if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
         if (!this.props.layoutDoc.pieSliceColors) this.props.layoutDoc.pieSliceColors = new List<string>();
         var selected: string;
-        var curSelectedSliceName;
-        if (this._currSelected){
-            curSelectedSliceName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))
+        var curSelectedSliceName = '';
+        if (this._currSelected) {
+            curSelectedSliceName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
             selected = '{ ';
             Object.keys(this._currSelected).map(key => {
-                key!=''? selected += key + ': ' + this._currSelected[key] + ', ': '';
-            })
-            selected = selected.substring(0, selected.length-2);
+                key != '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : '';
+            });
+            selected = selected.substring(0, selected.length - 2);
             selected += ' }';
-        }
-        else selected = 'none';
+        } else selected = 'none';
         var selectedSliceColor;
         var sliceColors = StrListCast(this.props.layoutDoc.pieSliceColors).map(each => each.split('::'));
-        sliceColors.map(each => {if (each[0]==curSelectedSliceName!) selectedSliceColor = each[1]});
+        sliceColors.map(each => {
+            if (each[0] == curSelectedSliceName!) selectedSliceColor = each[1];
+        });
 
-        if (this._piechartData.length>0){
-            return (
-                this.props.axes.length >= 1 ? (
+        if (this._piechartData.length > 0) {
+            return this.props.axes.length >= 1 ? (
                 <div className="chart-container">
-                    <div className="graph-title"> 
+                    <div className="graph-title">
                         <EditableText
                             val={StrCast(this.props.layoutDoc[titleAccessor])}
-                            setVal={undoable (action(val => this.props.layoutDoc[titleAccessor] = val as string), "Change Graph Title")}
-                            color={"black"}
+                            setVal={undoable(
+                                action(val => (this.props.layoutDoc[titleAccessor] = val as string)),
+                                'Change Graph Title'
+                            )}
+                            color={'black'}
                             size={Size.LARGE}
                             fillWidth
                         />
                     </div>
                     <div ref={this._piechartRef} />
-                    {selected != 'none' ? 
+                    {selected != 'none' ? (
                         <div className={'selected-data'}>
                             Selected: {selected}
                             &nbsp; &nbsp;
                             <ColorPicker
-                                tooltip={'Change Slice Color'} 
-                                type={Type.SEC} 
-                                icon={<FaFillDrip/>}
-                                selectedColor={selectedSliceColor? selectedSliceColor : this.curSliceSelected.attr("fill")}
-                                setSelectedColor={undoable (color => this.changeSelectedColor(color), "Change Selected Slice Color")}
+                                tooltip={'Change Slice Color'}
+                                type={Type.SEC}
+                                icon={<FaFillDrip />}
+                                selectedColor={selectedSliceColor ? selectedSliceColor : this.curSliceSelected.attr('fill')}
+                                setFinalColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Slice Color')}
+                                setSelectedColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Slice Color')}
                                 size={Size.XSMALL}
                             />
                         </div>
-                    : null}
+                    ) : null}
                 </div>
-                ) : <span className="chart-container"> {'first use table view to select a column to graph'}</span>
+            ) : (
+                <span className="chart-container"> {'first use table view to select a column to graph'}</span>
+            );
+        } else
+            return (
+                // when it is a brushed table and the incoming table doesn't have any rows selected
+                <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
             );
-        }
-        else return (
-            // when it is a brushed table and the incoming table doesn't have any rows selected
-            <div className='chart-container'>
-                Selected rows of data from the incoming DataVizBox to display.
-            </div>
-        )
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index f56d34fa6..5579d6d80 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -1,4 +1,4 @@
-import { action, computed, } from 'mobx';
+import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, Field, StrListCast } from '../../../../../fields/Doc';
@@ -31,44 +31,45 @@ interface TableBoxProps {
 
 @observer
 export class TableBox extends React.Component<TableBoxProps> {
-
     // filters all data to just display selected data if brushed (created from an incoming link)
     @computed get _tableData() {
         if (this.incomingLinks.length! <= 0) return this.props.pairs;
-        var guids = StrListCast(this.props.layoutDoc.rowGuids);
-        return this.props.pairs?.filter(pair => this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)]))
+        var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
+        return this.props.pairs?.filter(pair => this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)]));
     }
 
     @computed get incomingLinks() {
         return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
             .filter(link => {
-                return link.link_anchor_1 == this.props.rootDoc.draggedFrom}) // get links where this chart doc is the target of the link
+                return link.link_anchor_1 == this.props.rootDoc.draggedFrom;
+            }) // get links where this chart doc is the target of the link
             .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
     }
 
     @computed get columns() {
-        if (!this.props.layoutDoc.rowGuids) this.props.layoutDoc.rowGuids = new List<string>();
-        const guids = Cast(this.props.layoutDoc.rowGuids, listSpec("string"), null);
-        if (guids.length==0) this.props.pairs.map(row => guids.push(Utils.GenerateGuid()));
-        return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header!='' && header!=undefined) : [];
+        if (!this.props.layoutDoc.dataViz_rowGuids) this.props.layoutDoc.dataViz_rowGuids = new List<string>();
+        const guids = Cast(this.props.layoutDoc.dataViz_rowGuids, listSpec('string'), null);
+        if (guids.length == 0) this.props.pairs.map(row => guids.push(Utils.GenerateGuid()));
+        return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header != '' && header != undefined) : [];
     }
 
     // updates the 'selected' field to no longer include rows that aren't in the table
     filterSelectedRowsDown() {
-        if (!this.props.layoutDoc.selected) this.props.layoutDoc.selected = new List<string>();
-        const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null);
-        const incomingSelected = this.incomingLinks.length? StrListCast(this.incomingLinks[0].selected) : undefined;
-        if (incomingSelected){
+        if (!this.props.layoutDoc.dataViz_selectedRows) this.props.layoutDoc.dataViz_selectedRows = new List<string>();
+        const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('string'), null);
+        const incomingSelected = this.incomingLinks.length ? StrListCast(this.incomingLinks[0].dataViz_selectedRows) : undefined;
+        if (incomingSelected) {
             selected.map(guid => {
-                if (!incomingSelected.includes(guid)) selected.splice(selected.indexOf(guid), 1)}); // filters through selected to remove guids that were removed in the incoming data
+                if (!incomingSelected.includes(guid)) selected.splice(selected.indexOf(guid), 1);
+            }); // filters through selected to remove guids that were removed in the incoming data
         }
     }
 
     render() {
         this.filterSelectedRowsDown();
-        if (this._tableData.length>0){
+        if (this._tableData.length > 0) {
             return (
-                <div className="table-container" style={{height: this.props.height}}>
+                <div className="table-container" style={{ height: this.props.height }}>
                     <table className="table">
                         <thead>
                             <tr className="table-row">
@@ -83,7 +84,8 @@ export class TableBox extends React.Component<TableBoxProps> {
                                                 style={{
                                                     color: this.props.axes.slice().reverse().lastElement() === col ? 'darkgreen' : this.props.axes.lastElement() === col ? 'darkred' : undefined,
                                                     background: this.props.axes.slice().reverse().lastElement() === col ? '#E3fbdb' : this.props.axes.lastElement() === col ? '#Fbdbdb' : undefined,
-                                                    fontWeight: 'bolder', border: '3px solid black'
+                                                    fontWeight: 'bolder',
+                                                    border: '3px solid black',
                                                 }}
                                                 onPointerDown={e => {
                                                     const downX = e.clientX;
@@ -91,12 +93,13 @@ export class TableBox extends React.Component<TableBoxProps> {
                                                     setupMoveUpEvents(
                                                         {},
                                                         e,
-                                                        e => { // dragging off a column to create a brushed DataVizBox
+                                                        e => {
+                                                            // dragging off a column to create a brushed DataVizBox
                                                             const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!;
                                                             const targetCreator = (annotationOn: Doc | undefined) => {
                                                                 const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!);
-                                                                embedding._dataVizView = DataVizView.TABLE;
-                                                                embedding._data_vizAxes = new List<string>([col, col]);
+                                                                embedding._dataViz = DataVizView.TABLE;
+                                                                embedding._dataViz_axes = new List<string>([col, col]);
                                                                 embedding._draggedFrom = this.props.docView?.()!.rootDoc!;
                                                                 embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!;
                                                                 embedding.histogramBarColors = Field.Copy(this.props.layoutDoc.histogramBarColors);
@@ -142,25 +145,33 @@ export class TableBox extends React.Component<TableBoxProps> {
                         <tbody>
                             {this._tableData?.map((p, i) => {
                                 var containsData = false;
-                                var guid = StrListCast(this.props.layoutDoc.rowGuids)![this.props.pairs.indexOf(p)]
-                                this.columns.map(col => {if (p[col]!='' && p[col]!=null && p[col]!=undefined) containsData = true})
-                                if (containsData){
+                                var guid = StrListCast(this.props.layoutDoc.dataViz_rowGuids)![this.props.pairs.indexOf(p)];
+                                this.columns.map(col => {
+                                    if (p[col] != '' && p[col] != null && p[col] != undefined) containsData = true;
+                                });
+                                if (containsData) {
                                     return (
-                                        <tr key={i} className="table-row" onClick={action(e => {
+                                        <tr
+                                            key={i}
+                                            className="table-row"
+                                            onClick={action(e => {
                                                 // selecting a row
-                                                const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null);
+                                                const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('string'), null);
                                                 if (selected.includes(guid)) selected.splice(selected.indexOf(guid), 1);
                                                 else {
-                                                    selected.push(guid)};
-                                            })} style={{ background: StrListCast(this.props.layoutDoc.selected).includes(guid) ? 'lightgrey' : '', width: '110%' }}>
+                                                    selected.push(guid);
+                                                }
+                                            })}
+                                            style={{ background: StrListCast(this.props.layoutDoc.dataViz_selectedRows).includes(guid) ? 'lightgrey' : '', width: '110%' }}>
                                             {this.columns.map(col => {
-                                                    // each cell
-                                                    var colSelected = this.props.axes[0]==col || this.props.axes[1]==col;
-                                                    return (
-                                                    <td key={this.columns.indexOf(col)} style={{border: colSelected? '3px solid black' : '1px solid black', fontWeight: colSelected? 'bolder' : 'normal'}}>
+                                                // each cell
+                                                var colSelected = this.props.axes[0] == col || this.props.axes[1] == col;
+                                                return (
+                                                    <td key={this.columns.indexOf(col)} style={{ border: colSelected ? '3px solid black' : '1px solid black', fontWeight: colSelected ? 'bolder' : 'normal' }}>
                                                         {p[col]}
                                                     </td>
-                                             )})}
+                                                );
+                                            })}
                                         </tr>
                                     );
                                 }
@@ -169,12 +180,10 @@ export class TableBox extends React.Component<TableBoxProps> {
                     </table>
                 </div>
             );
-        }
-        else return (
-            // when it is a brushed table and the incoming table doesn't have any rows selected
-            <div className='chart-container'>
-                Selected rows of data from the incoming DataVizBox to display.
-            </div>
-        )
+        } else
+            return (
+                // when it is a brushed table and the incoming table doesn't have any rows selected
+                <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
+            );
     }
 }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 90fb55290..c5dead708 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -113,7 +113,7 @@ export interface DocComponentView {
     getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     restoreView?: (viewSpec: Doc) => boolean;
     scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
-    brushView?: (view: { width: number; height: number; panX: number; panY: number }) => void;
+    brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void;
     getView?: (doc: Doc) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
     addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
     addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections)
@@ -1095,6 +1095,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     pointerEvents: this.rootDoc.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
                     minWidth: 50 * ffscale(),
                     maxHeight: `max(100%, ${20 * ffscale()}px)`,
+                    background: StrCast(this.layoutDoc._backgroundColor, 'rgba(0,0,0,0.2)'),
+                    color: lightOrDark(StrCast(this.layoutDoc._backgroundColor, 'black')),
                 }}>
                 <FormattedTextBox
                     {...this.props}
@@ -1112,10 +1114,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>
         );
         const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
-        const background = StrCast(
-            SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor,
-            Doc.UserDoc().layout_showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().headingColor) : SettingsManager.Instance.userVariantColor
-        );
+        const background = StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.Instance.userVariantColor));
         const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', '');
         const titleView = !showTitle ? null : (
             <div
@@ -1204,7 +1203,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
      * @returns a function that will wrap a JSX animation element wrapping any JSX element
      */
     public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Opt<Doc>, root: Doc) {
-        const dir = presEffectDoc?.presEffectDirection ?? presEffectDoc?.followLinkAnimDirection;
+        const dir = presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection;
         const effectProps = {
             left: dir === PresEffectDirection.Left,
             right: dir === PresEffectDirection.Right,
@@ -1212,10 +1211,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             bottom: dir === PresEffectDirection.Bottom,
             opposite: true,
             delay: 0,
-            duration: Cast(presEffectDoc?.presTransition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null)),
+            duration: Cast(presEffectDoc?.presentation_transition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null)),
         };
         //prettier-ignore
-        switch (StrCast(presEffectDoc?.presEffect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
+        switch (StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
             default: 
             case PresEffect.None:       return renderDoc;
             case PresEffect.Zoom:       return <Zoom {...effectProps}>{renderDoc}</Zoom>;
@@ -1543,7 +1542,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                             <div className="webBox-textHighlight">
                                 <ObserverJsxParser autoCloseVoidElements={true} key={42} onError={(e: any) => console.log('PARSE error', e)} renderInWrapper={false} jsx={StrCast(this.textHtmlOverlay)} />
                             </div>,
-                            { presEffect: this.htmlOverlayEffect ?? 'Zoom' } as any as Doc,
+                            { presentation_effect: this.htmlOverlayEffect ?? 'Zoom' } as any as Doc,
                             this.rootDoc
                         )}{' '}
                     </Fade>
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 1b2209224..bf3c79cf9 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -267,7 +267,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         return ScriptCast(this.rootDoc.script);
     }
 
-    colorBatch:UndoManager.Batch|undefined;
+    colorBatch: UndoManager.Batch | undefined;
     /**
      * Color button
      */
@@ -285,7 +285,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 setFinalColor={value => {
                     this.colorScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: value, _readOnly_: false });
                     this.colorBatch?.end();
-                    this.colorBatch= undefined;
+                    this.colorBatch = undefined;
                 }}
                 selectedColor={curColor}
                 type={Type.PRIM}
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 61711417f..40f48dafe 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -48,8 +48,8 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
             annotationOn: this.rootDoc,
         });
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.rootDoc);
-        anchor.presXRange = new List<number>(Array.from(this._plot.options.xAxis.domain));
-        anchor.presYRange = new List<number>(Array.from(this._plot.options.yAxis.domain));
+        anchor.config_xRange = new List<number>(Array.from(this._plot.options.xAxis.domain));
+        anchor.config_yRange = new List<number>(Array.from(this._plot.options.yAxis.domain));
         if (addAsAnnotation) this.addDocument(anchor);
         return anchor;
     };
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d763753a5..c7ccd1ea0 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -22,7 +22,6 @@ import { DocumentType } from '../../documents/DocumentTypes';
 import { Networking } from '../../Network';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager } from '../../util/DragManager';
-import { SnappingManager } from '../../util/SnappingManager';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../../views/ContextMenu';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
@@ -80,10 +79,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._getAnchor?.(this._savedAnnotations, false) ?? // use marquee anchor, otherwise, save zoom/pan as anchor
             Docs.Create.ConfigDocument({
                 title: 'ImgAnchor:' + this.rootDoc.title,
-                presPanX: NumCast(this.layoutDoc._freeform_panX),
-                presPanY: NumCast(this.layoutDoc._freeform_panY),
-                presViewScale: Cast(this.layoutDoc._freeform_scale, 'number', null),
-                presTransition: 1000,
+                config_panX: NumCast(this.layoutDoc._freeform_panX),
+                config_panY: NumCast(this.layoutDoc._freeform_panY),
+                config_viewScale: Cast(this.layoutDoc._freeform_scale, 'number', null),
+                presentation_transition: 1000,
                 annotationOn: this.rootDoc,
             });
         if (anchor) {
@@ -295,7 +294,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     choosePath(url: URL) {
         const lower = url.href.toLowerCase();
         if (url.protocol === 'data') return url.href;
-        if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf("dashblobstore") === -1) return Utils.CorsProxy(url.href);
+        if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf('dashblobstore') === -1) return Utils.CorsProxy(url.href);
         if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return `/assets/unknown-file-icon-hi.png`;
 
         const ext = extname(url.href);
@@ -414,7 +413,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @computed get content() {
         TraceMobx();
 
-        const backAlpha = DashColor(this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor)).alpha();
+        const backColor = DashColor(this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor));
+        const backAlpha = backColor.red() === 0 && backColor.green() === 0 && backColor.blue() === 0 ? backColor.alpha() : 1;
         const srcpath = this.layoutDoc.hideImage ? '' : this.paths[0];
         const fadepath = this.layoutDoc.hideImage ? '' : this.paths.lastElement();
         const { nativeWidth, nativeHeight, nativeOrientation } = this.nativeSize;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index a6712a3db..673f711be 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -4,7 +4,7 @@ import { Doc, Field, FieldResult } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { RichTextField } from '../../../fields/RichTextField';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { DocCast, NumCast } from '../../../fields/Types';
+import { DocCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { returnAll, returnAlways, returnTrue } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
@@ -13,6 +13,7 @@ import { CompiledScript, CompileScript, ScriptOptions } from '../../util/Scripti
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
+import { DocumentIconContainer } from './DocumentIcon';
 import { OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
@@ -20,10 +21,6 @@ import { ImageBox } from './ImageBox';
 import './KeyValueBox.scss';
 import { KeyValuePair } from './KeyValuePair';
 import React = require('react');
-import { DocumentManager } from '../../util/DocumentManager';
-import { ScriptingGlobals } from '../../util/ScriptingGlobals';
-import { ScriptingRepl } from '../ScriptingRepl';
-import { DocumentIconContainer } from './DocumentIcon';
 
 export type KVPScript = {
     script: CompiledScript;
@@ -147,7 +144,15 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         const self = this;
         const keys = Object.keys(ids).slice();
         //for (const key of [...keys.filter(id => id !== 'layout' && !id.includes('_')).sort(), ...keys.filter(id => id === 'layout' || id.includes('_')).sort()]) {
-        for (const key of keys.sort()) {
+        for (const key of keys.sort((a: string, b: string) => {
+            const a_ = a.split('_')[0];
+            const b_ = b.split('_')[0];
+            if (a_ < b_) return -1;
+            if (a_ > b_) return 1;
+            if (a === a_) return -1;
+            if (b === b_) return 1;
+            return a === b ? 0 : a < b ? -1 : 1;
+        })) {
             rows.push(
                 <KeyValuePair
                     doc={realDoc}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 710d41471..efb949a47 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -18,7 +18,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.props.setContentView?.(this);
     }
     render() {
-        if (this.dataDoc.treeViewOpen === undefined) setTimeout(() => (this.dataDoc.treeViewOpen = true));
+        if (this.dataDoc.treeView_Open === undefined) setTimeout(() => (this.dataDoc.treeView_Open = true));
         return (
             <div className={`linkBox-container${this.props.isContentActive() ? '-interactive' : ''}`} style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }}>
                 <ComparisonBox
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 4919ee94c..cf017d746 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -102,7 +102,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
     @computed get inlineTextAnnotations() {
-        return this.allMapMarkers.filter(a => a.textInlineAnnotations);
+        return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
     }
 
     @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index a9154c5bb..b3ae8242d 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -100,7 +100,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
     @computed get inlineTextAnnotations() {
-        return this.allMapMarkers.filter(a => a.textInlineAnnotations);
+        return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
     }
 
     @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index fd4c6366b..758b49655 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -213,7 +213,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
     }
 
-    brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._pdfViewer?.brushView(view);
+    brushView = (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => this._pdfViewer?.brushView(view, transTime);
 
     sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
         if (DocListCast(this.props.Document[this.props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
@@ -224,7 +224,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
     focus = (anchor: Doc, options: DocFocusOptions) => {
         this._initialScrollTarget = anchor;
-        return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.y, NumCast(anchor.presViewScroll)), options);
+        return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.y, NumCast(anchor.config_scrollTop)), options);
     };
 
     getView = async (doc: Doc) => {
@@ -247,7 +247,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const anchor = annoAnchor ?? docAnchor();
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.rootDoc);
         anchor.text = ele?.textContent ?? '';
-        anchor.textHtml = ele?.innerHTML;
+        anchor.text_html = ele?.innerHTML;
         if (addAsAnnotation || annoAnchor) {
             this.addDocument(anchor);
         }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index f5df42161..febf8341e 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -51,7 +51,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     public static sidebarResizerWidth = 5;
     static webStyleSheet = addStyleSheet();
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
-    private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }) => void);
+    private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void);
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     private _outerRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposers: { [name: string]: IReactionDisposer } = {};
@@ -91,7 +91,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return DocListCast(this.dataDoc[this.annotationKey]);
     }
     @computed get inlineTextAnnotations() {
-        return this.allAnnotations.filter(a => a.textInlineAnnotations);
+        return this.allAnnotations.filter(a => a.text_inlineAnnotations);
     }
     @computed get webField() {
         return Cast(this.rootDoc[this.props.fieldKey], WebField)?.url;
@@ -133,7 +133,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 (this._iframe?.contentWindow as any)?.find(searchString, false, bwd, true);
             }
         } catch (e) {
-            console.log("WebBox search error", e)
+            console.log('WebBox search error', e);
         }
         return true;
     };
@@ -281,8 +281,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this._savedAnnotations;
     };
 
-    setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => (this._setBrushViewer = func);
-    brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._setBrushViewer?.(view);
+    setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => (this._setBrushViewer = func);
+    brushView = (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => this._setBrushViewer?.(view, transTime);
     focus = (anchor: Doc, options: DocFocusOptions) => {
         if (anchor !== this.rootDoc && this._outerRef.current) {
             const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
@@ -303,7 +303,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     getView = (doc: Doc) => {
         if (Doc.AreProtosEqual(doc, this.rootDoc)) return new Promise<Opt<DocumentView>>(res => res(this.props.DocumentView?.()));
         if (this.rootDoc.layout_fieldKey === 'layout_icon') this.props.DocumentView?.().iconify();
-        const webUrl = WebCast(doc.presData)?.url;
+        const webUrl = WebCast(doc.config_data)?.url;
         if (this._url && webUrl && webUrl.href !== this._url) this.setData(webUrl.href);
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false);
         return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
@@ -334,7 +334,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             });
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: pinProps?.pinData ? true : false, pannable: true } }, this.rootDoc);
         anchor.text = ele?.textContent ?? '';
-        anchor.textHtml = ele?.innerHTML;
+        anchor.text_html = ele?.innerHTML;
         //addAsAnnotation &&
         this.addDocumentWrapper(anchor);
         return anchor;
@@ -761,7 +761,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
-        (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.presData = new WebField(this._url)));
+        (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.config_data = new WebField(this._url)));
         return this.addDocument(doc, annotationKey);
     };
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 1dcc445e8..eea4f513e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -417,7 +417,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             DocListCast(Doc.MyPublishedDocs.data).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
             tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
             this._editorView?.dispatch(tr);
-            this.prepareForTyping();
+            // this.prepareForTyping();
         }
         oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
     };
@@ -730,7 +730,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
     @undoBatch
     showTargetTrail = (anchor: Doc) => {
-        const trail = DocCast(anchor.presTrail);
+        const trail = DocCast(anchor.presentationTrail);
         if (trail) {
             Doc.ActivePresentation = trail;
             this.props.addDocTab(trail, OpenWhere.replaceRight);
@@ -1237,7 +1237,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         this._disposers.selected = reaction(
             () => this.props.isSelected(),
             action(selected => {
-                selected && this.prepareForTyping();
+                //selected && setTimeout(() => this.prepareForTyping());
                 if (FormattedTextBox._globalHighlights.has('Bold Text')) {
                     this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed
                 }
@@ -1550,18 +1550,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
     // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
     prepareForTyping = () => {
-        this._editorView?.dispatch(
-            this._editorView?.state.tr.setStoredMarks([
-                ...(this._editorView.state.storedMarks?.filter(mark => ![schema.marks.em, schema.marks.underline, schema.marks.pFontFamily, schema.marks.pFontSize, schema.marks.strong, schema.marks.pFontColor].includes(mark.type)) ?? []),
-                ...[schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })],
-                ...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
-                ...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []),
-                ...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []),
-                ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontFamily) })] : []),
-                ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []),
-                ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []),
-            ])
-        );
+        if (!this._editorView) return;
+        const docDefaultMarks = [
+            ...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
+            ...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []),
+            ...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []),
+            ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontFamily) })] : []),
+            ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []),
+            ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []),
+            ...[schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })],
+        ];
+        this._editorView?.dispatch(this._editorView?.state.tr.setStoredMarks(docDefaultMarks));
     };
 
     @action
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 112a0d87e..ec11079b4 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -334,7 +334,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
 
     //Command to create a blank space
     bind('Space', (state: EditorState, dispatch: (tx: Transaction) => void) => {
-        if (GetEffectiveAcl(props.DataDoc) != AclEdit && GetEffectiveAcl(props.DataDoc) != AclAugment && GetEffectiveAcl(props.DataDoc) != AclAdmin) return true;
+        if (props.DataDoc && GetEffectiveAcl(props.DataDoc) != AclEdit && GetEffectiveAcl(props.DataDoc) != AclAugment && GetEffectiveAcl(props.DataDoc) != AclAdmin) return true;
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
         dispatch(splitMetadata(marks, state.tr));
         return false;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 7c3e4baad..9c46459b0 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -104,7 +104,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     _disposer: IReactionDisposer | undefined;
     componentDidMount() {
         this._disposer = reaction(
-            () => SelectionManager.Views(),
+            () => SelectionManager.Views().slice(),
             views => this.updateMenu(undefined, undefined, undefined)
         );
     }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index aaedffc77..05f59d8fe 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -74,7 +74,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     constructor(props: any) {
         super(props);
         if (!PresBox.navigateToDocScript) {
-            PresBox.navigateToDocScript = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)')!;
+            PresBox.navigateToDocScript = ScriptField.MakeFunction('navigateToDoc(self.presentation_targetDoc, self)')!;
         }
     }
 
@@ -116,7 +116,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return DocListCast(this.rootDoc[this.presFieldKey]);
     }
     @computed get tagDocs() {
-        return this.childDocs.map(doc => Cast(doc.presentationTargetDoc, Doc, null));
+        return this.childDocs.map(doc => Cast(doc.presentation_targetDoc, Doc, null));
     }
     @computed get itemIndex() {
         return NumCast(this.rootDoc._itemIndex);
@@ -125,10 +125,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return DocCast(this.childDocs[NumCast(this.rootDoc._itemIndex)]);
     }
     @computed get targetDoc() {
-        return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
+        return Cast(this.activeItem?.presentation_targetDoc, Doc, null);
     }
     public static targetRenderedDoc = (doc: Doc) => {
-        const targetDoc = Cast(doc?.presentationTargetDoc, Doc, null);
+        const targetDoc = Cast(doc?.presentation_targetDoc, Doc, null);
         return targetDoc?.layout_unrendered ? DocCast(targetDoc.annotationOn) : targetDoc;
     };
     @computed get scrollable() {
@@ -149,7 +149,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get selectedDoc() {
         return this.selectedDocumentView?.rootDoc;
     }
-    isActiveItemTarget = (layoutDoc: Doc) => this.activeItem?.presentationTargetDoc === layoutDoc;
+    isActiveItemTarget = (layoutDoc: Doc) => this.activeItem?.presentation_targetDoc === layoutDoc;
     clearSelectedArray = () => this.selectedArray.clear();
     addToSelectedArray = action((doc: Doc) => this.selectedArray.add(doc));
     removeFromSelectedArray = action((doc: Doc) => this.selectedArray.delete(doc));
@@ -196,18 +196,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         this.turnOffEdit(true);
         this._disposers.selection = reaction(
-            () => SelectionManager.Views(),
+            () => SelectionManager.Views().slice(),
             views => (!PresBox.Instance || views.some(view => view.props.Document === this.rootDoc)) && this.updateCurrentPresentation(),
             { fireImmediately: true }
         );
         this._disposers.editing = reaction(
-            () => this.layoutDoc.presStatus === PresStatus.Edit,
+            () => this.layoutDoc.presentation_status === PresStatus.Edit,
             editing => {
                 if (editing) {
                     this.childDocs.forEach(doc => {
-                        if (doc.presIndexed !== undefined) {
+                        if (doc.presentation_indexed !== undefined) {
                             this.progressivizedItems(doc)?.forEach(indexedDoc => (indexedDoc.opacity = undefined));
-                            doc.presIndexed = Math.min(this.progressivizedItems(doc)?.length ?? 0, 1);
+                            doc.presentation_indexed = Math.min(this.progressivizedItems(doc)?.length ?? 0, 1);
                         }
                     });
                 }
@@ -224,10 +224,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     _mediaTimer!: [NodeJS.Timeout, Doc];
     // 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played
     startTempMedia = (targetDoc: Doc, activeItem: Doc) => {
-        const duration: number = NumCast(activeItem.presEndTime) - NumCast(activeItem.presStartTime);
+        const duration: number = NumCast(activeItem.config_clipEnd) - NumCast(activeItem.config_clipStart);
         if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as any)) {
             const targMedia = DocumentManager.Instance.getDocumentView(targetDoc);
-            targMedia?.ComponentView?.playFrom?.(NumCast(activeItem.presStartTime), NumCast(activeItem.presStartTime) + duration);
+            targMedia?.ComponentView?.playFrom?.(NumCast(activeItem.config_clipStart), NumCast(activeItem.config_clipStart) + duration);
         }
     };
 
@@ -251,12 +251,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             (nextSelected: number, force = false) =>
             () => {
                 if (nextSelected < this.childDocs.length) {
-                    if (force || this.childDocs[nextSelected].groupWithUp) {
+                    if (force || this.childDocs[nextSelected].presentation_groupWithUp) {
                         this.addToSelectedArray(this.childDocs[nextSelected]);
-                        const serial = nextSelected + 1 < this.childDocs.length && NumCast(this.childDocs[nextSelected + 1].groupWithUp) > 1;
+                        const serial = nextSelected + 1 < this.childDocs.length && NumCast(this.childDocs[nextSelected + 1].presentation_groupWithUp) > 1;
                         if (serial) {
                             this.gotoDocument(nextSelected, this.activeItem, true, async () => {
-                                const waitTime = NumCast(this.activeItem.presDuration) - NumCast(this.activeItem.presTransition);
+                                const waitTime = NumCast(this.activeItem.presentation_duration) - NumCast(this.activeItem.presentation_transition);
                                 await new Promise<void>(res => setTimeout(() => res(), Math.max(0, waitTime)));
                                 doGroupWithUp(nextSelected + 1)();
                             });
@@ -274,7 +274,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // docs within a slide target that will be progressively revealed
     progressivizedItems = (doc: Doc) => {
         const targetList = PresBox.targetRenderedDoc(doc);
-        if (doc.presIndexed !== undefined && targetList) {
+        if (doc.presentation_indexed !== undefined && targetList) {
             const listItems = (Cast(targetList[Doc.LayoutFieldKey(targetList)], listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[]) ?? DocListCast(targetList[Doc.LayoutFieldKey(targetList) + '_annotations']);
             return listItems.filter(doc => !doc.layout_unrendered);
         }
@@ -283,7 +283,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     next = () => {
         const progressiveReveal = (first: boolean) => {
-            const presIndexed = Cast(this.activeItem?.presIndexed, 'number', null);
+            const presIndexed = Cast(this.activeItem?.presentation_indexed, 'number', null);
             if (presIndexed !== undefined) {
                 const targetRenderedDoc = PresBox.targetRenderedDoc(this.activeItem);
                 targetRenderedDoc._dataTransition = 'all 1s';
@@ -296,8 +296,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         const targetView = listItems && DocumentManager.Instance.getFirstDocumentView(listItemDoc);
                         Doc.linkFollowUnhighlight();
                         Doc.HighlightDoc(listItemDoc);
-                        listItemDoc.presEffect = this.activeItem.presBulletEffect;
-                        listItemDoc.presTransition = 500;
+                        listItemDoc.presentation_effect = this.activeItem.presBulletEffect;
+                        listItemDoc.presentation_transition = 500;
                         targetView?.setAnimEffect(listItemDoc, 500);
                         if (targetView?.docView && this.activeItem.presBulletExpand) {
                             targetView.docView._animateScalingTo = 1.2;
@@ -308,7 +308,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             });
                         }
                         listItemDoc.opacity = undefined;
-                        this.activeItem.presIndexed = presIndexed + 1;
+                        this.activeItem.presentation_indexed = presIndexed + 1;
                     }
                     return true;
                 }
@@ -322,7 +322,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.nextSlide(curLast + 1 === this.childDocs.length ? (this.layoutDoc.presLoop ? 0 : curLast) : curLast + 1);
             progressiveReveal(true); // shows first progressive document, but without a transition effect
         } else {
-            if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) {
+            if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presentation_status === PresStatus.Edit)) {
                 // Case 2: Last slide and presLoop is toggled ON or it is in Edit mode
                 this.nextSlide(0);
                 progressiveReveal(true); // shows first progressive document, but without a transition effect
@@ -338,9 +338,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeItem: Doc = this.activeItem;
         let prevSelected = this.itemIndex;
         // Functionality for group with up
-        let didZoom = activeItem.presMovement;
-        for (; prevSelected > 0 && this.childDocs[Math.max(0, prevSelected - 1)].groupWithUp; prevSelected--) {
-            didZoom = didZoom === 'none' ? this.childDocs[prevSelected].presMovement : didZoom;
+        let didZoom = activeItem.presentation_movement;
+        for (; prevSelected > 0 && this.childDocs[Math.max(0, prevSelected - 1)].presentation_groupWithUp; prevSelected--) {
+            didZoom = didZoom === 'none' ? this.childDocs[prevSelected].presentation_movement : didZoom;
         }
         if (activeItem && this.childDocs[this.itemIndex - 1] !== undefined) {
             // Case 2: There are no other frames so it should go to the previous slide
@@ -361,20 +361,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         Doc.UnBrushAllDocs();
         if (index >= 0 && index < this.childDocs.length) {
             this.rootDoc._itemIndex = index;
-            if (from?.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) {
+            if (from?.mediaStopTriggerList && this.layoutDoc.presentation_status !== PresStatus.Edit) {
                 DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia);
             }
-            if (from?.mediaStop === 'auto' && this.layoutDoc.presStatus !== PresStatus.Edit) {
-                this.stopTempMedia(from.presentationTargetDoc);
+            if (from?.mediaStop === 'auto' && this.layoutDoc.presentation_status !== PresStatus.Edit) {
+                this.stopTempMedia(from.presentation_targetDoc);
             }
             // If next slide is audio / video 'Play automatically' then the next slide should be played
-            if (this.layoutDoc.presStatus !== PresStatus.Edit && (this.targetDoc.type === DocumentType.AUDIO || this.targetDoc.type === DocumentType.VID) && this.activeItem.mediaStart === 'auto') {
+            if (this.layoutDoc.presentation_status !== PresStatus.Edit && (this.targetDoc.type === DocumentType.AUDIO || this.targetDoc.type === DocumentType.VID) && this.activeItem.mediaStart === 'auto') {
                 this.startTempMedia(this.targetDoc, this.activeItem);
             }
             if (!group) this.clearSelectedArray();
             this.childDocs[index] && this.addToSelectedArray(this.childDocs[index]); //Update selected array
             this.turnOffEdit();
-            this.navigateToActiveItem(finished); //Handles movement to element only when presTrail is list
+            this.navigateToActiveItem(finished); //Handles movement to element only when presentationTrail is list
             this.doHideBeforeAfter(); //Handles hide after/before
         }
     });
@@ -398,33 +398,33 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     playAnnotation = (anno: AudioField) => {};
     @action
-    static restoreTargetDocView(bestTargetView: Opt<DocumentView>, activeItem: Doc, transTime: number, pinDocLayout: boolean = BoolCast(activeItem.presPinLayout), pinDataTypes?: pinDataTypes, targetDoc?: Doc) {
+    static restoreTargetDocView(bestTargetView: Opt<DocumentView>, activeItem: Doc, transTime: number, pinDocLayout: boolean = BoolCast(activeItem.config_pinLayout), pinDataTypes?: pinDataTypes, targetDoc?: Doc) {
         const bestTarget = bestTargetView?.rootDoc ?? (targetDoc?.layout_unrendered ? DocCast(targetDoc?.annotationOn) : targetDoc);
         if (!bestTarget) return;
         let changed = false;
         if (pinDocLayout) {
             if (
-                bestTarget.x !== NumCast(activeItem.presX, NumCast(bestTarget.x)) ||
-                bestTarget.y !== NumCast(activeItem.presY, NumCast(bestTarget.y)) ||
-                bestTarget.rotation !== NumCast(activeItem.presRotation, NumCast(bestTarget.rotation)) ||
-                bestTarget.width !== NumCast(activeItem.presWidth, NumCast(bestTarget.width)) ||
-                bestTarget.height !== NumCast(activeItem.presHeight, NumCast(bestTarget.height))
+                bestTarget.x !== NumCast(activeItem.config_x, NumCast(bestTarget.x)) ||
+                bestTarget.y !== NumCast(activeItem.config_y, NumCast(bestTarget.y)) ||
+                bestTarget.rotation !== NumCast(activeItem.config_rotation, NumCast(bestTarget.rotation)) ||
+                bestTarget.width !== NumCast(activeItem.config_width, NumCast(bestTarget.width)) ||
+                bestTarget.height !== NumCast(activeItem.config_height, NumCast(bestTarget.height))
             ) {
                 bestTarget._dataTransition = `all ${transTime}ms`;
-                bestTarget.x = NumCast(activeItem.presX, NumCast(bestTarget.x));
-                bestTarget.y = NumCast(activeItem.presY, NumCast(bestTarget.y));
-                bestTarget.rotation = NumCast(activeItem.presRotation, NumCast(bestTarget.rotation));
-                bestTarget.width = NumCast(activeItem.presWidth, NumCast(bestTarget.width));
-                bestTarget.height = NumCast(activeItem.presHeight, NumCast(bestTarget.height));
+                bestTarget.x = NumCast(activeItem.config_x, NumCast(bestTarget.x));
+                bestTarget.y = NumCast(activeItem.config_y, NumCast(bestTarget.y));
+                bestTarget.rotation = NumCast(activeItem.config_rotation, NumCast(bestTarget.rotation));
+                bestTarget.width = NumCast(activeItem.config_width, NumCast(bestTarget.width));
+                bestTarget.height = NumCast(activeItem.config_height, NumCast(bestTarget.height));
                 setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
                 changed = true;
             }
         }
 
-        const activeFrame = activeItem.presActiveFrame ?? activeItem.presCurrentFrame;
+        const activeFrame = activeItem.config_activeFrame ?? activeItem.config_currentFrame;
         if (activeFrame !== undefined) {
-            const transTime = NumCast(activeItem.presTransition, 500);
-            const acontext = activeItem.presActiveFrame !== undefined ? DocCast(DocCast(activeItem.presentationTargetDoc).embedContainer) : DocCast(activeItem.presentationTargetDoc);
+            const transTime = NumCast(activeItem.presentation_transition, 500);
+            const acontext = activeItem.config_activeFrame !== undefined ? DocCast(DocCast(activeItem.presentation_targetDoc).embedContainer) : DocCast(activeItem.presentation_targetDoc);
             const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext;
             if (context) {
                 const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
@@ -434,73 +434,73 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
             }
         }
-        if ((pinDataTypes?.dataview && activeItem.presData !== undefined) || (!pinDataTypes && activeItem.presData !== undefined)) {
+        if ((pinDataTypes?.dataview && activeItem.config_data !== undefined) || (!pinDataTypes && activeItem.config_data !== undefined)) {
             bestTarget._dataTransition = `all ${transTime}ms`;
             const fkey = Doc.LayoutFieldKey(bestTarget);
             const setData = bestTargetView?.ComponentView?.setData;
-            if (setData) setData(activeItem.presData);
-            else Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
-            bestTarget[fkey + '_usePath'] = activeItem.presUsePath;
+            if (setData) setData(activeItem.config_data);
+            else Doc.GetProto(bestTarget)[fkey] = activeItem.config_data instanceof ObjectField ? activeItem.config_data[Copy]() : activeItem.config_data;
+            bestTarget[fkey + '_usePath'] = activeItem.config_usePath;
             setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
         }
-        if (pinDataTypes?.datarange || (!pinDataTypes && activeItem.presXRange !== undefined)) {
-            if (bestTarget.xRange !== activeItem.presXRange) {
-                bestTarget.xRange = (activeItem.presXRange as ObjectField)?.[Copy]();
+        if (pinDataTypes?.datarange || (!pinDataTypes && activeItem.config_xRange !== undefined)) {
+            if (bestTarget.xRange !== activeItem.config_xRange) {
+                bestTarget.xRange = (activeItem.config_xRange as ObjectField)?.[Copy]();
                 changed = true;
             }
-            if (bestTarget.yRange !== activeItem.presYRange) {
-                bestTarget.yRange = (activeItem.presYRange as ObjectField)?.[Copy]();
+            if (bestTarget.yRange !== activeItem.config_yRange) {
+                bestTarget.yRange = (activeItem.config_yRange as ObjectField)?.[Copy]();
                 changed = true;
             }
         }
-        if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.presClipWidth !== undefined)) {
+        if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.config_clipWidth !== undefined)) {
             const fkey = '_' + Doc.LayoutFieldKey(bestTarget);
-            if (bestTarget[fkey + '_clipWidth'] !== activeItem.presClipWidth) {
-                bestTarget[fkey + '_clipWidth'] = activeItem.presClipWidth;
+            if (bestTarget[fkey + '_clipWidth'] !== activeItem.config_clipWidth) {
+                bestTarget[fkey + '_clipWidth'] = activeItem.config_clipWidth;
                 changed = true;
             }
         }
-        if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.presStartTime !== undefined)) {
-            if (bestTarget._layout_currentTimecode !== activeItem.presStartTime) {
-                bestTarget._layout_currentTimecode = activeItem.presStartTime;
+        if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.config_clipStart !== undefined)) {
+            if (bestTarget._layout_currentTimecode !== activeItem.config_clipStart) {
+                bestTarget._layout_currentTimecode = activeItem.config_clipStart;
                 changed = true;
             }
         }
-        if (pinDataTypes?.inkable || (!pinDataTypes && (activeItem.presFillColor !== undefined || activeItem.color !== undefined))) {
-            if (bestTarget.fillColor !== activeItem.presFillColor) {
-                Doc.GetProto(bestTarget).fillColor = activeItem.presFillColor;
+        if (pinDataTypes?.inkable || (!pinDataTypes && (activeItem.config_fillColor !== undefined || activeItem.color !== undefined))) {
+            if (bestTarget.fillColor !== activeItem.config_fillColor) {
+                Doc.GetProto(bestTarget).fillColor = activeItem.config_fillColor;
                 changed = true;
             }
-            if (bestTarget.color !== activeItem.presColor) {
-                Doc.GetProto(bestTarget).color = activeItem.presColor;
+            if (bestTarget.color !== activeItem.config_color) {
+                Doc.GetProto(bestTarget).color = activeItem.config_color;
                 changed = true;
             }
             if (bestTarget.width !== activeItem.width) {
-                bestTarget._width = NumCast(activeItem.presWidth, NumCast(bestTarget.width));
+                bestTarget._width = NumCast(activeItem.config_width, NumCast(bestTarget.width));
                 changed = true;
             }
             if (bestTarget.height !== activeItem.height) {
-                bestTarget._height = NumCast(activeItem.presHeight, NumCast(bestTarget.height));
+                bestTarget._height = NumCast(activeItem.config_height, NumCast(bestTarget.height));
                 changed = true;
             }
         }
-        if ((pinDataTypes?.type_collection && activeItem.presViewType !== undefined) || (!pinDataTypes && activeItem.presViewType !== undefined)) {
-            if (bestTarget._type_collection !== activeItem.presViewType) {
-                bestTarget._type_collection = activeItem.presViewType;
+        if ((pinDataTypes?.type_collection && activeItem.config_viewType !== undefined) || (!pinDataTypes && activeItem.config_viewType !== undefined)) {
+            if (bestTarget._type_collection !== activeItem.config_viewType) {
+                bestTarget._type_collection = activeItem.config_viewType;
                 changed = true;
             }
         }
 
-        if ((pinDataTypes?.filters && activeItem.presDocFilters !== undefined) || (!pinDataTypes && activeItem.presDocFilters !== undefined)) {
-            if (bestTarget.childFilters !== activeItem.presDocFilters) {
-                bestTarget.childFilters = ObjectField.MakeCopy(activeItem.presDocFilters as ObjectField) || new List<string>([]);
+        if ((pinDataTypes?.filters && activeItem.config_docFilters !== undefined) || (!pinDataTypes && activeItem.config_docFilters !== undefined)) {
+            if (bestTarget.childFilters !== activeItem.config_docFilters) {
+                bestTarget.childFilters = ObjectField.MakeCopy(activeItem.config_docFilters as ObjectField) || new List<string>([]);
                 changed = true;
             }
         }
 
-        if ((pinDataTypes?.pivot && activeItem.presPivotField !== undefined) || (!pinDataTypes && activeItem.presPivotField !== undefined)) {
-            if (bestTarget.pivotField !== activeItem.presPivotField) {
-                bestTarget.pivotField = activeItem.presPivotField;
+        if ((pinDataTypes?.pivot && activeItem.config_pivotField !== undefined) || (!pinDataTypes && activeItem.config_pivotField !== undefined)) {
+            if (bestTarget.pivotField !== activeItem.config_pivotField) {
+                bestTarget.pivotField = activeItem.config_pivotField;
                 bestTarget._prevFilterIndex = 1; // need to revisit this...see CollectionTimeView
                 changed = true;
             }
@@ -509,21 +509,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             changed = true;
         }
 
-        if (pinDataTypes?.scrollable || (!pinDataTypes && activeItem.presViewScroll !== undefined)) {
-            if (bestTarget._layout_scrollTop !== activeItem.presViewScroll) {
-                bestTarget._layout_scrollTop = activeItem.presViewScroll;
+        if (pinDataTypes?.scrollable || (!pinDataTypes && activeItem.config_scrollTop !== undefined)) {
+            if (bestTarget._layout_scrollTop !== activeItem.config_scrollTop) {
+                bestTarget._layout_scrollTop = activeItem.config_scrollTop;
                 changed = true;
-                const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
+                const contentBounds = Cast(activeItem.config_viewBounds, listSpec('number'));
                 if (contentBounds) {
                     const dv = DocumentManager.Instance.getDocumentView(bestTarget)?.ComponentView;
-                    dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] });
+                    dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] }, transTime);
                 }
             }
         }
-        if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.presAnnotations !== undefined)) {
+        if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.config_annotations !== undefined)) {
             const fkey = Doc.LayoutFieldKey(bestTarget);
             const oldItems = DocListCast(bestTarget[fkey + '_annotations']).filter(doc => doc.layout_unrendered);
-            const newItems = DocListCast(activeItem.presAnnotations).map(doc => {
+            const newItems = DocListCast(activeItem.config_annotations).map(doc => {
                 doc.hidden = false;
                 return doc;
             });
@@ -536,11 +536,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const newList = new List<Doc>([...oldItems, ...hiddenItems, ...newItems]);
             Doc.GetProto(bestTarget)[fkey + '_annotations'] = newList;
         }
-        if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.presPinLayoutData !== undefined)) {
+        if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.config_pinLayoutData !== undefined)) {
             changed = true;
             const layoutField = Doc.LayoutFieldKey(bestTarget);
             const transitioned = new Set<Doc>();
-            StrListCast(activeItem.presPinLayoutData)
+            StrListCast(activeItem.config_pinLayoutData)
                 .map(str => JSON.parse(str) as { id: string; x: number; y: number; back: string; fill: string; w: number; h: number; data: string; text: string })
                 .forEach(async data => {
                     const doc = DocCast(DocServer.GetCachedRefField(data.id));
@@ -562,8 +562,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 });
             setTimeout(() => Array.from(transitioned).forEach(action(doc => (doc._dataTransition = undefined))), transTime + 10);
         }
-        if ((pinDataTypes?.pannable || (!pinDataTypes && (activeItem.presPinViewBounds !== undefined || activeItem.presPanX !== undefined || activeItem.presViewScale !== undefined))) && !bestTarget._isGroup) {
-            const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
+        if ((pinDataTypes?.pannable || (!pinDataTypes && (activeItem.config_viewBounds !== undefined || activeItem.config_panX !== undefined || activeItem.config_viewScale !== undefined))) && !bestTarget._isGroup) {
+            const contentBounds = Cast(activeItem.config_viewBounds, listSpec('number'));
             if (contentBounds) {
                 const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] };
                 bestTarget._freeform_panX = viewport.panX;
@@ -572,14 +572,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 if (dv) {
                     changed = true;
                     const computedScale = NumCast(activeItem.presZoom, 1) * Math.min(dv.props.PanelWidth() / viewport.width, dv.props.PanelHeight() / viewport.height);
-                    activeItem.presMovement === PresMovement.Zoom && (bestTarget._freeform_scale = computedScale);
-                    dv.ComponentView?.brushView?.(viewport);
+                    activeItem.presentation_movement === PresMovement.Zoom && (bestTarget._freeform_scale = computedScale);
+                    dv.ComponentView?.brushView?.(viewport, transTime);
                 }
             } else {
-                if (bestTarget._freeform_panX !== activeItem.presPanX || bestTarget._freeform_panY !== activeItem.presPanY || bestTarget._freeform_scale !== activeItem.presViewScale) {
-                    bestTarget._freeform_panX = activeItem.presPanX ?? bestTarget._freeform_panX;
-                    bestTarget._freeform_panY = activeItem.presPanY ?? bestTarget._freeform_panY;
-                    bestTarget._freeform_scale = activeItem.presViewScale ?? bestTarget._freeform_scale;
+                if (bestTarget._freeform_panX !== activeItem.config_panX || bestTarget._freeform_panY !== activeItem.config_panY || bestTarget._freeform_scale !== activeItem.config_viewScale) {
+                    bestTarget._freeform_panX = activeItem.config_panX ?? bestTarget._freeform_panX;
+                    bestTarget._freeform_panY = activeItem.config_panY ?? bestTarget._freeform_panY;
+                    bestTarget._freeform_scale = activeItem.config_viewScale ?? bestTarget._freeform_scale;
                     changed = true;
                 }
             }
@@ -594,17 +594,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /// target doc when navigating to it.
     @action
     static pinDocView(pinDoc: Doc, pinProps: PinProps, targetDoc: Doc) {
+        pinDoc.presentation = true;
+        pinDoc.config = '';
         if (pinProps.pinDocLayout) {
-            pinDoc.presPinLayout = true;
-            pinDoc.presX = NumCast(targetDoc.x);
-            pinDoc.presY = NumCast(targetDoc.y);
-            pinDoc.presRotation = NumCast(targetDoc.rotation);
-            pinDoc.presWidth = NumCast(targetDoc.width);
-            pinDoc.presHeight = NumCast(targetDoc.height);
+            pinDoc.config_pinLayout = true;
+            pinDoc.config_x = NumCast(targetDoc.x);
+            pinDoc.config_y = NumCast(targetDoc.y);
+            pinDoc.config_rotation = NumCast(targetDoc.rotation);
+            pinDoc.config_width = NumCast(targetDoc.width);
+            pinDoc.config_height = NumCast(targetDoc.height);
         }
         if (pinProps.pinAudioPlay) pinDoc.presPlayAudio = true;
         if (pinProps.pinData) {
-            pinDoc.presPinData =
+            pinDoc.config_pinData =
                 pinProps.pinData.scrollable ||
                 pinProps.pinData.temporal ||
                 pinProps.pinData.pannable ||
@@ -616,30 +618,30 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 pinProps?.activeFrame !== undefined;
             const fkey = Doc.LayoutFieldKey(targetDoc);
             if (pinProps.pinData.dataview) {
-                pinDoc.presUsePath = targetDoc[fkey + '_usePath'];
-                pinDoc.presData = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data;
+                pinDoc.config_usePath = targetDoc[fkey + '_usePath'];
+                pinDoc.config_data = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data;
             }
             if (pinProps.pinData.dataannos) {
                 const fkey = Doc.LayoutFieldKey(targetDoc);
-                pinDoc.presAnnotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '_annotations']).filter(doc => !doc.layout_unrendered));
+                pinDoc.config_annotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '_annotations']).filter(doc => !doc.layout_unrendered));
             }
             if (pinProps.pinData.inkable) {
-                pinDoc.presFillColor = targetDoc.fillColor;
-                pinDoc.presColor = targetDoc.color;
-                pinDoc.presWidth = targetDoc._width;
-                pinDoc.presHeight = targetDoc._height;
+                pinDoc.config_fillColor = targetDoc.fillColor;
+                pinDoc.config_color = targetDoc.color;
+                pinDoc.config_width = targetDoc._width;
+                pinDoc.config_height = targetDoc._height;
             }
-            if (pinProps.pinData.scrollable) pinDoc.presViewScroll = targetDoc._layout_scrollTop;
+            if (pinProps.pinData.scrollable) pinDoc.config_scrollTop = targetDoc._layout_scrollTop;
             if (pinProps.pinData.clippable) {
                 const fkey = Doc.LayoutFieldKey(targetDoc);
-                pinDoc.presClipWidth = targetDoc[fkey + '_clipWidth'];
+                pinDoc.config_clipWidth = targetDoc[fkey + '_clipWidth'];
             }
             if (pinProps.pinData.datarange) {
-                pinDoc.presXRange = undefined; //targetDoc?.xrange;
-                pinDoc.presYRange = undefined; //targetDoc?.yrange;
+                pinDoc.config_xRange = undefined; //targetDoc?.xrange;
+                pinDoc.config_yRange = undefined; //targetDoc?.yrange;
             }
             if (pinProps.pinData.poslayoutview)
-                pinDoc.presPinLayoutData = new List<string>(
+                pinDoc.config_pinLayoutData = new List<string>(
                     DocListCast(targetDoc[fkey] as ObjectField).map(d =>
                         JSON.stringify({
                             id: d[Id],
@@ -654,28 +656,28 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         })
                     )
                 );
-            if (pinProps.pinData.type_collection) pinDoc.presViewType = targetDoc._type_collection;
-            if (pinProps.pinData.filters) pinDoc.presDocFilters = ObjectField.MakeCopy(targetDoc.childFilters as ObjectField);
-            if (pinProps.pinData.pivot) pinDoc.presPivotField = targetDoc._pivotField;
+            if (pinProps.pinData.type_collection) pinDoc.config_viewType = targetDoc._type_collection;
+            if (pinProps.pinData.filters) pinDoc.config_docFilters = ObjectField.MakeCopy(targetDoc.childFilters as ObjectField);
+            if (pinProps.pinData.pivot) pinDoc.config_pivotField = targetDoc._pivotField;
             if (pinProps.pinData.pannable) {
-                pinDoc.presPanX = NumCast(targetDoc._freeform_panX);
-                pinDoc.presPanY = NumCast(targetDoc._freeform_panY);
-                pinDoc.presViewScale = NumCast(targetDoc._freeform_scale, 1);
+                pinDoc.config_panX = NumCast(targetDoc._freeform_panX);
+                pinDoc.config_panY = NumCast(targetDoc._freeform_panY);
+                pinDoc.config_viewScale = NumCast(targetDoc._freeform_scale, 1);
             }
             if (pinProps.pinData.temporal) {
-                pinDoc.presStartTime = targetDoc._layout_currentTimecode;
-                const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], NumCast(targetDoc.presStartTime) + 0.1);
-                pinDoc.presEndTime = NumCast(targetDoc.clipEnd, duration);
+                pinDoc.config_clipStart = targetDoc._layout_currentTimecode;
+                const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], NumCast(targetDoc.config_clipStart) + 0.1);
+                pinDoc.config_clipEnd = NumCast(targetDoc.clipEnd, duration);
             }
         }
         if (pinProps?.pinViewport) {
             // If pinWithView option set then update scale and x / y props of slide
             const bounds = pinProps.pinViewport;
-            pinDoc.presPinView = true;
-            pinDoc.presViewScale = NumCast(targetDoc._freeform_scale, 1);
-            pinDoc.presPanX = bounds.left + bounds.width / 2;
-            pinDoc.presPanY = bounds.top + bounds.height / 2;
-            pinDoc.presPinViewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
+            pinDoc.config_pinView = true;
+            pinDoc.config_viewScale = NumCast(targetDoc._freeform_scale, 1);
+            pinDoc.config_panX = bounds.left + bounds.width / 2;
+            pinDoc.config_panY = bounds.top + bounds.height / 2;
+            pinDoc.config_viewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
         }
     }
     /**
@@ -712,23 +714,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: () => void) {
-        if (activeItem.presMovement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) {
+        if (activeItem.presentation_movement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) {
             (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.();
             return;
         }
-        const effect = activeItem.presEffect && activeItem.presEffect !== PresEffect.None ? activeItem.presEffect : undefined;
-        const presTime = NumCast(activeItem.presTransition, effect ? 750 : 500);
+        const effect = activeItem.presentation_effect && activeItem.presentation_effect !== PresEffect.None ? activeItem.presentation_effect : undefined;
+        const presTime = NumCast(activeItem.presentation_transition, effect ? 750 : 500);
         const options: DocFocusOptions = {
-            willPan: activeItem.presMovement !== PresMovement.None,
-            willZoomCentered: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center,
-            zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1),
-            zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : Math.min(Math.max(effect ? 750 : 500, (effect ? 0.2 : 1) * presTime), presTime),
+            willPan: activeItem.presentation_movement !== PresMovement.None,
+            willZoomCentered: activeItem.presentation_movement === PresMovement.Zoom || activeItem.presentation_movement === PresMovement.Jump || activeItem.presentation_movement === PresMovement.Center,
+            zoomScale: activeItem.presentation_movement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1),
+            zoomTime: activeItem.presentation_movement === PresMovement.Jump ? 0 : Math.min(Math.max(effect ? 750 : 500, (effect ? 0.2 : 1) * presTime), presTime),
             effect: activeItem,
             noSelect: true,
             openLocation: OpenWhere.addLeft,
             anchorDoc: activeItem,
             easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
-            zoomTextSelections: BoolCast(activeItem.presZoomText),
+            zoomTextSelections: BoolCast(activeItem.presentation_zoomText),
             playAudio: BoolCast(activeItem.presPlayAudio),
         };
         if (activeItem.presOpenInLightbox) {
@@ -738,7 +740,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
         }
         if (targetDoc) {
-            if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[Animation] = undefined;
+            if (activeItem.presentation_targetDoc instanceof Doc) activeItem.presentation_targetDoc[Animation] = undefined;
 
             DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, dv => {
                 // if target or the doc it annotates is not in the lightbox, then close the lightbox
@@ -761,16 +763,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const tagDoc = PresBox.targetRenderedDoc(curDoc);
             const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc);
             let opacity: Opt<number> = index === this.itemIndex ? 1 : undefined;
-            if (curDoc.presHide) {
+            if (curDoc.presentation_hide) {
                 if (index !== this.itemIndex) {
                     opacity = 1;
                 }
             }
             const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex) ?? itemIndexes.slice().reverse().lastElement();
-            if (curDoc.presHideBefore && index === hidingIndBef) {
+            if (curDoc.presentation_hideBefore && index === hidingIndBef) {
                 if (index > this.itemIndex) {
                     opacity = 0;
-                } else if (index === this.itemIndex || !curDoc.presHideAfter) {
+                } else if (index === this.itemIndex || !curDoc.presentation_hideAfter) {
                     opacity = 1;
                     setTimeout(() => (tagDoc._dataTransition = undefined), 1000);
                 }
@@ -780,15 +782,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     .slice()
                     .reverse()
                     .find(item => item <= this.itemIndex) ?? itemIndexes.lastElement();
-            if (curDoc.presHideAfter && index === hidingIndAft) {
+            if (curDoc.presentation_hideAfter && index === hidingIndAft) {
                 if (index < this.itemIndex) {
                     opacity = 0;
-                } else if (index === this.itemIndex || !curDoc.presHideBefore) {
+                } else if (index === this.itemIndex || !curDoc.presentation_hideBefore) {
                     opacity = 1;
                 }
             }
             const hidingInd = itemIndexes.find(item => item === this.itemIndex);
-            if (curDoc.presHide && index === hidingInd) {
+            if (curDoc.presentation_hide && index === hidingInd) {
                 if (index === this.itemIndex) {
                     opacity = 0;
                 }
@@ -846,9 +848,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // The function pauses the auto presentation
     @action
     pauseAutoPres = () => {
-        if (this.layoutDoc.presStatus === PresStatus.Autoplay) {
+        if (this.layoutDoc.presentation_status === PresStatus.Autoplay) {
             if (this._presTimer) clearTimeout(this._presTimer);
-            this.layoutDoc.presStatus = PresStatus.Manual;
+            this.layoutDoc.presentation_status = PresStatus.Manual;
             this.childDocs.forEach(this.stopTempMedia);
         }
     };
@@ -879,23 +881,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         runInAction(() => (this._expandBoolean = !this._expandBoolean));
         this.rootDoc.expandBoolean = this._expandBoolean;
         this.childDocs.forEach(doc => {
-            doc.presExpandInlineButton = this._expandBoolean;
+            doc.presentation_expandInlineButton = this._expandBoolean;
         });
     };
 
     initializePresState = (startIndex: number) => {
         this.childDocs.forEach((doc, index) => {
             const tagDoc = PresBox.targetRenderedDoc(doc);
-            if (doc.presHideBefore && index > startIndex) tagDoc.opacity = 0;
-            if (doc.presHideAfter && index < startIndex) tagDoc.opacity = 0;
-            if (doc.presIndexed !== undefined && index >= startIndex) {
-                const startInd = NumCast(doc.presIndexedStart);
+            if (doc.presentation_hideBefore && index > startIndex) tagDoc.opacity = 0;
+            if (doc.presentation_hideAfter && index < startIndex) tagDoc.opacity = 0;
+            if (doc.presentation_indexed !== undefined && index >= startIndex) {
+                const startInd = NumCast(doc.presentation_indexedStart);
                 this.progressivizedItems(doc)
                     ?.slice(startInd)
                     .forEach(indexedDoc => (indexedDoc.opacity = 0));
-                doc.presIndexed = Math.min(this.progressivizedItems(doc)?.length ?? 0, startInd);
+                doc.presentation_indexed = Math.min(this.progressivizedItems(doc)?.length ?? 0, startInd);
             }
-            // if (doc.presHide && this.childDocs.indexOf(doc) === startIndex)  tagDoc.opacity = 0;
+            // if (doc.presentation_hide && this.childDocs.indexOf(doc) === startIndex)  tagDoc.opacity = 0;
         });
     };
 
@@ -909,13 +911,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         PresBox.Instance = this;
         clearTimeout(this._presTimer);
         if (this.childDocs.length) {
-            this.layoutDoc.presStatus = PresStatus.Autoplay;
+            this.layoutDoc.presentation_status = PresStatus.Autoplay;
             this.initializePresState(startIndex);
             const func = () => {
-                const delay = NumCast(this.activeItem.presDuration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presTransition);
+                const delay = NumCast(this.activeItem.presentation_duration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presentation_transition);
                 this._presTimer = setTimeout(() => {
-                    if (!this.next()) this.layoutDoc.presStatus = this._exitTrail?.() ?? PresStatus.Manual;
-                    this.layoutDoc.presStatus === PresStatus.Autoplay && func();
+                    if (!this.next()) this.layoutDoc.presentation_status = this._exitTrail?.() ?? PresStatus.Manual;
+                    this.layoutDoc.presentation_status === PresStatus.Autoplay && func();
                 }, delay);
             };
             this.gotoDocument(startIndex, this.activeItem, undefined, func);
@@ -949,7 +951,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         doc._width = PresBox.minimizedWidth;
         Doc.AddToMyOverlay(doc);
         PresBox.Instance?.initializePresState(PresBox.Instance.itemIndex);
-        return (doc.presStatus = PresStatus.Manual);
+        return (doc.presentation_status = PresStatus.Manual);
     }
 
     /**
@@ -993,34 +995,34 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     });
 
     movementName = action((activeItem: Doc) => {
-        if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Center, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presMovement) as any)) {
+        if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Center, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presentation_movement) as any)) {
             return PresMovement.Zoom;
         }
-        return StrCast(activeItem.presMovement);
+        return StrCast(activeItem.presentation_movement);
     });
 
     whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isChildActive = isActive)));
     // For dragging documents into the presentation trail
     addDocumentFilter = (docs: Doc[]) => {
         docs.forEach((doc, i) => {
-            if (doc.presentationTargetDoc) return true;
+            if (doc.presentation_targetDoc) return true;
             if (doc.type === DocumentType.LABEL) {
                 const audio = Cast(doc.annotationOn, Doc, null);
                 if (audio) {
                     audio.mediaStart = 'manual';
                     audio.mediaStop = 'manual';
-                    audio.presStartTime = NumCast(doc._timecodeToShow /* audioStart */, NumCast(doc._timecodeToShow /* videoStart */));
-                    audio.presEndTime = NumCast(doc._timecodeToHide /* audioEnd */, NumCast(doc._timecodeToHide /* videoEnd */));
-                    audio.presDuration = audio.presStartTime - audio.presEndTime;
+                    audio.config_clipStart = NumCast(doc._timecodeToShow /* audioStart */, NumCast(doc._timecodeToShow /* videoStart */));
+                    audio.config_clipEnd = NumCast(doc._timecodeToHide /* audioEnd */, NumCast(doc._timecodeToHide /* videoEnd */));
+                    audio.presentation_duration = audio.config_clipStart - audio.config_clipEnd;
                     TabDocView.PinDoc(audio, { audioRange: true });
                     setTimeout(() => this.removeDocument(doc), 0);
                     return false;
                 }
             } else {
-                if (!doc.presentationTargetDoc) doc.title = doc.title + ' - Slide';
-                doc.presentationTargetDoc = doc.createdFrom; // dropped document will be a new embedding of an embedded document somewhere else.
-                doc.presMovement = PresMovement.Zoom;
-                if (this._expandBoolean) doc.presExpandInlineButton = true;
+                if (!doc.presentation_targetDoc) doc.title = doc.title + ' - Slide';
+                doc.presentation_targetDoc = doc.createdFrom; // dropped document will be a new embedding of an embedded document somewhere else.
+                doc.presentation_movement = PresMovement.Zoom;
+                if (this._expandBoolean) doc.presentation_expandInlineButton = true;
             }
         });
         return true;
@@ -1040,7 +1042,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get listOfSelected() {
         return Array.from(this.selectedArray).map((doc: Doc, index: any) => {
             const curDoc = Cast(doc, Doc, null);
-            const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
+            const tagDoc = Cast(curDoc.presentation_targetDoc, Doc, null);
             if (curDoc && curDoc === this.activeItem)
                 return (
                     <div key={index} className="selectedList-items">
@@ -1146,7 +1148,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (anchorNode && anchorNode.className?.includes('lm_title')) return;
         switch (e.key) {
             case 'Backspace':
-                if (this.layoutDoc.presStatus === 'edit') {
+                if (this.layoutDoc.presentation_status === 'edit') {
                     undoBatch(
                         action(() => {
                             for (const doc of this.selectedArray) {
@@ -1163,11 +1165,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             case 'Escape':
                 if (Doc.IsInMyOverlay(this.layoutDoc)) {
                     this.exitClicked();
-                } else if (this.layoutDoc.presStatus === PresStatus.Edit) {
+                } else if (this.layoutDoc.presentation_status === PresStatus.Edit) {
                     this.clearSelectedArray();
                     this._eleArray.length = this._dragArray.length = 0;
                 } else {
-                    this.layoutDoc.presStatus = PresStatus.Edit;
+                    this.layoutDoc.presentation_status = PresStatus.Edit;
                 }
                 if (this._presTimer) clearTimeout(this._presTimer);
                 handled = true;
@@ -1184,7 +1186,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     this.next();
                     if (this._presTimer) {
                         clearTimeout(this._presTimer);
-                        this.layoutDoc.presStatus = PresStatus.Manual;
+                        this.layoutDoc.presentation_status = PresStatus.Manual;
                     }
                 }
                 handled = true;
@@ -1201,19 +1203,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     this.back();
                     if (this._presTimer) {
                         clearTimeout(this._presTimer);
-                        this.layoutDoc.presStatus = PresStatus.Manual;
+                        this.layoutDoc.presentation_status = PresStatus.Manual;
                     }
                 }
                 handled = true;
                 break;
             case 'Spacebar':
             case ' ':
-                if (this.layoutDoc.presStatus === PresStatus.Manual) this.startOrPause(true);
-                else if (this.layoutDoc.presStatus === PresStatus.Autoplay) if (this._presTimer) clearTimeout(this._presTimer);
+                if (this.layoutDoc.presentation_status === PresStatus.Manual) this.startOrPause(true);
+                else if (this.layoutDoc.presentation_status === PresStatus.Autoplay) if (this._presTimer) clearTimeout(this._presTimer);
                 handled = true;
                 break;
             case 'a':
-                if ((e.metaKey || e.altKey) && this.layoutDoc.presStatus === 'edit') {
+                if ((e.metaKey || e.altKey) && this.layoutDoc.presentation_status === 'edit') {
                     this.clearSelectedArray();
                     this.childDocs.forEach(doc => this.addToSelectedArray(doc));
                     handled = true;
@@ -1236,9 +1238,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement();
         const dv = DocumentManager.Instance.getDocumentView(presCollection);
         this.childDocs
-            .filter(doc => Cast(doc.presentationTargetDoc, Doc, null))
+            .filter(doc => Cast(doc.presentation_targetDoc, Doc, null))
             .forEach((doc, index) => {
-                const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
+                const tagDoc = Cast(doc.presentation_targetDoc, Doc, null);
                 const srcContext = Cast(tagDoc.embedContainer, Doc, null);
                 const width = NumCast(tagDoc._width) / 10;
                 const height = Math.max(NumCast(tagDoc._height) / 10, 15);
@@ -1271,17 +1273,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             </div>
                         );
                     }
-                } else if (doc.presPinView && presCollection === tagDoc && dv) {
+                } else if (doc.config_pinView && presCollection === tagDoc && dv) {
                     // Case B: Document is presPinView and is presCollection
-                    const scale: number = 1 / NumCast(doc.presViewScale);
+                    const scale: number = 1 / NumCast(doc.config_viewScale);
                     const height: number = dv.props.PanelHeight() * scale;
                     const width: number = dv.props.PanelWidth() * scale;
                     const indWidth = width / 10;
                     const indHeight = Math.max(height / 10, 15);
                     const indEdge = Math.max(indWidth, indHeight);
                     const indFontSize = indEdge * 0.8;
-                    const xLoc: number = NumCast(doc.presPanX) - width / 2;
-                    const yLoc: number = NumCast(doc.presPanY) - height / 2;
+                    const xLoc: number = NumCast(doc.config_panX) - width / 2;
+                    const yLoc: number = NumCast(doc.config_panY) - height / 2;
                     docs.push(tagDoc);
                     order.push(
                         <>
@@ -1307,15 +1309,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get paths() {
         let pathPoints = '';
         this.childDocs.forEach((doc, index) => {
-            const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
+            const tagDoc = Cast(doc.presentation_targetDoc, Doc, null);
             if (tagDoc) {
                 const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2;
                 const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2;
                 if ((index = 0)) pathPoints = n1x + ',' + n1y;
                 else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
-            } else if (doc.presPinView) {
-                const n1x = NumCast(doc.presPanX);
-                const n1y = NumCast(doc.presPanY);
+            } else if (doc.config_pinView) {
+                const n1x = NumCast(doc.config_panX);
+                const n1y = NumCast(doc.config_panY);
                 if ((index = 0)) pathPoints = n1x + ',' + n1y;
                 else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
             }
@@ -1339,7 +1341,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     getPaths = (collection: Doc) => this.paths; // needs to be smarter and figure out the paths to draw for this specific collection.  or better yet, draw everything in an overlay layer instad of within a collection
 
-    // Converts seconds to ms and updates presTransition
+    // Converts seconds to ms and updates presentation_transition
     public static SetTransitionTime = (number: String, setter: (timeInMS: number) => void, change?: number) => {
         let timeInMS = Number(number) * 1000;
         if (change) timeInMS += change;
@@ -1350,10 +1352,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @undoBatch
     updateTransitionTime = (number: String, change?: number) => {
-        PresBox.SetTransitionTime(number, (timeInMS: number) => this.selectedArray.forEach(doc => (doc.presTransition = timeInMS)), change);
+        PresBox.SetTransitionTime(number, (timeInMS: number) => this.selectedArray.forEach(doc => (doc.presentation_transition = timeInMS)), change);
     };
 
-    // Converts seconds to ms and updates presTransition
+    // Converts seconds to ms and updates presentation_transition
     @undoBatch
     updateZoom = (number: String, change?: number) => {
         let scale = Number(number) / 100;
@@ -1364,7 +1366,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     /*
-     * Converts seconds to ms and updates presDuration
+     * Converts seconds to ms and updates presentation_duration
      */
     @undoBatch
     updateDurationTime = (number: String, change?: number) => {
@@ -1372,31 +1374,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) timeInMS += change;
         if (timeInMS < 100) timeInMS = 100;
         if (timeInMS > 20000) timeInMS = 20000;
-        this.selectedArray.forEach(doc => (doc.presDuration = timeInMS));
+        this.selectedArray.forEach(doc => (doc.presentation_duration = timeInMS));
     };
 
     @undoBatch
-    updateMovement = action((movement: PresMovement, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presMovement = movement)));
+    updateMovement = action((movement: PresMovement, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presentation_movement = movement)));
 
     @undoBatch
     @action
     updateHideBefore = (activeItem: Doc) => {
-        activeItem.presHideBefore = !activeItem.presHideBefore;
-        this.selectedArray.forEach(doc => (doc.presHideBefore = activeItem.presHideBefore));
+        activeItem.presentation_hideBefore = !activeItem.presentation_hideBefore;
+        this.selectedArray.forEach(doc => (doc.presentation_hideBefore = activeItem.presentation_hideBefore));
     };
 
     @undoBatch
     @action
     updateHide = (activeItem: Doc) => {
-        activeItem.presHide = !activeItem.presHide;
-        this.selectedArray.forEach(doc => (doc.presHide = activeItem.presHide));
+        activeItem.presentation_hide = !activeItem.presentation_hide;
+        this.selectedArray.forEach(doc => (doc.presentation_hide = activeItem.presentation_hide));
     };
 
     @undoBatch
     @action
     updateHideAfter = (activeItem: Doc) => {
-        activeItem.presHideAfter = !activeItem.presHideAfter;
-        this.selectedArray.forEach(doc => (doc.presHideAfter = activeItem.presHideAfter));
+        activeItem.presentation_hideAfter = !activeItem.presentation_hideAfter;
+        this.selectedArray.forEach(doc => (doc.presentation_hideAfter = activeItem.presentation_hideAfter));
     };
 
     @undoBatch
@@ -1415,11 +1417,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @undoBatch
     @action
-    updateEffectDirection = (effect: PresEffectDirection, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffectDirection = effect));
+    updateEffectDirection = (effect: PresEffectDirection, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presentation_effectDirection = effect));
 
     @undoBatch
     @action
-    updateEffect = (effect: PresEffect, bullet: boolean, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (bullet ? (doc.presBulletEffect = effect) : (doc.presEffect = effect)));
+    updateEffect = (effect: PresEffect, bullet: boolean, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (bullet ? (doc.presBulletEffect = effect) : (doc.presentation_effect = effect)));
 
     static _sliderBatch: any;
     public static inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void, hmargin?: number) => {
@@ -1449,16 +1451,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     applyTo = (array: Doc[]) => {
-        this.updateMovement(this.activeItem.presMovement as PresMovement, true);
-        this.updateEffect(this.activeItem.presEffect as PresEffect, false, true);
+        this.updateMovement(this.activeItem.presentation_movement as PresMovement, true);
+        this.updateEffect(this.activeItem.presentation_effect as PresEffect, false, true);
         this.updateEffect(this.activeItem.presBulletEffect as PresEffect, true, true);
-        this.updateEffectDirection(this.activeItem.presEffectDirection as PresEffectDirection, true);
-        const { presTransition, presDuration, presHideBefore, presHideAfter } = this.activeItem;
+        this.updateEffectDirection(this.activeItem.presentation_effectDirection as PresEffectDirection, true);
+        const { presentation_transition, presentation_duration, presentation_hideBefore, presentation_hideAfter } = this.activeItem;
         array.forEach(curDoc => {
-            curDoc.presTransition = presTransition;
-            curDoc.presDuration = presDuration;
-            curDoc.presHideBefore = presHideBefore;
-            curDoc.presHideAfter = presHideAfter;
+            curDoc.presentation_transition = presentation_transition;
+            curDoc.presentation_duration = presentation_duration;
+            curDoc.presentation_hideBefore = presentation_hideBefore;
+            curDoc.presentation_hideAfter = presentation_hideAfter;
         });
     };
 
@@ -1466,24 +1468,24 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeItem = this.activeItem;
         if (activeItem && this.targetDoc) {
             const targetType = this.targetDoc.type;
-            let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 0;
+            let duration = activeItem.presentation_duration ? NumCast(activeItem.presentation_duration) / 1000 : 0;
             if (activeItem.type === DocumentType.AUDIO) duration = NumCast(activeItem.duration);
             return (
                 <div className="presBox-ribbon">
                     <div className="ribbon-doubleButton">
                         <Tooltip title={<div className="dash-tooltip">{'Hide before presented'}</div>}>
-                            <div className={`ribbon-toggle ${activeItem.presHideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
+                            <div className={`ribbon-toggle ${activeItem.presentation_hideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
                                 Hide before
                             </div>
                         </Tooltip>
                         <Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}>
-                            <div className={`ribbon-toggle ${activeItem.presHide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}>
+                            <div className={`ribbon-toggle ${activeItem.presentation_hide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}>
                                 Hide
                             </div>
                         </Tooltip>
 
                         <Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
-                            <div className={`ribbon-toggle ${activeItem.presHideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
+                            <div className={`ribbon-toggle ${activeItem.presentation_hideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
                                 Hide after
                             </div>
                         </Tooltip>
@@ -1532,7 +1534,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (activeItem && this.targetDoc) {
             const effect = activeItem.presBulletEffect ? activeItem.presBulletEffect : PresMovement.None;
             const bulletEffect = (effect: PresEffect) => (
-                <div className={`presBox-dropdownOption ${activeItem.presEffect === effect || (effect === PresEffect.None && !activeItem.presEffect) ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateEffect(effect, true)}>
+                <div
+                    className={`presBox-dropdownOption ${activeItem.presentation_effect === effect || (effect === PresEffect.None && !activeItem.presentation_effect) ? 'active' : ''}`}
+                    onPointerDown={StopEvent}
+                    onClick={() => this.updateEffect(effect, true)}>
                     {effect}
                 </div>
             );
@@ -1545,25 +1550,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             style={{ margin: 10 }}
                             type="checkbox"
                             onChange={() => {
-                                activeItem.presIndexed = activeItem.presIndexed === undefined ? 0 : undefined;
-                                activeItem.presHideBefore = activeItem.presIndexed !== undefined;
+                                activeItem.presentation_indexed = activeItem.presentation_indexed === undefined ? 0 : undefined;
+                                activeItem.presentation_hideBefore = activeItem.presentation_indexed !== undefined;
                                 const tagDoc = PresBox.targetRenderedDoc(this.activeItem);
                                 const type = DocCast(tagDoc?.annotationOn)?.type ?? tagDoc.type;
-                                activeItem.presIndexedStart = type === DocumentType.COL ? 1 : 0;
+                                activeItem.presentation_indexedStart = type === DocumentType.COL ? 1 : 0;
                                 // a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized.
                                 // to avoid creating a new slide to correspond to each of the target's data list, we create a computedField to refernce the target's data list.
                                 let dataField = Doc.LayoutFieldKey(tagDoc);
                                 if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '_annotations';
 
-                                if (DocCast(activeItem.presentationTargetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`self.presentationTargetDoc.annotationOn["${dataField}"]`);
-                                else activeItem.data = ComputedField.MakeFunction(`self.presentationTargetDoc["${dataField}"]`);
+                                if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc.annotationOn["${dataField}"]`);
+                                else activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc["${dataField}"]`);
                             }}
-                            checked={Cast(activeItem.presIndexed, 'number', null) !== undefined ? true : false}
+                            checked={Cast(activeItem.presentation_indexed, 'number', null) !== undefined ? true : false}
                         />
                     </div>
                     <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                         <div className="presBox-subheading">Progressivize First Bullet</div>
-                        <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presIndexedStart = activeItem.presIndexedStart ? 0 : 1)} checked={!NumCast(activeItem.presIndexedStart)} />
+                        <input
+                            className="presBox-checkbox"
+                            style={{ margin: 10 }}
+                            type="checkbox"
+                            onChange={() => (activeItem.presentation_indexedStart = activeItem.presentation_indexedStart ? 0 : 1)}
+                            checked={!NumCast(activeItem.presentation_indexedStart)}
+                        />
                     </div>
                     <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                         <div className="presBox-subheading">Expand Current Bullet</div>
@@ -1598,18 +1609,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     @computed get transitionDropdown() {
         const activeItem = this.activeItem;
-        const presEffect = (effect: PresEffect) => (
-            <div className={`presBox-dropdownOption ${activeItem.presEffect === effect || (effect === PresEffect.None && !activeItem.presEffect) ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateEffect(effect, false)}>
+        const preseEffect = (effect: PresEffect) => (
+            <div
+                className={`presBox-dropdownOption ${activeItem.presentation_effect === effect || (effect === PresEffect.None && !activeItem.presentation_effect) ? 'active' : ''}`}
+                onPointerDown={StopEvent}
+                onClick={() => this.updateEffect(effect, false)}>
                 {effect}
             </div>
         );
         const presMovement = (movement: PresMovement) => (
-            <div className={`presBox-dropdownOption ${activeItem.presMovement === movement ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateMovement(movement)}>
+            <div className={`presBox-dropdownOption ${activeItem.presentation_movement === movement ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateMovement(movement)}>
                 {movement}
             </div>
         );
         const presDirection = (direction: PresEffectDirection, icon: string, gridColumn: number, gridRow: number, opts: object) => {
-            const color = activeItem.presEffectDirection === direction || (direction === PresEffectDirection.Center && !activeItem.presEffectDirection) ? Colors.LIGHT_BLUE : 'black';
+            const color = activeItem.presentation_effectDirection === direction || (direction === PresEffectDirection.Center && !activeItem.presentation_effectDirection) ? Colors.LIGHT_BLUE : 'black';
             return (
                 <Tooltip title={<div className="dash-tooltip">{direction}</div>}>
                     <div
@@ -1621,12 +1635,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             );
         };
         if (activeItem && this.targetDoc) {
-            const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5;
+            const transitionSpeed = activeItem.presentation_transition ? NumCast(activeItem.presentation_transition) / 1000 : 0.5;
             const zoom = NumCast(activeItem.presZoom, 1) * 100;
-            const effect = activeItem.presEffect ? activeItem.presEffect : PresMovement.None;
+            const effect = activeItem.presentation_effect ? activeItem.presentation_effect : PresMovement.None;
             return (
                 <div
-                    className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? 'active' : ''}`}
+                    className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presentation_status === PresStatus.Edit ? 'active' : ''}`}
                     onPointerDown={StopEvent}
                     onPointerUp={StopEvent}
                     onClick={action(e => {
@@ -1654,7 +1668,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 {presMovement(PresMovement.Jump)}
                             </div>
                         </div>
-                        <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
+                        <div className="ribbon-doubleButton" style={{ display: activeItem.presentation_movement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
                             <div className="presBox-subheading">Zoom (% screen filled)</div>
                             <div className="ribbon-property">
                                 <input className="presBox-input" type="number" readOnly={true} value={zoom} onChange={e => this.updateZoom(e.target.value)} />%
@@ -1668,7 +1682,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 </div>
                             </div>
                         </div>
-                        {PresBox.inputter('0', '1', '100', zoom, activeItem.presMovement === PresMovement.Zoom, this.updateZoom)}
+                        {PresBox.inputter('0', '1', '100', zoom, activeItem.presentation_movement === PresMovement.Zoom, this.updateZoom)}
                         <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                             <div className="presBox-subheading">Transition Time</div>
                             <div className="ribbon-property">
@@ -1698,7 +1712,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         </div>
                         <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                             <div className="presBox-subheading">Zoom Text Selections</div>
-                            <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presZoomText = !BoolCast(activeItem.presZoomText))} checked={BoolCast(activeItem.presZoomText)} />
+                            <input
+                                className="presBox-checkbox"
+                                style={{ margin: 10 }}
+                                type="checkbox"
+                                onChange={() => (activeItem.presentation_zoomText = !BoolCast(activeItem.presentation_zoomText))}
+                                checked={BoolCast(activeItem.presentation_zoomText)}
+                            />
                         </div>
                         <div
                             className="presBox-dropdown"
@@ -1710,17 +1730,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             {effect?.toString()}
                             <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
                             <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this._openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
-                                {presEffect(PresEffect.None)}
-                                {presEffect(PresEffect.Fade)}
-                                {presEffect(PresEffect.Flip)}
-                                {presEffect(PresEffect.Rotate)}
-                                {presEffect(PresEffect.Bounce)}
-                                {presEffect(PresEffect.Roll)}
+                                {preseEffect(PresEffect.None)}
+                                {preseEffect(PresEffect.Fade)}
+                                {preseEffect(PresEffect.Flip)}
+                                {preseEffect(PresEffect.Rotate)}
+                                {preseEffect(PresEffect.Bounce)}
+                                {preseEffect(PresEffect.Roll)}
                             </div>
                         </div>
                         <div className="ribbon-doubleButton" style={{ display: effect === PresEffectDirection.None ? 'none' : 'inline-flex' }}>
                             <div className="presBox-subheading">Effect direction</div>
-                            <div className="ribbon-property">{StrCast(this.activeItem.presEffectDirection)}</div>
+                            <div className="ribbon-property">{StrCast(this.activeItem.presentation_effectDirection)}</div>
                         </div>
                         <div className="effectDirection" style={{ display: effect === PresEffectDirection.None ? 'none' : 'grid', width: 40 }}>
                             {presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
@@ -1760,10 +1780,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                             style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
                                             type="number"
                                             readOnly={true}
-                                            value={NumCast(activeItem.presStartTime).toFixed(2)}
+                                            value={NumCast(activeItem.config_clipStart).toFixed(2)}
                                             onKeyDown={e => e.stopPropagation()}
                                             onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                                activeItem.presStartTime = Number(e.target.value);
+                                                activeItem.config_clipStart = Number(e.target.value);
                                             })}
                                         />
                                     </div>
@@ -1773,7 +1793,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         Duration (s)
                                     </div>
                                     <div className="slider-number" style={{ backgroundColor: Colors.LIGHT_BLUE }}>
-                                        {Math.round((NumCast(activeItem.presEndTime) - NumCast(activeItem.presStartTime)) * 10) / 10}
+                                        {Math.round((NumCast(activeItem.config_clipEnd) - NumCast(activeItem.config_clipStart)) * 10) / 10}
                                     </div>
                                 </div>
                                 <div className="slider-block">
@@ -1787,9 +1807,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                             style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
                                             type="number"
                                             readOnly={true}
-                                            value={NumCast(activeItem.presEndTime).toFixed(2)}
+                                            value={NumCast(activeItem.config_clipEnd).toFixed(2)}
                                             onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                                activeItem.presEndTime = Number(e.target.value);
+                                                activeItem.config_clipEnd = Number(e.target.value);
                                             })}
                                         />
                                     </div>
@@ -1801,12 +1821,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     step="0.1"
                                     min={clipStart}
                                     max={clipEnd}
-                                    value={NumCast(activeItem.presEndTime)}
+                                    value={NumCast(activeItem.config_clipEnd)}
                                     style={{ gridColumn: 1, gridRow: 1 }}
                                     className={`toolbar-slider ${'end'}`}
                                     id="toolbar-slider"
                                     onPointerDown={e => {
-                                        this._batch = UndoManager.StartBatch('presEndTime');
+                                        this._batch = UndoManager.StartBatch('config_clipEnd');
                                         const endBlock = document.getElementById('endTime');
                                         if (endBlock) {
                                             endBlock.style.color = Colors.LIGHT_GRAY;
@@ -1824,7 +1844,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     }}
                                     onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                         e.stopPropagation();
-                                        activeItem.presEndTime = Number(e.target.value);
+                                        activeItem.config_clipEnd = Number(e.target.value);
                                     }}
                                 />
                                 <input
@@ -1832,12 +1852,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     step="0.1"
                                     min={clipStart}
                                     max={clipEnd}
-                                    value={NumCast(activeItem.presStartTime)}
+                                    value={NumCast(activeItem.config_clipStart)}
                                     style={{ gridColumn: 1, gridRow: 1 }}
                                     className={`toolbar-slider ${'start'}`}
                                     id="toolbar-slider"
                                     onPointerDown={e => {
-                                        this._batch = UndoManager.StartBatch('presStartTime');
+                                        this._batch = UndoManager.StartBatch('config_clipStart');
                                         const startBlock = document.getElementById('startTime');
                                         if (startBlock) {
                                             startBlock.style.color = Colors.LIGHT_GRAY;
@@ -1855,7 +1875,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     }}
                                     onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                         e.stopPropagation();
-                                        activeItem.presStartTime = Number(e.target.value);
+                                        activeItem.config_clipStart = Number(e.target.value);
                                     }}
                                 />
                             </div>
@@ -1916,7 +1936,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return (
             <div
                 className="presBox-toolbar-dropdown"
-                style={{ display: this._newDocumentTools && this.layoutDoc.presStatus === 'edit' ? 'inline-flex' : 'none' }}
+                style={{ display: this._newDocumentTools && this.layoutDoc.presentation_status === 'edit' ? 'inline-flex' : 'none' }}
                 onClick={e => e.stopPropagation()}
                 onPointerUp={e => e.stopPropagation()}
                 onPointerDown={e => e.stopPropagation()}>
@@ -2053,10 +2073,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (doc) {
             const tabMap = CollectionDockingView.Instance?.tabMap;
             const tab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc.type === DocumentType.COL)?.DashDoc;
-            const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement().presentationTargetDoc ?? tab;
+            const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement().presentation_targetDoc ?? tab;
             const data = Cast(presCollection?.data, listSpec(Doc));
-            const presData = Cast(this.rootDoc.data, listSpec(Doc));
-            if (data && presData) {
+            const config_data = Cast(this.rootDoc.data, listSpec(Doc));
+            if (data && config_data) {
                 data.push(doc);
                 TabDocView.PinDoc(doc, {});
                 this.gotoDocument(this.childDocs.length, this.activeItem);
@@ -2105,7 +2125,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     className="dropdown-play-button"
                     onClick={undoBatch(
                         action(() => {
-                            this.layoutDoc.presStatus = 'manual';
+                            this.layoutDoc.presentation_status = 'manual';
                             this.initializePresState(this.itemIndex);
                             this.turnOffEdit(true);
                             this.gotoDocument(this.itemIndex, this.activeItem);
@@ -2181,7 +2201,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 className={`presBox-buttons${Doc.IsInMyOverlay(this.rootDoc) ? ' inOverlay' : ''}`}
                 style={{ background: Doc.ActivePresentation === this.rootDoc ? Colors.LIGHT_BLUE : undefined, display: !this.rootDoc._chromeHidden ? 'none' : undefined }}>
                 {isMini ? null : (
-                    <select className="presBox-viewPicker" style={{ display: this.layoutDoc.presStatus === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}>
+                    <select className="presBox-viewPicker" style={{ display: this.layoutDoc.presentation_status === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}>
                         <option onPointerDown={StopEvent} value={CollectionViewType.Stacking}>
                             List
                         </option>
@@ -2196,12 +2216,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     </select>
                 )}
                 <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
-                    <span className={`presBox-button ${this.layoutDoc.presStatus === PresStatus.Edit ? 'present' : ''}`}>
+                    <span className={`presBox-button ${this.layoutDoc.presentation_status === PresStatus.Edit ? 'present' : ''}`}>
                         <div
                             className="presBox-button-left"
                             onClick={undoBatch(() => {
                                 if (this.childDocs.length) {
-                                    this.layoutDoc.presStatus = 'manual';
+                                    this.layoutDoc.presentation_status = 'manual';
                                     this.initializePresState(this.itemIndex);
                                     this.gotoDocument(this.itemIndex, this.activeItem);
                                 }
@@ -2227,12 +2247,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @computed get playButtons() {
-        const presEnd = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1 && (this.activeItem.presIndexed === undefined || NumCast(this.activeItem.presIndexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
+        const presEnd =
+            !this.layoutDoc.presLoop &&
+            this.itemIndex === this.childDocs.length - 1 &&
+            (this.activeItem.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
         const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
         const inOverlay = Doc.IsInMyOverlay(this.rootDoc);
         // Case 1: There are still other frames and should go through all frames before going to next slide
         return (
-            <div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== 'edit' ? 'inline-flex' : 'none' }}>
+            <div className="presPanelOverlay" style={{ display: this.layoutDoc.presentation_status !== 'edit' ? 'inline-flex' : 'none' }}>
                 <Tooltip title={<div className="dash-tooltip">{'Loop'}</div>}>
                     <div
                         className="presPanel-button"
@@ -2255,7 +2278,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 this.back();
                                 if (this._presTimer) {
                                     clearTimeout(this._presTimer);
-                                    this.layoutDoc.presStatus = PresStatus.Manual;
+                                    this.layoutDoc.presentation_status = PresStatus.Manual;
                                 }
                                 e.stopPropagation();
                             },
@@ -2265,9 +2288,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     }>
                     <FontAwesomeIcon icon={'arrow-left'} />
                 </div>
-                <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
+                <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presentation_status === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
                     <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.startOrPause(true), false, false)}>
-                        <FontAwesomeIcon color={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'red' : undefined} icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'pause' : 'play'} />
+                        <FontAwesomeIcon color={this.layoutDoc.presentation_status === PresStatus.Autoplay ? 'red' : undefined} icon={this.layoutDoc.presentation_status === PresStatus.Autoplay ? 'pause' : 'play'} />
                     </div>
                 </Tooltip>
                 <div
@@ -2283,7 +2306,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 this.next();
                                 if (this._presTimer) {
                                     clearTimeout(this._presTimer);
-                                    this.layoutDoc.presStatus = PresStatus.Manual;
+                                    this.layoutDoc.presentation_status = PresStatus.Manual;
                                 }
                                 e.stopPropagation();
                             },
@@ -2316,7 +2339,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </Tooltip>
                 <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: inOverlay || this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}>
                     {inOverlay ? '' : 'Slide'} {this.itemIndex + 1}
-                    {this.activeItem?.presIndexed !== undefined ? `(${this.activeItem.presIndexed}/${this.progressivizedItems(this.activeItem)?.length})` : ''} / {this.childDocs.length}
+                    {this.activeItem?.presentation_indexed !== undefined ? `(${this.activeItem.presentation_indexed}/${this.progressivizedItems(this.activeItem)?.length})` : ''} / {this.childDocs.length}
                 </div>
                 <div className="presPanel-divider"></div>
                 {this.props.PanelWidth() > 250 ? (
@@ -2324,7 +2347,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         className="presPanel-button-text"
                         onClick={undoBatch(
                             action(() => {
-                                this.layoutDoc.presStatus = PresStatus.Edit;
+                                this.layoutDoc.presentation_status = PresStatus.Edit;
                                 clearTimeout(this._presTimer);
                             })
                         )}>
@@ -2342,7 +2365,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     startOrPause = (makeActive = true) => {
         makeActive && this.updateCurrentPresentation();
-        if (!this.layoutDoc.presStatus || this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startPresentation(this.itemIndex);
+        if (!this.layoutDoc.presentation_status || this.layoutDoc.presentation_status === PresStatus.Manual || this.layoutDoc.presentation_status === PresStatus.Edit) this.startPresentation(this.itemIndex);
         else this.pauseAutoPres();
     };
 
@@ -2351,7 +2374,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.back();
         if (this._presTimer) {
             clearTimeout(this._presTimer);
-            this.layoutDoc.presStatus = PresStatus.Manual;
+            this.layoutDoc.presentation_status = PresStatus.Manual;
         }
     };
 
@@ -2360,19 +2383,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.next();
         if (this._presTimer) {
             clearTimeout(this._presTimer);
-            this.layoutDoc.presStatus = PresStatus.Manual;
+            this.layoutDoc.presentation_status = PresStatus.Manual;
         }
     };
 
     @undoBatch
     @action
     exitClicked = () => {
-        this.layoutDoc.presStatus = this._exitTrail?.() ?? this.exitMinimize();
+        this.layoutDoc.presentation_status = this._exitTrail?.() ?? this.exitMinimize();
         clearTimeout(this._presTimer);
     };
 
     AddToMap = (treeViewDoc: Doc, index: number[]) => {
-        if (!treeViewDoc.presentationTargetDoc) return this.childDocs; // if treeViewDoc is not a pres elements, then it's a sub-bullet of a progressivized slide which isn't added to the linearized list of pres elements since it's not really a pres element.
+        if (!treeViewDoc.presentation_targetDoc) return this.childDocs; // if treeViewDoc is not a pres elements, then it's a sub-bullet of a progressivized slide which isn't added to the linearized list of pres elements since it's not really a pres element.
         var indexNum = 0;
         for (let i = 0; i < index.length; i++) {
             indexNum += index[i] * 10 ** -i;
@@ -2388,20 +2411,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     RemFromMap = (treeViewDoc: Doc, index: number[]) => {
-        if (!treeViewDoc.presentationTargetDoc) return this.childDocs; // if treeViewDoc is not a pres elements, then it's a sub-bullet of a progressivized slide which isn't added to the linearized list of pres elements since it's not really a pres element.
+        if (!treeViewDoc.presentation_targetDoc) return this.childDocs; // if treeViewDoc is not a pres elements, then it's a sub-bullet of a progressivized slide which isn't added to the linearized list of pres elements since it's not really a pres element.
         if (!this._unmounting && this.isTree) {
             this._treeViewMap.delete(treeViewDoc);
             this.dataDoc[this.presFieldKey] = new List<Doc>(this.sort(this._treeViewMap));
         }
     };
 
-    sort = (treeViewMap: Map<Doc, number>) => [...treeViewMap.entries()].sort((a: [Doc, number], b: [Doc, number]) => (a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0)).map(kv => kv[0]);
+    sort = (treeView_Map: Map<Doc, number>) => [...treeView_Map.entries()].sort((a: [Doc, number], b: [Doc, number]) => (a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0)).map(kv => kv[0]);
 
     render() {
         // needed to ensure that the childDocs are loaded for looking up fields
         this.childDocs.slice();
         const mode = StrCast(this.rootDoc._type_collection) as CollectionViewType;
-        const presEnd = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1 && (this.activeItem.presIndexed === undefined || NumCast(this.activeItem.presIndexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
+        const presEnd =
+            !this.layoutDoc.presLoop &&
+            this.itemIndex === this.childDocs.length - 1 &&
+            (this.activeItem.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
         const presStart = !this.layoutDoc.presLoop && this.itemIndex === 0;
         return this.props.addDocTab === returnFalse ? ( // bcz: hack!! - addDocTab === returnFalse only when this is being rendered by the OverlayView which means the doc is a mini player
             <div className="miniPres" onClick={e => e.stopPropagation()} onPointerEnter={action(e => (this._forceKeyEvents = true))}>
@@ -2420,9 +2446,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.prevClicked, false, false)}>
                         <FontAwesomeIcon icon={'arrow-left'} />
                     </div>
-                    <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
+                    <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presentation_status === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
                         <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.startOrPause(true), false, false)}>
-                            <FontAwesomeIcon icon={this.layoutDoc.presStatus === 'auto' ? 'pause' : 'play'} />
+                            <FontAwesomeIcon icon={this.layoutDoc.presentation_status === 'auto' ? 'pause' : 'play'} />
                         </div>
                     </Tooltip>
                     <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.nextClicked, false, false)}>
@@ -2436,7 +2462,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     </Tooltip>
                     <div className="presPanel-button-text">
                         Slide {this.itemIndex + 1}
-                        {this.activeItem?.presIndexed !== undefined ? `(${this.activeItem.presIndexed}/${this.progressivizedItems(this.activeItem)?.length})` : ''} / {this.childDocs.length}
+                        {this.activeItem?.presentation_indexed !== undefined ? `(${this.activeItem.presentation_indexed}/${this.progressivizedItems(this.activeItem)?.length})` : ''} / {this.childDocs.length}
                     </div>
                     <div className="presPanel-divider" />
                     <div className="presPanel-button-text" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.exitClicked, false, false)}>
@@ -2460,6 +2486,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 moveDocument={returnFalse}
                                 ignoreUnrendered={true}
                                 childDragAction="move"
+                                setContentView={emptyFunction}
                                 //childLayoutFitWidth={returnTrue}
                                 childOpacity={returnOne}
                                 childClickScript={PresBox.navigateToDocScript}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index f31cf6147..aa514be3b 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -58,13 +58,13 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc;
     }
     @computed get targetDoc() {
-        return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc;
+        return Cast(this.rootDoc.presentation_targetDoc, Doc, null) || this.rootDoc;
     }
 
     componentDidMount() {
         this.layoutDoc.layout_hideLinkButton = true;
         this._heightDisposer = reaction(
-            () => ({ expand: this.rootDoc.presExpandInlineButton, height: this.collapsedHeight }),
+            () => ({ expand: this.rootDoc.presentation_expandInlineButton, height: this.collapsedHeight }),
             ({ expand, height }) => (this.layoutDoc._height = height + (expand ? this.expandViewHeight : 0)),
             { fireImmediately: true }
         );
@@ -79,7 +79,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord];
 
     @action
-    presExpandDocumentClick = () => (this.rootDoc.presExpandInlineButton = !this.rootDoc.presExpandInlineButton);
+    presExpandDocumentClick = () => (this.rootDoc.presentation_expandInlineButton = !this.rootDoc.presentation_expandInlineButton);
 
     embedHeight = (): number => this.collapsedHeight + this.expandViewHeight;
     // embedWidth = () => this.props.PanelWidth();
@@ -94,7 +94,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * presentation element.
      */
     @computed get renderEmbeddedInline() {
-        return !this.rootDoc.presExpandInlineButton || !this.targetDoc ? null : (
+        return !this.rootDoc.presentation_expandInlineButton || !this.targetDoc ? null : (
             <div className="presItem-embedded" style={{ height: this.embedHeight(), width: '50%' }}>
                 <DocumentView
                     Document={PresBox.targetRenderedDoc(this.rootDoc)}
@@ -158,9 +158,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @computed get transition() {
         let transitionInS: number;
-        if (this.rootDoc.presTransition) transitionInS = NumCast(this.rootDoc.presTransition) / 1000;
+        if (this.rootDoc.presentation_transition) transitionInS = NumCast(this.rootDoc.presentation_transition) / 1000;
         else transitionInS = 0.5;
-        return this.rootDoc.presMovement === PresMovement.Jump || this.rootDoc.presMovement === PresMovement.None ? null : 'M: ' + transitionInS + 's';
+        return this.rootDoc.presentation_movement === PresMovement.Jump || this.rootDoc.presentation_movement === PresMovement.None ? null : 'M: ' + transitionInS + 's';
     }
 
     private _itemRef: React.RefObject<HTMLDivElement> = React.createRef();
@@ -238,7 +238,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     onPointerMove = (e: PointerEvent) => {
         const slide = this._itemRef.current!;
-        const dragIsPresItem = DragManager.docsBeingDragged.some(d => d.presentationTargetDoc);
+        const dragIsPresItem = DragManager.docsBeingDragged.some(d => d.presentation_targetDoc);
         if (slide && dragIsPresItem) {
             const rect = slide.getBoundingClientRect();
             const y = e.clientY - rect.top; //y position within the element.
@@ -296,12 +296,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     updateCapturedContainerLayout = (presTargetDoc: Doc, activeItem: Doc) => {
         const targetDoc = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc;
-        activeItem.presX = NumCast(targetDoc.x);
-        activeItem.presY = NumCast(targetDoc.y);
-        activeItem.presRotation = NumCast(targetDoc.rotation);
-        activeItem.presWidth = NumCast(targetDoc.width);
-        activeItem.presHeight = NumCast(targetDoc.height);
-        activeItem.presPinLayout = true;
+        activeItem.config_x = NumCast(targetDoc.x);
+        activeItem.config_y = NumCast(targetDoc.y);
+        activeItem.config_rotation = NumCast(targetDoc.rotation);
+        activeItem.config_width = NumCast(targetDoc.width);
+        activeItem.config_height = NumCast(targetDoc.height);
+        activeItem.config_pinLayout = true;
     };
     /**
      * Method called for updating the view of the currently selected document
@@ -421,7 +421,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 <div
                     className="slideButton"
                     onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedContainerLayout(targetDoc, activeItem), true)}
-                    style={{ opacity: activeItem.presPinLayout ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
+                    style={{ opacity: activeItem.config_pinLayout ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
                     L
                 </div>
             </Tooltip>
@@ -431,7 +431,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 <div
                     className="slideButton"
                     onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))}
-                    style={{ opacity: activeItem.presPinData || activeItem.presPinView ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
+                    style={{ opacity: activeItem.config_pinData || activeItem.config_pinView ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
                     C
                 </div>
             </Tooltip>
@@ -445,19 +445,19 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         );
         if (this.indexInPres !== 0) {
             items.push(
-                <Tooltip key="arrow" title={<div className="dash-tooltip">{activeItem.groupWithUp ? 'Ungroup' : 'Group with up'}</div>}>
+                <Tooltip key="arrow" title={<div className="dash-tooltip">{activeItem.presentation_groupWithUp ? 'Ungroup' : 'Group with up'}</div>}>
                     <div
                         className="slideButton"
-                        onClick={() => (activeItem.groupWithUp = (NumCast(activeItem.groupWithUp) + 1) % 3)}
+                        onClick={() => (activeItem.presentation_groupWithUp = (NumCast(activeItem.presentation_groupWithUp) + 1) % 3)}
                         style={{
                             zIndex: 1000 - this.indexInPres,
                             fontWeight: 700,
-                            backgroundColor: activeItem.groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined,
-                            outline: NumCast(activeItem.groupWithUp) > 1 ? 'solid black 1px' : undefined,
-                            height: activeItem.groupWithUp ? 53 : 18,
-                            transform: activeItem.groupWithUp ? 'translate(0, -17px)' : undefined,
+                            backgroundColor: activeItem.presentation_groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined,
+                            outline: NumCast(activeItem.presentation_groupWithUp) > 1 ? 'solid black 1px' : undefined,
+                            height: activeItem.presentation_groupWithUp ? 53 : 18,
+                            transform: activeItem.presentation_groupWithUp ? 'translate(0, -17px)' : undefined,
                         }}>
-                        <div style={{ transform: activeItem.groupWithUp ? 'rotate(180deg) translate(0, -17.5px)' : 'rotate(0deg)' }}>
+                        <div style={{ transform: activeItem.presentation_groupWithUp ? 'rotate(180deg) translate(0, -17.5px)' : 'rotate(0deg)' }}>
                             <FontAwesomeIcon icon={'arrow-up'} onPointerDown={e => e.stopPropagation()} />
                         </div>
                     </div>
@@ -465,14 +465,14 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             );
         }
         items.push(
-            <Tooltip key="eye" title={<div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? 'Minimize' : 'Expand'}</div>}>
+            <Tooltip key="eye" title={<div className="dash-tooltip">{this.rootDoc.presentation_expandInlineButton ? 'Minimize' : 'Expand'}</div>}>
                 <div
                     className="slideButton"
                     onClick={e => {
                         e.stopPropagation();
                         this.presExpandDocumentClick();
                     }}>
-                    <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? 'eye-slash' : 'eye'} onPointerDown={e => e.stopPropagation()} />
+                    <FontAwesomeIcon icon={this.rootDoc.presentation_expandInlineButton ? 'eye-slash' : 'eye'} onPointerDown={e => e.stopPropagation()} />
                 </div>
             </Tooltip>
         );
@@ -534,7 +534,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             style={{
                                 display: 'inline-flex',
                                 pointerEvents: isSelected ? undefined : 'none',
-                                width: `calc(100% ${this.rootDoc.presExpandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`,
+                                width: `calc(100% ${this.rootDoc.presentation_expandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`,
                                 cursor: isSelected ? 'text' : 'grab',
                             }}>
                             <div
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index db6b1f011..caa72c9dc 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -24,7 +24,7 @@ export class Annotation extends React.Component<IAnnotationProps> {
     render() {
         return (
             <div style={{ display: this.props.anno.textCopied && !Doc.isBrushedHighlightedDegree(this.props.anno) ? 'none' : undefined }}>
-                {DocListCast(this.props.anno.textInlineAnnotations).map(a => (
+                {DocListCast(this.props.anno.text_inlineAnnotations).map(a => (
                     <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />
                 ))}
             </div>
@@ -61,7 +61,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
     isTargetToggler = () => BoolCast(this.annoTextRegion.followLinkToggle);
     @undoBatch
     showTargetTrail = (anchor: Doc) => {
-        const trail = DocCast(anchor.presTrail);
+        const trail = DocCast(anchor.presentationTrail);
         if (trail) {
             Doc.ActivePresentation = trail;
             this.props.addDocTab(trail, OpenWhere.replaceRight);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 1319a236d..395fbd1ca 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -68,7 +68,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     private _styleRule: any; // stylesheet rule for making hyperlinks clickable
     private _retries = 0; // number of times tried to create the PDF viewer
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
-    private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }) => void);
+    private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void);
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -95,7 +95,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']), this.props.childFilters(), this.props.childFiltersByRanges());
     }
     @computed get inlineTextAnnotations() {
-        return this.allAnnotations.filter(a => a.textInlineAnnotations);
+        return this.allAnnotations.filter(a => a.text_inlineAnnotations);
     }
 
     componentDidMount = async () => {
@@ -195,7 +195,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return focusSpeed;
     };
     crop = (region: Doc | undefined, addCrop?: boolean) => this.props.crop(region, addCrop);
-    brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._setBrushViewer?.(view);
+    brushView = (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => this._setBrushViewer?.(view, transTime);
 
     @action
     setupPdfJsViewer = async () => {
@@ -470,7 +470,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     };
 
     setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
-    setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => (this._setBrushViewer = func);
+    setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => (this._setBrushViewer = func);
 
     @action
     onZoomWheel = (e: React.WheelEvent) => {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index eb52cff88..d3912b8a0 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1363,9 +1363,9 @@ export namespace Doc {
             UnhighlightWatchers.push(watcher);
         } else watcher();
     }
-    export function linkFollowHighlight(destDoc: Doc | Doc[], dataAndDisplayDocs = true, presEffect?: Doc) {
+    export function linkFollowHighlight(destDoc: Doc | Doc[], dataAndDisplayDocs = true, presentation_effect?: Doc) {
         linkFollowUnhighlight();
-        (destDoc instanceof Doc ? [destDoc] : destDoc).forEach(doc => Doc.HighlightDoc(doc, dataAndDisplayDocs, presEffect));
+        (destDoc instanceof Doc ? [destDoc] : destDoc).forEach(doc => Doc.HighlightDoc(doc, dataAndDisplayDocs, presentation_effect));
         document.removeEventListener('pointerdown', linkFollowUnhighlight);
         document.addEventListener('pointerdown', linkFollowUnhighlight);
         if (UnhighlightTimer) clearTimeout(UnhighlightTimer);
@@ -1380,11 +1380,11 @@ export namespace Doc {
         if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return false;
         return doc[Highlight] || Doc.GetProto(doc)[Highlight];
     }
-    export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true, presEffect?: Doc) {
+    export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true, presentation_effect?: Doc) {
         runInAction(() => {
             highlightedDocs.add(doc);
             doc[Highlight] = true;
-            doc[Animation] = presEffect;
+            doc[Animation] = presentation_effect;
             if (dataAndDisplayDocs) {
                 highlightedDocs.add(Doc.GetProto(doc));
                 Doc.GetProto(doc)[Highlight] = true;
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 76b287be7..e33a17416 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -72,11 +72,11 @@ export const documentSchema = createSchema({
     stroke_endMarker: 'string',
     stroke_dash: 'string',
     textTransform: 'string',
-    treeViewOpen: 'boolean', //  flag denoting whether the documents sub-tree (contents) is visible or hidden
-    treeViewExpandedView: 'string', // name of field whose contents are being displayed as the document's subtree
-    treeViewExpandedViewLock: 'boolean', // whether the expanded view can be changed
-    treeViewOpenIsTransient: 'boolean', // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
-    treeViewType: 'string', // whether tree view is an outline, file syste or (default) hierarchy. For outline, clicks edit document titles immediately since double-click opening is turned off
+    treeView_Open: 'boolean', //  flag denoting whether the documents sub-tree (contents) is visible or hidden
+    treeView_ExpandedView: 'string', // name of field whose contents are being displayed as the document's subtree
+    treeView_ExpandedViewLock: 'boolean', // whether the expanded view can be changed
+    treeView_OpenIsTransient: 'boolean', // ignores the treeView_Open flag (for allowing a view to not be slaved to other views of the document)
+    treeView_Type: 'string', // whether tree view is an outline, file syste or (default) hierarchy. For outline, clicks edit document titles immediately since double-click opening is turned off
 
     // interaction and linking properties
     ignoreClick: 'boolean', // whether documents ignores input clicks (but does not ignore manipulation and other events)
-- 
cgit v1.2.3-70-g09d2


From eb35837e5ffe8e7eba9decea1fae5c163528dc1e Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Mon, 21 Aug 2023 11:59:05 -0400
Subject: Buggy

---
 src/client/documents/Documents.ts               |   5 +-
 src/client/views/nodes/MapBox/MapBox.scss       |   4 +
 src/client/views/nodes/MapBox/MapBox.tsx        | 201 +++++++++++++-----------
 src/client/views/nodes/MapBox/MapPushpinBox.tsx |   4 +-
 src/client/views/nodes/trails/PresBox.tsx       |   5 +-
 5 files changed, 120 insertions(+), 99 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index af4cf2243..eb777aae2 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -368,8 +368,6 @@ export class DocumentOptions {
     waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire.  'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait
     onPointerDown?: ScriptField;
     onPointerUp?: ScriptField;
-    openFactoryLocation?: string; // an OpenWhere value to place the factory created document
-    openFactoryAsDelegate?: BOOLt = new BoolInfo('create a delegate of the factory');
     _forceActive?: BOOLt = new BoolInfo('flag to handle pointer events when not selected (or otherwise active)');
     _dragOnlyWithinContainer?: BOOLt = new BoolInfo('whether the document should remain in its collection when someone tries to drag and drop it elsewhere');
     _raiseWhenDragged?: BOOLt = new BoolInfo('whether a document is brought to front when dragged.');
@@ -381,7 +379,6 @@ export class DocumentOptions {
     cloneFieldFilter?: List<string>; // fields not to copy when the document is clonedclipboard?: Doc;
     dragWhenActive?: BOOLt = new BoolInfo('should document drag when it is active - e.g., pileView, group');
     dragAction?: DROPt = new DAInfo('how to drag document when it is active (e.g., tree, groups)');
-    dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
     dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script
     clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script
     onDragStart?: ScriptField; //script to execute at start of drag operation --  e.g., when a "creator" button is dragged this script generates a different document to drop
@@ -1104,7 +1101,7 @@ export namespace Docs {
             );
         }
         export function MapanchorDocument(options: DocumentOptions = {}, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.MAP), options?.data, options, id);
         }
 
         export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index fcd4fc9be..39fa3262e 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -11,6 +11,10 @@
         padding: 12;
         font-size: 17;
     }
+    .mapBox-searchbar{
+        display:flex;
+        flex-direction: row;
+    }
     .mapBox-topbar{
         display:flex;
         flex-direction: row;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 283d57bb6..bad1a3ebd 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,7 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { GoogleMapProps, Marker } from '@react-google-maps/api';
 import BingMapsReact from 'bingmaps-react';
-import { EditableText, Toggle } from 'browndash-components';
+import { EditableText, IconButton, Toggle, Type } from 'browndash-components';
 import e from 'connect-flash';
 import { truncateSync } from 'fs';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
@@ -13,12 +13,12 @@ import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { ScriptField } from '../../../../fields/ScriptField';
 import { NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnIgnore, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
-import { UndoManager } from '../../../util/UndoManager';
+import { undoable, UndoManager } from '../../../util/UndoManager';
 import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
@@ -116,7 +116,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private _marqueeing: number[] | undefined;
     @observable private inputRef = React.createRef<HTMLInputElement>();
     @observable private searchMarkers: google.maps.Marker[] = [];
-    // @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
+    @observable private searchBox =  undefined as any; //  new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @computed get allSidebarDocs() {
         return DocListCast(this.dataDoc[this.SidebarKey]);
@@ -138,45 +138,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _ref: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposer: {[key:string]:IReactionDisposer} = {}
     componentDidMount() {
-        this.props.setContentView?.(this);
-        this._disposer.location = reaction(() => ({lat:this.rootDoc.latitude, lng:this.rootDoc.longitude, zoom:this.rootDoc.zoom,mapType:this.rootDoc.mapType}), 
-        (locationObject) => {
-
-            this._bingMap.current.setView({
-                mapTypeId: locationObject.mapType,
-                zoom:locationObject.zoom,
-                center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng),
-            });
-
-        }, {fireImmediately: true});
-        // this._disposer.pins = reaction(() => ({pinList:this.dataDoc[this.annotationKey]}), 
-        // (pinsObject) => {
-        //     this._bingMap.current.entities.clear();
-        //     pinsObject.pinList.map((pushpin: Doc) => (
-        //                         <DocumentView
-        //                             {...this.props}
-        //                             Document={pushpin}
-        //                             DataDoc={undefined}
-        //                             PanelWidth={returnOne}
-        //                             PanelHeight={returnOne}
-        //                             NativeWidth={returnOne} 
-        //                             NativeHeight={returnOne}
-        //                             onClick={()=>new ScriptField(undefined)}
-        //                             onKey={undefined}
-        //                             onDoubleClick={undefined}
-        //                             onBrowseClick={undefined}
-        //                             docFilters={returnEmptyDoclist}
-        //                             docRangeFilters={returnEmptyDoclist}
-        //                             searchFilterDocs={returnEmptyDoclist}
-        //                             isDocumentActive={returnFalse}
-        //                             isContentActive={returnFalse}
-        //                             addDocTab={returnFalse}
-        //                             ScreenToLocalTransform={()=>new Transform(0,0,0)}
-        //                             fitContentsToBox={undefined}
-        //                             focus={returnOne}
-        //                 />));
-
-        // }, {fireImmediately: false});
 
         
     }
@@ -638,7 +599,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      *
      *
      **/
-
+    @observable
     bingSearchBarContents: any = 'Boston, MA'; // For Bing Maps: The contents of the Bing search bar (string)
 
     geoDataRequestOptions = {
@@ -675,7 +636,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Pushpin onclick
      */
     @action
-    pushpinClicked = (pinDoc:Doc,pin:any) => {
+    pushpinClicked = (pinDoc:Doc) => {
         // TODO:
         // if (sidebarannos is not open) open sidebarannos
         // creates button onclick removes the doc from annotations
@@ -685,27 +646,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.dataDoc.longitude = pinDoc.lng;
 
         
-
-
-    //     @action
-    // onPointerDown = (e: React.PointerEvent) => {
-    //     if (e.button === 2 || e.ctrlKey) {
-    //         AnchorMenu.Instance.Status = 'annotation';
-    //         AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
-    //         AnchorMenu.Instance.Pinned = false;
-    //         AnchorMenu.Instance.PinToPres = this.pinToPres;
-    //         AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle;
-    //         AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
-    //         AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion);
-    //         AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
-    //         e.stopPropagation();
-    //     } else if (e.button === 0) {
-    //         e.stopPropagation();
-    //         LinkFollower.FollowLink(undefined, this.annoTextRegion, false);
-    //     }
-    // };
-
-        // TODO: UPDATE FOR DASHDOC SELECTION
     };
 
     /**
@@ -811,10 +751,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 presLat: NumCast(this.dataDoc.latitude),
                 presLong: NumCast(this.dataDoc.longitude),
                 presZoom: NumCast(this.dataDoc.zoom),
-                presMapType: this.dataDoc.mapType,
+                presMapType: StrCast(this.dataDoc.mapType),
                 // preslocationToLookAt:this.dataDoc.locationToLookAt,
                 // presType: 
-                unrendered: true,
+                layout_unrendered: true,
                 annotationOn: this.rootDoc,
             });
         if (anchor) {
@@ -839,7 +779,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         
         this._bingMap.current.entities.push(pushPin);
 
-        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin,pushPin));
+        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin));
         this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pushPin, pin));
     }
 
@@ -876,7 +816,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Map options
      */
     bingMapOptions = {
-        navigationBarMode: 'compact',
+        navigationBarMode: 'square',
         backgroundColor: '#f1f3f4',
         enableInertia: true,
         supportedMapTypes:['grayscale', 'canvasLight'],
@@ -901,19 +841,71 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         else this.placePinOn = true;
     };
 
+    @action
+    searchbarOnEdit=(newText:string)=>{
+        this.bingSearchBarContents = newText
+    }
+
     /*
      * Called when BingMap is first rendered 
      * Initializes starting values
      */
     bingMapReady = (map: any) => {
         this._bingMap = map.map;
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', this.updateLayout);
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', this.updateMapType);
-        this.updateLayout();
-        this.updateMapType();
+        if (!this._bingMap.current) {
+            alert("NO Map!?")
+        }
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', undoable(this.mapOnClick, "Added Pin to Map"));
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.updateLayout, "Map Layout Change"));
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, "Map ViewType Change"));
+        //this.updateLayout();
+       // this.updateMapType();
+        this.props.setContentView?.(this);
+        this._disposer.location = reaction(() => ({lat:this.rootDoc.latitude, lng:this.rootDoc.longitude, zoom:this.rootDoc.zoom,mapType:this.rootDoc.mapType}), 
+        (locationObject) => {
+            // if (this._bingMap.current)
+                this._bingMap.current?.setView({
+                    mapTypeId: locationObject.mapType,
+                    zoom:locationObject.zoom,
+                    center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng),
+                });
+
+        }, {fireImmediately: true});
+        // this._disposer.pins = reaction(() => ({pinList:this.dataDoc[this.annotationKey]}), 
+        // (pinsObject) => {
+        //     this._bingMap.current.entities.clear();
+        //     pinsObject.pinList.map((pushpin: Doc) => (
+        //                         <DocumentView
+        //                             {...this.props}
+        //                             Document={pushpin}
+        //                             DataDoc={undefined}
+        //                             PanelWidth={returnOne}
+        //                             PanelHeight={returnOne}
+        //                             NativeWidth={returnOne} 
+        //                             NativeHeight={returnOne}
+        //                             onClick={()=>new ScriptField(undefined)}
+        //                             onKey={undefined}
+        //                             onDoubleClick={undefined}
+        //                             onBrowseClick={undefined}
+        //                             docFilters={returnEmptyDoclist}
+        //                             docRangeFilters={returnEmptyDoclist}
+        //                             searchFilterDocs={returnEmptyDoclist}
+        //                             isDocumentActive={returnFalse}
+        //                             isContentActive={returnFalse}
+        //                             addDocTab={returnFalse}
+        //                             ScreenToLocalTransform={()=>new Transform(0,0,0)}
+        //                             fitContentsToBox={undefined}
+        //                             focus={returnOne}
+        //                 />));
+
+        // }, {fireImmediately: false});
+    }
+    searchbarKeyDown = (e:any)=>{
+        if (e.key === 'Enter') {
+            this.bingSearch()
+          }
+            
     }
-        
     render() {
         const renderAnnotations = (childFilters?: () => string[]) => null;
         return (
@@ -937,9 +929,44 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
                     {this.annotationLayer}
 
-                    <EditableText setEditing={(isEditing: boolean) => ({})} editing onEdit={(newText: string) => (this.bingSearchBarContents = newText)} placeholder="Boston, MA" text="Boston, MA" />
+
+                    <div className='mapBox-searchbar'>
+                        <EditableText 
+                            // editing 
+                            setVal={(newText:string|number) => typeof newText === "string" && this.searchbarOnEdit(newText)}
+                            placeholder="Boston" 
+                            // text="text" 
+                            />
+                        <IconButton
+                        icon={<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="magnifying-glass" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" color="#DFDFDF"><path fill="currentColor" d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"></path></svg>}
+                        onClick={this.bingSearch}
+                        onDoubleClick={function noRefCheck(){}}
+                        onPointerDown={function noRefCheck(){}}
+                        onPointerDownCapture={function noRefCheck(){}}
+                        onPointerMove={function noRefCheck(){}}
+                        onPointerMoveCapture={function noRefCheck(){}}
+                        onPointerUp={function noRefCheck(){}}
+                        type={Type.TERT}
+                        />
+                        <input type="checkbox"
+                                    id="switch"
+                                    className="checkbox" 
+                                    checked={this.placePinOn}
+                                    onChange={this.togglePlacePin}
+                                    />
+                                        
+                                <label htmlFor="switch"
+                                    className="toggle">
+                                    <p>
+                                    {this.placePinOn ? 'Place Pin ON' : 'Place Pin OFF'}
+                                    </p>
+                                </label>
+                    </div>
+                    
+                    
                     <div className='mapBox-topbar'>
-                        <input type="button" value="Search" onClick={this.bingSearch} />
+                        {/* <input type="button" value="Search" onClick={this.bingSearch} /> */}
+                        
 
                         {/* {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />} */}
                         {/* {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />}
@@ -957,19 +984,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             type="secondary"
                         /> */}
 
-                        <input type="checkbox"
-                                    id="switch"
-                                    className="checkbox" 
-                                    checked={this.placePinOn}
-                                    onChange={this.togglePlacePin}
-                                    />
-                                        
-                                <label htmlFor="switch"
-                                    className="toggle">
-                                    <p>
-                                    {this.placePinOn ? 'Place Pin ON' : 'Place Pin OFF'}
-                                    </p>
-                                </label>
+                        
                     </div>
                     
 
@@ -992,8 +1007,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                     onKey={undefined}
                                     onDoubleClick={undefined}
                                     onBrowseClick={undefined}
-                                    docFilters={returnEmptyDoclist}
-                                    docRangeFilters={returnEmptyDoclist}
+                                    childFilters={returnEmptyFilter}
+                                    childFiltersByRanges={returnEmptyFilter}
                                     searchFilterDocs={returnEmptyDoclist}
                                     isDocumentActive={returnFalse}
                                     isContentActive={returnFalse}
diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
index 588bddd1a..6369f9e04 100644
--- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx
+++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
@@ -5,6 +5,7 @@ import { FieldView, FieldViewProps } from '../FieldView';
 import React = require('react');
 import { computed } from 'mobx';
 import { MapBox } from './MapBox';
+import { undoable } from '../../../util/UndoManager';
 
 
 /**
@@ -18,7 +19,8 @@ export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return FieldView.LayoutString(MapPushpinBox, fieldKey);
     }
     componentDidMount() {
-        this.mapBoxView.addPushpin(this.rootDoc);
+        // if (this.mapBoxView)
+            this.mapBoxView.addPushpin(this.rootDoc);
     }
     componentWillUnmount() {
         // this.mapBoxView.removePushpin(this.rootDoc);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 76f42778b..1560ce3e1 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -659,7 +659,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 pinDoc.presYRange = undefined; //targetDoc?.yrange;
             }
             if (pinProps.pinData.map) {
-                pinDoc.presLat = targetDoc?.lat;
+                pinDoc.presLat = targetDoc?.latitude;
+                pinDoc.presLong = targetDoc?.longitude;
+                pinDoc.presZoom = targetDoc?.zoom;
+                pinDoc.mapType = targetDoc?.mapType;
                 //...
             }
             if (pinProps.pinData.poslayoutview)
-- 
cgit v1.2.3-70-g09d2


From 2207673156981e6313909023dbbde3d1a7f4d7a6 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Aug 2023 12:21:48 -0400
Subject: merged with master

---
 src/client/views/nodes/MapBox/MapBox.tsx | 293 ++++++++++++++-----------------
 1 file changed, 135 insertions(+), 158 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 9adae0a6e..f93126e04 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -116,7 +116,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private _marqueeing: number[] | undefined;
     @observable private inputRef = React.createRef<HTMLInputElement>();
     @observable private searchMarkers: google.maps.Marker[] = [];
-    @observable private searchBox =  undefined as any; //  new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
+    @observable private searchBox = undefined as any; //  new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @computed get allSidebarDocs() {
         return DocListCast(this.dataDoc[this.SidebarKey]);
@@ -136,14 +136,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     static _hadSelection: boolean = false;
     private _sidebarRef = React.createRef<SidebarAnnos>();
     private _ref: React.RefObject<HTMLDivElement> = React.createRef();
-    private _disposer: {[key:string]:IReactionDisposer} = {}
-    componentDidMount() {
-
-        
-    }
+    private _disposer: { [key: string]: IReactionDisposer } = {};
+    componentDidMount() {}
 
     componentWillUnmount(): void {
-        Object.keys(this._disposer).forEach(key => this._disposer[key]?.())
+        Object.keys(this._disposer).forEach(key => this._disposer[key]?.());
     }
     // iterate allMarkers to size, center, and zoom map to contain all markers
     private fitBounds = (map: google.maps.Map) => {
@@ -606,7 +603,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         entityType: 'PopulatedPlace',
     };
 
-    
     // incrementer: number = 0;
     /*
      * Creates Pushpin doc and adds it to the list of annotations
@@ -614,8 +610,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     createPushpin = (latitude: number, longitude: number) => {
         // Stores the pushpin as a MapMarkerDocument
-        const mapMarker = Docs.Create.PushpinDocument(NumCast(latitude), NumCast(longitude), false, [], {}
-        // ,'pushpinIDamongus'+ this.incrementer++
+        const mapMarker = Docs.Create.PushpinDocument(
+            NumCast(latitude),
+            NumCast(longitude),
+            false,
+            [],
+            {}
+            // ,'pushpinIDamongus'+ this.incrementer++
         );
         this.addDocument(mapMarker, this.annotationKey);
 
@@ -626,17 +627,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Pushpin dblclick
      */
     @action
-    pushpinDblClicked = (pin:any, pinDoc?:Doc) => {
-        if(pinDoc)
-            this.removePushpin(pinDoc,pin);
-        else
-            this._bingMap.current.entities.remove(pin);
+    pushpinDblClicked = (pin: any, pinDoc?: Doc) => {
+        if (pinDoc) this.removePushpin(pinDoc, pin);
+        else this._bingMap.current.entities.remove(pin);
     };
     /*
      * Pushpin onclick
      */
     @action
-    pushpinClicked = (pinDoc:Doc) => {
+    pushpinClicked = (pinDoc: Doc) => {
         // TODO:
         // if (sidebarannos is not open) open sidebarannos
         // creates button onclick removes the doc from annotations
@@ -644,8 +643,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // pan to pushpin location
         this.dataDoc.latitude = pinDoc.lat;
         this.dataDoc.longitude = pinDoc.lng;
-
-        
     };
 
     /**
@@ -667,32 +664,27 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     };
 
-
-    
-
     /*
      * Updates values of layout doc to match the current map
      */
     @action
     updateLayout = () => {
-            this.dataDoc.latitude = this._bingMap.current.getCenter().latitude;
-            this.dataDoc.longitude = this._bingMap.current.getCenter().longitude;
-            this.dataDoc.zoom = this._bingMap.current.getZoom();
-            // if(this.dataDoc.mapType == 'x'){
-            //     this.dataDoc.locationToLookAt
-            // }
-            // this.dataDoc.mapType = new this.MicrosoftMaps.MapTypeId();
+        this.dataDoc.latitude = this._bingMap.current.getCenter().latitude;
+        this.dataDoc.longitude = this._bingMap.current.getCenter().longitude;
+        this.dataDoc.mapZoom = this._bingMap.current.getZoom();
+        // if(this.dataDoc.mapType == 'x'){
+        //     this.dataDoc.locationToLookAt
+        // }
+        // this.dataDoc.mapType = new this.MicrosoftMaps.MapTypeId();
     };
     /*
      * Updates maptype
      */
     @action
     updateMapType = () => {
-            this.dataDoc.mapType = this._bingMap.current.getMapTypeId();
-
+        this.dataDoc.mapType = this._bingMap.current.getMapTypeId();
     };
 
-
     searched_pin: any;
     /*
      * For Bing Maps
@@ -704,7 +696,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
         this.dataDoc.latitude = location.latitude;
         this.dataDoc.longitude = location.longitude;
-        this.dataDoc.zoom = this._bingMap.current.getZoom();
+        this.dataDoc.mapZoom = this._bingMap.current.getZoom();
         // this.dataDoc.mapType = this._bingMap.current.getMapTypeId();
         // this.bingSearchBarContents=location.name;
 
@@ -714,21 +706,17 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // this._bingMap.current.setView({
         //     center: new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude),
         // });
-        
-        
 
-        
         // Creates a temporary pin but does not add it to the dataDoc
         var temp = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
             color: 'blue',
         });
-        
+
         this.MicrosoftMaps.Events.addHandler(temp, 'dblclick', (e: any) => this.pushpinDblClicked(temp));
         if (temp != this.searched_pin || this.searched_pin == null) {
             this._bingMap.current.entities.push(temp);
             this.searched_pin = temp;
         }
-
     };
 
     /**
@@ -745,18 +733,17 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Returns doc w/ relevant info
      */
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
-        const anchor =
-            Docs.Create.MapanchorDocument({
-                title: 'MapAnchor:' + this.rootDoc.title,
-                presLat: NumCast(this.dataDoc.latitude),
-                presLong: NumCast(this.dataDoc.longitude),
-                presZoom: NumCast(this.dataDoc.zoom),
-                presMapType: StrCast(this.dataDoc.mapType),
-                // preslocationToLookAt:this.dataDoc.locationToLookAt,
-                // presType: 
-                layout_unrendered: true,
-                annotationOn: this.rootDoc,
-            });
+        const anchor = Docs.Create.MapanchorDocument({
+            title: 'MapAnchor:' + this.rootDoc.title,
+            config_latitude: NumCast(this.dataDoc.latitude),
+            config_longitude: NumCast(this.dataDoc.longitude),
+            config_zoom: NumCast(this.dataDoc.mapZoom),
+            config_mapType: StrCast(this.dataDoc.mapType),
+            // preslocationToLookAt:this.dataDoc.locationToLookAt,
+            // presType:
+            layout_unrendered: true,
+            annotationOn: this.rootDoc,
+        });
         if (anchor) {
             if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
             /* addAsAnnotation &&*/ this.addDocument(anchor);
@@ -767,33 +754,33 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     /*
-    *   Input: pin doc
-    *   Adds MicrosoftMaps Pushpin to the map (render)
-    */
+     *   Input: pin doc
+     *   Adds MicrosoftMaps Pushpin to the map (render)
+     */
     @action
     addPushpin = (pin: Doc) => {
-        const pushPin = pin.infoWindowOpen ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {}): new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), 
-        // {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'}
-        );
+        const pushPin = pin.infoWindowOpen
+            ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.latitude, pin.longitude), {})
+            : new this.MicrosoftMaps.Pushpin(
+                  new this.MicrosoftMaps.Location(pin.latitude, pin.longitude)
+                  // {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'}
+              );
 
-        
         this._bingMap.current.entities.push(pushPin);
 
         this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin));
         this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pushPin, pin));
-    }
-
-
+    };
 
     @observable
-    pinIsSelected_TEMPORARY:boolean=false; // toggles if remove pin button appears
+    pinIsSelected_TEMPORARY: boolean = false; // toggles if remove pin button appears
 
     /*
-    *   Input: pin doc
-    *   Removes MicrosoftMaps Pushpin to the map (render)
-    */
+     *   Input: pin doc
+     *   Removes MicrosoftMaps Pushpin to the map (render)
+     */
     @action
-    removePushpin = (pinDoc:Doc,pin:any)=>{
+    removePushpin = (pinDoc: Doc, pin: any) => {
         // this.allMapPushpins
         // this.allMapPushpins.map(pin => this.addPushpin(pin));
         // this._bingMap.current.entities.clear();
@@ -801,7 +788,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.removeDocument(pinDoc, this.annotationKey);
 
         // this.dataDoc[this.annotationKey]
-    }
+    };
 
     /**
      * View options for bing maps
@@ -819,14 +806,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         navigationBarMode: 'square',
         backgroundColor: '#f1f3f4',
         enableInertia: true,
-        supportedMapTypes:['grayscale', 'canvasLight'],
-        disableMapTypeSelectorMouseOver :true,
+        supportedMapTypes: ['grayscale', 'canvasLight'],
+        disableMapTypeSelectorMouseOver: true,
         // showScalebar:true
         // disableRoadView:true,
         // disableBirdseye:true
-        streetsideOptions:{
-            showProblemReporting:false,
-            showCurrentAddress:false,
+        streetsideOptions: {
+            showProblemReporting: false,
+            showCurrentAddress: false,
         },
     };
 
@@ -842,36 +829,38 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     @action
-    searchbarOnEdit=(newText:string)=>{
-        this.bingSearchBarContents = newText
-    }
+    searchbarOnEdit = (newText: string) => {
+        this.bingSearchBarContents = newText;
+    };
 
     /*
-     * Called when BingMap is first rendered 
+     * Called when BingMap is first rendered
      * Initializes starting values
      */
     bingMapReady = (map: any) => {
         this._bingMap = map.map;
         if (!this._bingMap.current) {
-            alert("NO Map!?")
+            alert('NO Map!?');
         }
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', undoable(this.mapOnClick, "Added Pin to Map"));
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.updateLayout, "Map Layout Change"));
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, "Map ViewType Change"));
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', undoable(this.mapOnClick, 'Added Pin to Map'));
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.updateLayout, 'Map Layout Change'));
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change'));
         //this.updateLayout();
-       // this.updateMapType();
+        // this.updateMapType();
         this.props.setContentView?.(this);
-        this._disposer.location = reaction(() => ({lat:this.rootDoc.latitude, lng:this.rootDoc.longitude, zoom:this.rootDoc.zoom,mapType:this.rootDoc.mapType}), 
-        (locationObject) => {
-            // if (this._bingMap.current)
+        this._disposer.location = reaction(
+            () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }),
+            locationObject => {
+                // if (this._bingMap.current)
                 this._bingMap.current?.setView({
                     mapTypeId: locationObject.mapType,
-                    zoom:locationObject.zoom,
+                    zoom: locationObject.zoom,
                     center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng),
                 });
-
-        }, {fireImmediately: true});
-        // this._disposer.pins = reaction(() => ({pinList:this.dataDoc[this.annotationKey]}), 
+            },
+            { fireImmediately: true }
+        );
+        // this._disposer.pins = reaction(() => ({pinList:this.dataDoc[this.annotationKey]}),
         // (pinsObject) => {
         //     this._bingMap.current.entities.clear();
         //     pinsObject.pinList.map((pushpin: Doc) => (
@@ -881,7 +870,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //                             DataDoc={undefined}
         //                             PanelWidth={returnOne}
         //                             PanelHeight={returnOne}
-        //                             NativeWidth={returnOne} 
+        //                             NativeWidth={returnOne}
         //                             NativeHeight={returnOne}
         //                             onClick={()=>new ScriptField(undefined)}
         //                             onKey={undefined}
@@ -899,13 +888,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //                 />));
 
         // }, {fireImmediately: false});
-    }
-    searchbarKeyDown = (e:any)=>{
+    };
+    searchbarKeyDown = (e: any) => {
         if (e.key === 'Enter') {
-            this.bingSearch()
-          }
-            
-    }
+            this.bingSearch();
+        }
+    };
     render() {
         const renderAnnotations = (childFilters?: () => string[]) => null;
         return (
@@ -929,44 +917,39 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
                     {this.annotationLayer}
 
-
-                    <div className='mapBox-searchbar'>
-                        <EditableText 
-                            // editing 
-                            setVal={(newText:string|number) => typeof newText === "string" && this.searchbarOnEdit(newText)}
-                            placeholder="Boston" 
-                            // text="text" 
-                            />
+                    <div className="mapBox-searchbar">
+                        <EditableText
+                            // editing
+                            setVal={(newText: string | number) => typeof newText === 'string' && this.searchbarOnEdit(newText)}
+                            placeholder="Boston"
+                            // text="text"
+                        />
                         <IconButton
-                        icon={<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="magnifying-glass" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" color="#DFDFDF"><path fill="currentColor" d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"></path></svg>}
-                        onClick={this.bingSearch}
-                        onDoubleClick={function noRefCheck(){}}
-                        onPointerDown={function noRefCheck(){}}
-                        onPointerDownCapture={function noRefCheck(){}}
-                        onPointerMove={function noRefCheck(){}}
-                        onPointerMoveCapture={function noRefCheck(){}}
-                        onPointerUp={function noRefCheck(){}}
-                        type={Type.TERT}
+                            icon={
+                                <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="magnifying-glass" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" color="#DFDFDF">
+                                    <path
+                                        fill="currentColor"
+                                        d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"></path>
+                                </svg>
+                            }
+                            onClick={this.bingSearch}
+                            onDoubleClick={function noRefCheck() {}}
+                            onPointerDown={function noRefCheck() {}}
+                            onPointerDownCapture={function noRefCheck() {}}
+                            onPointerMove={function noRefCheck() {}}
+                            onPointerMoveCapture={function noRefCheck() {}}
+                            onPointerUp={function noRefCheck() {}}
+                            type={Type.TERT}
                         />
-                        <input type="checkbox"
-                                    id="switch"
-                                    className="checkbox" 
-                                    checked={this.placePinOn}
-                                    onChange={this.togglePlacePin}
-                                    />
-                                        
-                                <label htmlFor="switch"
-                                    className="toggle">
-                                    <p>
-                                    {this.placePinOn ? 'Place Pin ON' : 'Place Pin OFF'}
-                                    </p>
-                                </label>
+                        <input type="checkbox" id="switch" className="checkbox" checked={this.placePinOn} onChange={this.togglePlacePin} />
+
+                        <label htmlFor="switch" className="toggle">
+                            <p>{this.placePinOn ? 'Place Pin ON' : 'Place Pin OFF'}</p>
+                        </label>
                     </div>
-                    
-                    
-                    <div className='mapBox-topbar'>
+
+                    <div className="mapBox-topbar">
                         {/* <input type="button" value="Search" onClick={this.bingSearch} /> */}
-                        
 
                         {/* {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />} */}
                         {/* {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />}
@@ -983,41 +966,35 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             toggleStatus= {false}
                             type="secondary"
                         /> */}
-
-                        
                     </div>
-                    
 
                     {/* {this.pinIsSelected_TEMPORARY? <input type="button" value="Delete Pin" onClick={this.removeSelectedPin} /> : null} */}
-                    <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions}>
-                    </BingMapsReact>
+                    <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions}></BingMapsReact>
                     <div>
-                        
-                        {this.allMapPushpins
-                            .map(pushpin => (
-                                <DocumentView
-                                    {...this.props}
-                                    Document={pushpin}
-                                    DataDoc={undefined}
-                                    PanelWidth={returnOne}
-                                    PanelHeight={returnOne}
-                                    NativeWidth={returnOne} 
-                                    NativeHeight={returnOne}
-                                    onClick={()=>new ScriptField(undefined)}
-                                    onKey={undefined}
-                                    onDoubleClick={undefined}
-                                    onBrowseClick={undefined}
-                                    childFilters={returnEmptyFilter}
-                                    childFiltersByRanges={returnEmptyFilter}
-                                    searchFilterDocs={returnEmptyDoclist}
-                                    isDocumentActive={returnFalse}
-                                    isContentActive={returnFalse}
-                                    addDocTab={returnFalse}
-                                    ScreenToLocalTransform={()=>new Transform(0,0,0)}
-                                    fitContentsToBox={undefined}
-                                    focus={returnOne}
-                        />
-                            ))}
+                        {this.allMapPushpins.map(pushpin => (
+                            <DocumentView
+                                {...this.props}
+                                Document={pushpin}
+                                DataDoc={undefined}
+                                PanelWidth={returnOne}
+                                PanelHeight={returnOne}
+                                NativeWidth={returnOne}
+                                NativeHeight={returnOne}
+                                onClick={() => new ScriptField(undefined)}
+                                onKey={undefined}
+                                onDoubleClick={undefined}
+                                onBrowseClick={undefined}
+                                childFilters={returnEmptyFilter}
+                                childFiltersByRanges={returnEmptyFilter}
+                                searchFilterDocs={returnEmptyDoclist}
+                                isDocumentActive={returnFalse}
+                                isContentActive={returnFalse}
+                                addDocTab={returnFalse}
+                                ScreenToLocalTransform={() => new Transform(0, 0, 0)}
+                                fitContentsToBox={undefined}
+                                focus={returnOne}
+                            />
+                        ))}
                     </div>
                     {/* <MapBoxInfoWindow
                                     key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}
-- 
cgit v1.2.3-70-g09d2


From c2d4aafbe58d5b3ae091c4ce3be379b693e04075 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Aug 2023 12:23:39 -0400
Subject: from last

---
 src/client/views/nodes/MapBox/MapBox.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index f93126e04..50d1aa9d6 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -737,7 +737,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             title: 'MapAnchor:' + this.rootDoc.title,
             config_latitude: NumCast(this.dataDoc.latitude),
             config_longitude: NumCast(this.dataDoc.longitude),
-            config_zoom: NumCast(this.dataDoc.mapZoom),
+            config_mapZoom: NumCast(this.dataDoc.mapZoom),
             config_mapType: StrCast(this.dataDoc.mapType),
             // preslocationToLookAt:this.dataDoc.locationToLookAt,
             // presType:
-- 
cgit v1.2.3-70-g09d2


From 565d4c98628f547ba27e2d9c74138602c8cbd7a5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Aug 2023 12:40:39 -0400
Subject: fixed calling setComponentView for maps

---
 src/client/views/nodes/MapBox/MapBox.tsx        | 147 ++----------------------
 src/client/views/nodes/MapBox/MapPushpinBox.tsx |   9 +-
 2 files changed, 12 insertions(+), 144 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 50d1aa9d6..860528ec4 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,9 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { GoogleMapProps, Marker } from '@react-google-maps/api';
 import BingMapsReact from 'bingmaps-react';
-import { EditableText, IconButton, Toggle, Type } from 'browndash-components';
-import e from 'connect-flash';
-import { truncateSync } from 'fs';
+import { EditableText, IconButton, Type } from 'browndash-components';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -13,7 +11,7 @@ import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { ScriptField } from '../../../../fields/ScriptField';
 import { NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnIgnore, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
@@ -23,14 +21,12 @@ import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
 import { MarqueeAnnotator } from '../../MarqueeAnnotator';
-import { AnchorMenu } from '../../pdf/AnchorMenu';
 import { Annotation } from '../../pdf/Annotation';
 import { SidebarAnnos } from '../../SidebarAnnos';
 import { DocumentView } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { PinProps, PresBox } from '../trails';
 import './MapBox.scss';
-import { MapBoxInfoWindow } from './MapBoxInfoWindow';
 // amongus
 /**
  * MapBox architecture:
@@ -62,11 +58,11 @@ const mapOptions = {
 
 const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/  and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey>
 
-const script = document.createElement('script');
-script.defer = true;
-script.async = true;
-script.src = `https://maps.googleapis.com/maps/api/js?key=${bingApiKey}&libraries=places,drawing`;
-document.head.appendChild(script);
+// const script = document.createElement('script');
+// script.defer = true;
+// script.async = true;
+// script.src = `https://maps.googleapis.com/maps/api/js?key=${bingApiKey}&libraries=places,drawing`;
+// document.head.appendChild(script);
 
 /**
  * Consider integrating later: allows for drawing, circling, making shapes on map
@@ -84,13 +80,6 @@ document.head.appendChild(script);
 //     },
 // });
 
-// options for searchbox in Google Maps Places Autocomplete API
-const options = {
-    fields: ['formatted_address', 'geometry', 'name'], // note: level of details is charged by item per retrieval, not recommended to return all fields
-    strictBounds: false,
-    types: ['establishment'], // type pf places, subject of change according to user need
-} as google.maps.places.AutocompleteOptions;
-
 @observer
 export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
     private _dropDisposer?: DragManager.DragDropDisposer;
@@ -137,7 +126,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _sidebarRef = React.createRef<SidebarAnnos>();
     private _ref: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposer: { [key: string]: IReactionDisposer } = {};
-    componentDidMount() {}
+    componentDidMount() {
+        this.props.setContentView?.(this);
+    }
 
     componentWillUnmount(): void {
         Object.keys(this._disposer).forEach(key => this._disposer[key]?.());
@@ -149,74 +140,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds()));
     };
 
-    /**
-     * Custom control for add marker button
-     * @param controlDiv
-     * @param map
-     */
-    private CenterControl = () => {
-        const controlDiv = document.createElement('div');
-        controlDiv.className = 'mapBox-addMarker';
-        // Set CSS for the control border.
-        const controlUI = document.createElement('div');
-        controlUI.style.backgroundColor = '#fff';
-        controlUI.style.borderRadius = '3px';
-        controlUI.style.cursor = 'pointer';
-        controlUI.style.marginTop = '10px';
-        controlUI.style.borderRadius = '4px';
-        controlUI.style.marginBottom = '22px';
-        controlUI.style.textAlign = 'center';
-        controlUI.style.position = 'absolute';
-        controlUI.style.width = '32px';
-        controlUI.style.height = '32px';
-        controlUI.title = 'Click to toggle marker mode.  In marker mode, click on map to place a marker.';
-
-        const plIcon = document.createElement('img');
-        plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png';
-        plIcon.style.color = 'rgb(25,25,25)';
-        plIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
-        plIcon.style.fontSize = '16px';
-        plIcon.style.lineHeight = '32px';
-        plIcon.style.left = '18';
-        plIcon.style.top = '15';
-        plIcon.style.position = 'absolute';
-        plIcon.width = 14;
-        plIcon.height = 14;
-        plIcon.innerHTML = 'Add';
-        controlUI.appendChild(plIcon);
-
-        // Set CSS for the control interior.
-        const markerIcon = document.createElement('img');
-        markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png';
-        markerIcon.style.color = 'rgb(25,25,25)';
-        markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
-        markerIcon.style.fontSize = '16px';
-        markerIcon.style.lineHeight = '32px';
-        markerIcon.style.left = '-2';
-        markerIcon.style.top = '1';
-        markerIcon.width = 30;
-        markerIcon.height = 30;
-        markerIcon.style.position = 'absolute';
-        markerIcon.innerHTML = 'Add';
-        controlUI.appendChild(markerIcon);
-
-        // Setup the click event listeners
-        controlUI.addEventListener('click', () => {
-            if (this.toggleAddMarker === true) {
-                this.toggleAddMarker = false;
-                console.log('add marker button status:' + this.toggleAddMarker);
-                controlUI.style.backgroundColor = '#fff';
-                markerIcon.style.color = 'rgb(25,25,25)';
-            } else {
-                this.toggleAddMarker = true;
-                console.log('add marker button status:' + this.toggleAddMarker);
-                controlUI.style.backgroundColor = '#4476f7';
-                markerIcon.style.color = 'rgb(255,255,255)';
-            }
-        });
-        controlDiv.appendChild(controlUI);
-        return controlDiv;
-    };
     /**
      * Load and render all map markers
      * @param marker
@@ -256,47 +179,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     _loadPending = true;
-    /**
-     * store a reference to google map instance
-     * setup the drawing manager on the top right corner of map
-     * fit map bounds to contain all markers
-     * @param map
-     */
-    @action
-    private loadHandler = (map: google.maps.Map) => {
-        this._map = map;
-        this._loadPending = true;
-        const centerControlDiv = this.CenterControl();
-        map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv);
-        //drawingManager.setMap(map);
-        // if (navigator.geolocation) {
-        //     navigator.geolocation.getCurrentPosition(
-        //         (position: Position) => {
-        //             const pos = {
-        //                 lat: position.coords.latitude,
-        //                 lng: position.coords.longitude,
-        //             };
-        //             this._map.setCenter(pos);
-        //         }
-        //     );
-        // } else {
-        //     alert("Your geolocation is not supported by browser.")
-        // };
-        map.setZoom(NumCast(this.dataDoc.mapZoom, 2.5));
-        map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
-        setTimeout(() => {
-            if (this._loadPending && this._map.getBounds()) {
-                this._loadPending = false;
-                this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
-            }
-        }, 250);
-        // listener to addmarker event
-        this._map.addListener('click', (e: MouseEvent) => {
-            if (this.toggleAddMarker === true) {
-                this.placeMarker((e as any).latLng, map);
-            }
-        });
-    };
 
     @action
     centered = () => {
@@ -535,14 +417,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         ));
     };
 
-    // TODO: auto center on select a document in the sidebar
-    private handleMapCenter = (map: google.maps.Map) => {
-        // console.log("print the selected views in selectionManager:")
-        // if (SelectionManager.Views().lastElement()) {
-        //     console.log(SelectionManager.Views().lastElement());
-        // }
-    };
-
     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
@@ -847,7 +721,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change'));
         //this.updateLayout();
         // this.updateMapType();
-        this.props.setContentView?.(this);
         this._disposer.location = reaction(
             () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }),
             locationObject => {
diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
index 6369f9e04..d28209ea1 100644
--- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx
+++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
@@ -5,28 +5,23 @@ import { FieldView, FieldViewProps } from '../FieldView';
 import React = require('react');
 import { computed } from 'mobx';
 import { MapBox } from './MapBox';
-import { undoable } from '../../../util/UndoManager';
-
 
 /**
  * Map Pushpin doc class
  */
 @observer
 export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
-
-
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(MapPushpinBox, fieldKey);
     }
     componentDidMount() {
         // if (this.mapBoxView)
-            this.mapBoxView.addPushpin(this.rootDoc);
+        this.mapBoxView.addPushpin(this.rootDoc);
     }
     componentWillUnmount() {
         // this.mapBoxView.removePushpin(this.rootDoc);
     }
 
-
     @computed get mapBoxView() {
         return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as MapBox;
     }
@@ -35,6 +30,6 @@ export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     render() {
-        return (<div></div>);
+        return <div></div>;
     }
 }
-- 
cgit v1.2.3-70-g09d2


From fbdd45bf7467267a4629a78e4ad87bae10879573 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Aug 2023 12:48:19 -0400
Subject: fixed map pushpins by only rendering them after the map is ready

---
 src/client/views/nodes/MapBox/MapBox.tsx | 53 +++++++++++++++++---------------
 1 file changed, 29 insertions(+), 24 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 860528ec4..453a3d6fc 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -711,7 +711,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Called when BingMap is first rendered
      * Initializes starting values
      */
+    @observable _mapReady = false;
+    @action
     bingMapReady = (map: any) => {
+        this._mapReady = true;
         this._bingMap = map.map;
         if (!this._bingMap.current) {
             alert('NO Map!?');
@@ -844,30 +847,32 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {/* {this.pinIsSelected_TEMPORARY? <input type="button" value="Delete Pin" onClick={this.removeSelectedPin} /> : null} */}
                     <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions}></BingMapsReact>
                     <div>
-                        {this.allMapPushpins.map(pushpin => (
-                            <DocumentView
-                                {...this.props}
-                                Document={pushpin}
-                                DataDoc={undefined}
-                                PanelWidth={returnOne}
-                                PanelHeight={returnOne}
-                                NativeWidth={returnOne}
-                                NativeHeight={returnOne}
-                                onClick={() => new ScriptField(undefined)}
-                                onKey={undefined}
-                                onDoubleClick={undefined}
-                                onBrowseClick={undefined}
-                                childFilters={returnEmptyFilter}
-                                childFiltersByRanges={returnEmptyFilter}
-                                searchFilterDocs={returnEmptyDoclist}
-                                isDocumentActive={returnFalse}
-                                isContentActive={returnFalse}
-                                addDocTab={returnFalse}
-                                ScreenToLocalTransform={() => new Transform(0, 0, 0)}
-                                fitContentsToBox={undefined}
-                                focus={returnOne}
-                            />
-                        ))}
+                        {!this._mapReady
+                            ? null
+                            : this.allMapPushpins.map(pushpin => (
+                                  <DocumentView
+                                      {...this.props}
+                                      Document={pushpin}
+                                      DataDoc={undefined}
+                                      PanelWidth={returnOne}
+                                      PanelHeight={returnOne}
+                                      NativeWidth={returnOne}
+                                      NativeHeight={returnOne}
+                                      onClick={() => new ScriptField(undefined)}
+                                      onKey={undefined}
+                                      onDoubleClick={undefined}
+                                      onBrowseClick={undefined}
+                                      childFilters={returnEmptyFilter}
+                                      childFiltersByRanges={returnEmptyFilter}
+                                      searchFilterDocs={returnEmptyDoclist}
+                                      isDocumentActive={returnFalse}
+                                      isContentActive={returnFalse}
+                                      addDocTab={returnFalse}
+                                      ScreenToLocalTransform={() => new Transform(0, 0, 0)}
+                                      fitContentsToBox={undefined}
+                                      focus={returnOne}
+                                  />
+                              ))}
                     </div>
                     {/* <MapBoxInfoWindow
                                     key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}
-- 
cgit v1.2.3-70-g09d2


From 50fc991461e9fd6f76cb982ebffb79fcb7190881 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Mon, 21 Aug 2023 13:22:22 -0400
Subject: Working on Geireann's component stuff. Now gonna try drag drop pins

---
 src/client/views/nodes/MapBox/MapBox.tsx | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 453a3d6fc..242611135 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,10 +1,11 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { GoogleMapProps, Marker } from '@react-google-maps/api';
 import BingMapsReact from 'bingmaps-react';
-import { EditableText, IconButton, Type } from 'browndash-components';
+import { EditableText, IconButton, Toggle, Type } from 'browndash-components';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { getMenuPlacement } from 'react-select/src/components/Menu';
 import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
 import { Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
@@ -695,7 +696,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Toggles mode for placing a pin down on map
      */
     @observable
-    placePinOn: boolean | undefined;
+    placePinOn = true;
     @action
     togglePlacePin = () => {
         if (this.placePinOn) this.placePinOn = false;
@@ -822,6 +823,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         <label htmlFor="switch" className="toggle">
                             <p>{this.placePinOn ? 'Place Pin ON' : 'Place Pin OFF'}</p>
                         </label>
+                        <Toggle 
+                            toggleStatus = {this.placePinOn}
+                            onClick={this.togglePlacePin}
+                            iconPlacement={"right"}
+                            text="amongus"
+
+                        />
                     </div>
 
                     <div className="mapBox-topbar">
-- 
cgit v1.2.3-70-g09d2


From f573d7f0e93fe4b2b1f64b8591c77a225170847f Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Mon, 21 Aug 2023 14:28:03 -0400
Subject: map

---
 src/client/views/nodes/MapBox/MapBox.tsx | 101 +++++++++++++++----------------
 1 file changed, 49 insertions(+), 52 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 242611135..bd631f942 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -2,6 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { GoogleMapProps, Marker } from '@react-google-maps/api';
 import BingMapsReact from 'bingmaps-react';
 import { EditableText, IconButton, Toggle, Type } from 'browndash-components';
+import { tickStep } from 'd3';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -737,35 +738,38 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             },
             { fireImmediately: true }
         );
-        // this._disposer.pins = reaction(() => ({pinList:this.dataDoc[this.annotationKey]}),
-        // (pinsObject) => {
-        //     this._bingMap.current.entities.clear();
-        //     pinsObject.pinList.map((pushpin: Doc) => (
-        //                         <DocumentView
-        //                             {...this.props}
-        //                             Document={pushpin}
-        //                             DataDoc={undefined}
-        //                             PanelWidth={returnOne}
-        //                             PanelHeight={returnOne}
-        //                             NativeWidth={returnOne}
-        //                             NativeHeight={returnOne}
-        //                             onClick={()=>new ScriptField(undefined)}
-        //                             onKey={undefined}
-        //                             onDoubleClick={undefined}
-        //                             onBrowseClick={undefined}
-        //                             docFilters={returnEmptyDoclist}
-        //                             docRangeFilters={returnEmptyDoclist}
-        //                             searchFilterDocs={returnEmptyDoclist}
-        //                             isDocumentActive={returnFalse}
-        //                             isContentActive={returnFalse}
-        //                             addDocTab={returnFalse}
-        //                             ScreenToLocalTransform={()=>new Transform(0,0,0)}
-        //                             fitContentsToBox={undefined}
-        //                             focus={returnOne}
-        //                 />));
-
-        // }, {fireImmediately: false});
     };
+
+    // Keeps track of when dragging a pin onto map
+    draggingPin = false;
+    dragToggle(){
+        console.log("DRAGGING TOGGLE")
+        document.addEventListener('pointerup', this.dropPin)                   
+    }
+    dropPin = (e:PointerEvent) => {
+        // document.removeChild(ele);
+        
+              
+        let target = document.elementFromPoint(e.x, e.y);  
+
+        while (target != null){
+            if (target === this._ref.current){
+                const location =  this.MicrosoftMaps.tryPixelToLocation(new this.MicrosoftMaps.Point(e.clientX,e.clientY))
+                
+console.log(location)
+                this.createPushpin(location.latitude, location.longitude);
+                
+                
+
+                break;
+            }
+            target = target.parentElement
+        }  
+        document.removeEventListener('pointerup', this.dropPin)
+    }
+
+    
+
     searchbarKeyDown = (e: any) => {
         if (e.key === 'Enter') {
             this.bingSearch();
@@ -799,6 +803,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             // editing
                             setVal={(newText: string | number) => typeof newText === 'string' && this.searchbarOnEdit(newText)}
                             placeholder="Boston"
+                            
                             // text="text"
                         />
                         <IconButton
@@ -823,37 +828,29 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         <label htmlFor="switch" className="toggle">
                             <p>{this.placePinOn ? 'Place Pin ON' : 'Place Pin OFF'}</p>
                         </label>
-                        <Toggle 
+                        <div  draggable={true} onPointerDown={this.dragToggle}
+                        > 
+                            <FontAwesomeIcon className="right" icon="angle-double-left" size="lg" />
+                        {/* <Toggle 
                             toggleStatus = {this.placePinOn}
                             onClick={this.togglePlacePin}
                             iconPlacement={"right"}
-                            text="amongus"
-
-                        />
+                            text="amongus" /> */}
+                        </div>
                     </div>
 
-                    <div className="mapBox-topbar">
-                        {/* <input type="button" value="Search" onClick={this.bingSearch} /> */}
-
-                        {/* {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />} */}
-                        {/* {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />}
 
-                        <Toggle
-                            onClick={this.togglePlacePin}
-                            onPointerDown={function noRefCheck(){}}
-                            onPointerDownCapture={function noRefCheck(){}}
-                            onPointerMove={function noRefCheck(){}}
-                            onPointerMoveCapture={function noRefCheck(){}}
-                            onPointerUp={function noRefCheck(){}}
-                            text="Place Pin"
-                            toggleType = "checkbox"
-                            toggleStatus= {false}
-                            type="secondary"
-                        /> */}
-                    </div>
 
-                    {/* {this.pinIsSelected_TEMPORARY? <input type="button" value="Delete Pin" onClick={this.removeSelectedPin} /> : null} */}
-                    <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions}></BingMapsReact>
+                    <BingMapsReact 
+                        onMapReady={this.bingMapReady} 
+                        bingMapsKey={bingApiKey} 
+                        height="100%" 
+                        mapOptions={this.bingMapOptions} 
+                        width="100%" 
+                        viewOptions={this.bingViewOptions}
+                        onPointerUp{...()=>this.draggingPin=false}
+                        
+                        ></BingMapsReact>
                     <div>
                         {!this._mapReady
                             ? null
-- 
cgit v1.2.3-70-g09d2


From cba0d4b26e8e5daf274c7a8ccfd130f284ce2946 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Mon, 21 Aug 2023 14:46:05 -0400
Subject: new push

---
 src/client/views/nodes/MapBox/MapBox.tsx | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index bd631f942..58a43c55a 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -742,11 +742,17 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     // Keeps track of when dragging a pin onto map
     draggingPin = false;
-    dragToggle(){
+    dragToggle = (e:React.PointerEvent) => {
         console.log("DRAGGING TOGGLE")
-        document.addEventListener('pointerup', this.dropPin)                   
+        document.addEventListener('drop', this.dropPin, true) 
+        document.addEventListener('pointermove', this.pinMove, true)
+        e.stopPropagation();                  
     }
-    dropPin = (e:PointerEvent) => {
+    pinMove = (e:PointerEvent) => {
+        console.log("MOVING");
+        e.stopPropagation();
+    }
+    dropPin = (e:DragEvent) => {
         // document.removeChild(ele);
         
               
@@ -759,13 +765,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 console.log(location)
                 this.createPushpin(location.latitude, location.longitude);
                 
-                
+                e.stopPropagation();
 
                 break;
             }
             target = target.parentElement
         }  
-        document.removeEventListener('pointerup', this.dropPin)
+        document.removeEventListener('drop', this.dropPin)
     }
 
     
-- 
cgit v1.2.3-70-g09d2


From 1f7cf33fef75001ee10983a8269ddb4c45d3a07d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Aug 2023 15:39:49 -0400
Subject: map fixes for drag and drop

---
 src/client/views/nodes/MapBox/MapBox.scss |   9 +--
 src/client/views/nodes/MapBox/MapBox.tsx  | 107 +++++++++++++-----------------
 2 files changed, 50 insertions(+), 66 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index 39fa3262e..a910f294e 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -11,12 +11,13 @@
         padding: 12;
         font-size: 17;
     }
-    .mapBox-searchbar{
-        display:flex;
+    .mapBox-searchbar {
+        display: flex;
         flex-direction: row;
+        width: calc(100% - 40px);
     }
-    .mapBox-topbar{
-        display:flex;
+    .mapBox-topbar {
+        display: flex;
         flex-direction: row;
     }
 
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 58a43c55a..0eafe4312 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,7 +1,8 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@mui/material';
 import { GoogleMapProps, Marker } from '@react-google-maps/api';
 import BingMapsReact from 'bingmaps-react';
-import { EditableText, IconButton, Toggle, Type } from 'browndash-components';
+import { Button, EditableText, IconButton, Toggle, Type } from 'browndash-components';
 import { tickStep } from 'd3';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -697,7 +698,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Toggles mode for placing a pin down on map
      */
     @observable
-    placePinOn = true;
+    placePinOn = false;
     @action
     togglePlacePin = () => {
         if (this.placePinOn) this.placePinOn = false;
@@ -742,39 +743,35 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     // Keeps track of when dragging a pin onto map
     draggingPin = false;
-    dragToggle = (e:React.PointerEvent) => {
-        console.log("DRAGGING TOGGLE")
-        document.addEventListener('drop', this.dropPin, true) 
-        document.addEventListener('pointermove', this.pinMove, true)
-        e.stopPropagation();                  
-    }
-    pinMove = (e:PointerEvent) => {
-        console.log("MOVING");
+    dragToggle = (e: React.PointerEvent) => {
+        console.log('DRAGGING TOGGLE');
+        document.addEventListener('drop', this.dropPin, true);
+        document.addEventListener('pointermove', this.pinMove, true);
         e.stopPropagation();
-    }
-    dropPin = (e:DragEvent) => {
-        // document.removeChild(ele);
-        
-              
-        let target = document.elementFromPoint(e.x, e.y);  
-
-        while (target != null){
-            if (target === this._ref.current){
-                const location =  this.MicrosoftMaps.tryPixelToLocation(new this.MicrosoftMaps.Point(e.clientX,e.clientY))
-                
-console.log(location)
+    };
+    pinMove = (e: PointerEvent) => {
+        console.log('MOVING');
+        e.stopPropagation();
+    };
+    dropPin = (e: DragEvent) => {
+        e.stopPropagation();
+        e.preventDefault();
+        document.removeEventListener('drop', this.dropPin, true);
+        document.removeEventListener('pointermove', this.pinMove, true);
+        let target = document.elementFromPoint(e.x, e.y);
+
+        while (target != null) {
+            if (target === this._ref.current) {
+                const cpt = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
+                const x = cpt[0] - this.props.PanelWidth() / 2;
+                const y = cpt[1] - 32 - this.props.PanelHeight() / 2;
+                const location = this._bingMap.current.tryPixelToLocation(new this.MicrosoftMaps.Point(x, y));
                 this.createPushpin(location.latitude, location.longitude);
-                
-                e.stopPropagation();
-
                 break;
             }
-            target = target.parentElement
-        }  
-        document.removeEventListener('drop', this.dropPin)
-    }
-
-    
+            target = target.parentElement;
+        }
+    };
 
     searchbarKeyDown = (e: any) => {
         if (e.key === 'Enter') {
@@ -805,13 +802,13 @@ console.log(location)
                     {this.annotationLayer}
 
                     <div className="mapBox-searchbar">
-                        <EditableText
-                            // editing
-                            setVal={(newText: string | number) => typeof newText === 'string' && this.searchbarOnEdit(newText)}
-                            placeholder="Boston"
-                            
-                            // text="text"
-                        />
+                        <div style={{ width: 'calc(100% - 40px)' }}>
+                            <EditableText
+                                // editing
+                                setVal={(newText: string | number) => typeof newText === 'string' && this.searchbarOnEdit(newText)}
+                                placeholder="Boston"
+                            />
+                        </div>
                         <IconButton
                             icon={
                                 <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="magnifying-glass" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" color="#DFDFDF">
@@ -829,34 +826,20 @@ console.log(location)
                             onPointerUp={function noRefCheck() {}}
                             type={Type.TERT}
                         />
-                        <input type="checkbox" id="switch" className="checkbox" checked={this.placePinOn} onChange={this.togglePlacePin} />
-
-                        <label htmlFor="switch" className="toggle">
-                            <p>{this.placePinOn ? 'Place Pin ON' : 'Place Pin OFF'}</p>
-                        </label>
-                        <div  draggable={true} onPointerDown={this.dragToggle}
-                        > 
-                            <FontAwesomeIcon className="right" icon="angle-double-left" size="lg" />
-                        {/* <Toggle 
-                            toggleStatus = {this.placePinOn}
-                            onClick={this.togglePlacePin}
-                            iconPlacement={"right"}
-                            text="amongus" /> */}
+                        <div draggable={true} style={{ width: 30, height: 30 }} onPointerDown={this.dragToggle}>
+                            <Button tooltip="drag to place a pushpin" icon={<FontAwesomeIcon size={'lg'} icon={'bullseye'} />} />
                         </div>
                     </div>
 
-
-
-                    <BingMapsReact 
-                        onMapReady={this.bingMapReady} 
-                        bingMapsKey={bingApiKey} 
-                        height="100%" 
-                        mapOptions={this.bingMapOptions} 
-                        width="100%" 
+                    <BingMapsReact
+                        onMapReady={this.bingMapReady}
+                        bingMapsKey={bingApiKey}
+                        height="100%"
+                        mapOptions={this.bingMapOptions}
+                        width="100%"
                         viewOptions={this.bingViewOptions}
-                        onPointerUp{...()=>this.draggingPin=false}
-                        
-                        ></BingMapsReact>
+                        onPointerUp
+                        {...() => (this.draggingPin = false)}></BingMapsReact>
                     <div>
                         {!this._mapReady
                             ? null
-- 
cgit v1.2.3-70-g09d2


From 371a56fd7c6d31545af4798fd256b5a5bb078e55 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Aug 2023 15:41:00 -0400
Subject: form last

---
 src/client/views/nodes/MapBox/MapBox.tsx | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 0eafe4312..568cc275a 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,13 +1,10 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@mui/material';
 import { GoogleMapProps, Marker } from '@react-google-maps/api';
 import BingMapsReact from 'bingmaps-react';
-import { Button, EditableText, IconButton, Toggle, Type } from 'browndash-components';
-import { tickStep } from 'd3';
+import { Button, EditableText, IconButton, Type } from 'browndash-components';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { getMenuPlacement } from 'react-select/src/components/Menu';
 import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
 import { Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
-- 
cgit v1.2.3-70-g09d2


From 775f55b2d013608d3c53166d36ec927e08c3dcef Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Aug 2023 15:45:09 -0400
Subject: fixed alignemnt of map search

---
 src/client/views/nodes/MapBox/MapBox.scss | 4 +++-
 src/client/views/nodes/MapBox/MapBox.tsx  | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index a910f294e..7e96d2d4e 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -14,7 +14,9 @@
     .mapBox-searchbar {
         display: flex;
         flex-direction: row;
-        width: calc(100% - 40px);
+        .editableText-container {
+            width: 100%;
+        }
     }
     .mapBox-topbar {
         display: flex;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 568cc275a..f6f0d4402 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -804,6 +804,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 // editing
                                 setVal={(newText: string | number) => typeof newText === 'string' && this.searchbarOnEdit(newText)}
                                 placeholder="Boston"
+                                textAlign="center"
                             />
                         </div>
                         <IconButton
-- 
cgit v1.2.3-70-g09d2


From 9505f9d488fb7850a6cb95cab5695b474a271fae Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Aug 2023 16:05:14 -0400
Subject: added Enter event for editableText

---
 package-lock.json                        | 134 +++++++++----------------------
 package.json                             |   2 +-
 src/client/views/nodes/MapBox/MapBox.tsx |   1 +
 3 files changed, 38 insertions(+), 99 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/package-lock.json b/package-lock.json
index e6985cf65..11e7e7db9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6507,9 +6507,9 @@
             }
         },
         "browndash-components": {
-            "version": "0.0.97",
-            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.97.tgz",
-            "integrity": "sha512-UO8NQrOJoAq+UQGR8TXCRzr6jX9qSnEot9FHP7zdwlKH1sGntbQpyM5BXdwfkyXo+oh1qstTGkyR4s20CY7Yrw==",
+            "version": "0.0.98",
+            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.98.tgz",
+            "integrity": "sha512-g69eW7TJGYo7w3foy68ebZ3A9+2vk2MExIEIV1pH/rWupm6nriZaP64tOOnmOd96oD8Efrb0KT5D3xQeVlfnjw==",
             "requires": {
                 "@emotion/react": "^11.11.1",
                 "@emotion/styled": "^11.11.0",
@@ -6911,9 +6911,9 @@
                     }
                 },
                 "fsevents": {
-                    "version": "2.3.2",
-                    "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
-                    "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+                    "version": "2.3.3",
+                    "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+                    "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
                     "optional": true
                 },
                 "glob-parent": {
@@ -7063,97 +7063,12 @@
                                         "strip-ansi": "^7.0.1"
                                     }
                                 },
-                                "string-width-cjs": {
-                                    "version": "npm:string-width@4.2.3",
-                                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-                                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-                                    "requires": {
-                                        "emoji-regex": "^8.0.0",
-                                        "is-fullwidth-code-point": "^3.0.0",
-                                        "strip-ansi": "^6.0.1"
-                                    },
-                                    "dependencies": {
-                                        "ansi-regex": {
-                                            "version": "5.0.1",
-                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
-                                        },
-                                        "emoji-regex": {
-                                            "version": "8.0.0",
-                                            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-                                            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
-                                        },
-                                        "strip-ansi": {
-                                            "version": "6.0.1",
-                                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-                                            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-                                            "requires": {
-                                                "ansi-regex": "^5.0.1"
-                                            }
-                                        }
-                                    }
-                                },
                                 "strip-ansi": {
                                     "version": "7.1.0",
                                     "bundled": true,
                                     "requires": {
                                         "ansi-regex": "^6.0.1"
                                     }
-                                },
-                                "strip-ansi-cjs": {
-                                    "version": "npm:strip-ansi@6.0.1",
-                                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-                                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-                                    "requires": {
-                                        "ansi-regex": "^5.0.1"
-                                    },
-                                    "dependencies": {
-                                        "ansi-regex": {
-                                            "version": "5.0.1",
-                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
-                                        }
-                                    }
-                                },
-                                "wrap-ansi-cjs": {
-                                    "version": "npm:wrap-ansi@7.0.0",
-                                    "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-                                    "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-                                    "requires": {
-                                        "ansi-styles": "^4.0.0",
-                                        "string-width": "^4.1.0",
-                                        "strip-ansi": "^6.0.0"
-                                    },
-                                    "dependencies": {
-                                        "ansi-regex": {
-                                            "version": "5.0.1",
-                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
-                                        },
-                                        "emoji-regex": {
-                                            "version": "8.0.0",
-                                            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-                                            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
-                                        },
-                                        "string-width": {
-                                            "version": "4.2.3",
-                                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-                                            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-                                            "requires": {
-                                                "emoji-regex": "^8.0.0",
-                                                "is-fullwidth-code-point": "^3.0.0",
-                                                "strip-ansi": "^6.0.1"
-                                            }
-                                        },
-                                        "strip-ansi": {
-                                            "version": "6.0.1",
-                                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-                                            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-                                            "requires": {
-                                                "ansi-regex": "^5.0.1"
-                                            }
-                                        }
-                                    }
                                 }
                             }
                         },
@@ -8658,7 +8573,7 @@
                             }
                         },
                         "string-width-cjs": {
-                            "version": "npm:string-width-cjs@4.2.3",
+                            "version": "npm:string-width@4.2.3",
                             "bundled": true,
                             "requires": {
                                 "emoji-regex": "^8.0.0",
@@ -8681,7 +8596,7 @@
                             }
                         },
                         "strip-ansi-cjs": {
-                            "version": "npm:strip-ansi-cjs@6.0.1",
+                            "version": "npm:strip-ansi@6.0.1",
                             "bundled": true,
                             "requires": {
                                 "ansi-regex": "^5.0.1"
@@ -8840,7 +8755,7 @@
                             }
                         },
                         "wrap-ansi-cjs": {
-                            "version": "npm:wrap-ansi-cjs@7.0.0",
+                            "version": "npm:wrap-ansi@7.0.0",
                             "bundled": true,
                             "requires": {
                                 "ansi-styles": "^4.0.0",
@@ -10031,11 +9946,34 @@
             "integrity": "sha512-GiZn9D4Z/rSYvTeg1ljAIsEqFm0LaN9gVtwDCrKL80zHtS31p9BAjmTxVqTQDMpwlMolJZOFntUG2uwyj7DAqw=="
         },
         "core-js-compat": {
-            "version": "3.32.0",
-            "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.32.0.tgz",
-            "integrity": "sha512-7a9a3D1k4UCVKnLhrgALyFcP7YCsLOQIxPd0dKjf/6GuPcgyiGP70ewWdCGrSK7evyhymi0qO4EqCmSJofDeYw==",
+            "version": "3.32.1",
+            "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.32.1.tgz",
+            "integrity": "sha512-GSvKDv4wE0bPnQtjklV101juQ85g6H3rm5PDP20mqlS5j0kXF3pP97YvAu5hl+uFHqMictp3b2VxOHljWMAtuA==",
             "requires": {
-                "browserslist": "^4.21.9"
+                "browserslist": "^4.21.10"
+            },
+            "dependencies": {
+                "browserslist": {
+                    "version": "4.21.10",
+                    "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz",
+                    "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==",
+                    "requires": {
+                        "caniuse-lite": "^1.0.30001517",
+                        "electron-to-chromium": "^1.4.477",
+                        "node-releases": "^2.0.13",
+                        "update-browserslist-db": "^1.0.11"
+                    }
+                },
+                "caniuse-lite": {
+                    "version": "1.0.30001522",
+                    "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz",
+                    "integrity": "sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg=="
+                },
+                "electron-to-chromium": {
+                    "version": "1.4.497",
+                    "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.497.tgz",
+                    "integrity": "sha512-9cvj6XkrgyxDySKJWYVIyq7p9bOAkH3M3jANgvWNX/F2jIAfbBN4oBNLJg1i68I8wAKVuih2IL4y1n9hqbL3Aw=="
+                }
             }
         },
         "core-js-pure": {
diff --git a/package.json b/package.json
index ea4e6a5e6..4851cc87a 100644
--- a/package.json
+++ b/package.json
@@ -178,7 +178,7 @@
         "body-parser": "^1.19.2",
         "bootstrap": "^4.6.1",
         "brotli": "^1.3.3",
-        "browndash-components": "^0.0.97",
+        "browndash-components": "^0.0.98",
         "browser-assert": "^1.2.1",
         "bson": "^4.6.1",
         "canvas": "^2.9.3",
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index f6f0d4402..f7f046e9a 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -803,6 +803,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             <EditableText
                                 // editing
                                 setVal={(newText: string | number) => typeof newText === 'string' && this.searchbarOnEdit(newText)}
+                                onEnter={e => this.bingSearch()}
                                 placeholder="Boston"
                                 textAlign="center"
                             />
-- 
cgit v1.2.3-70-g09d2


From 04c05a0e0c1c84575ea064303a083bb011ddd270 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Aug 2023 16:09:33 -0400
Subject: fixed input placement in map search

---
 src/client/views/nodes/MapBox/MapBox.scss | 3 +++
 src/client/views/nodes/MapBox/MapBox.tsx  | 4 ++--
 2 files changed, 5 insertions(+), 2 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index 7e96d2d4e..b704b26a2 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -17,6 +17,9 @@
         .editableText-container {
             width: 100%;
         }
+        input {
+            width: 100%;
+        }
     }
     .mapBox-topbar {
         display: flex;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index f7f046e9a..91333845a 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -471,7 +471,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      *
      **/
     @observable
-    bingSearchBarContents: any = 'Boston, MA'; // For Bing Maps: The contents of the Bing search bar (string)
+    bingSearchBarContents: any = 'enter city/zip/...'; // For Bing Maps: The contents of the Bing search bar (string)
 
     geoDataRequestOptions = {
         entityType: 'PopulatedPlace',
@@ -804,7 +804,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 // editing
                                 setVal={(newText: string | number) => typeof newText === 'string' && this.searchbarOnEdit(newText)}
                                 onEnter={e => this.bingSearch()}
-                                placeholder="Boston"
+                                placeholder={this.bingSearchBarContents}
                                 textAlign="center"
                             />
                         </div>
-- 
cgit v1.2.3-70-g09d2


From cb03770340e81986dd2c9a0f99f60588c1207745 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Mon, 21 Aug 2023 16:15:18 -0400
Subject: layout of search bar

---
 src/client/views/nodes/MapBox/MapBox.scss | 1 +
 src/client/views/nodes/MapBox/MapBox.tsx  | 2 --
 2 files changed, 1 insertion(+), 2 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index b704b26a2..464646a23 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -14,6 +14,7 @@
     .mapBox-searchbar {
         display: flex;
         flex-direction: row;
+        width: calc(100% - 40px);
         .editableText-container {
             width: 100%;
         }
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 91333845a..bd6adaecb 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -799,7 +799,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {this.annotationLayer}
 
                     <div className="mapBox-searchbar">
-                        <div style={{ width: 'calc(100% - 40px)' }}>
                             <EditableText
                                 // editing
                                 setVal={(newText: string | number) => typeof newText === 'string' && this.searchbarOnEdit(newText)}
@@ -807,7 +806,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 placeholder={this.bingSearchBarContents}
                                 textAlign="center"
                             />
-                        </div>
                         <IconButton
                             icon={
                                 <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="magnifying-glass" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" color="#DFDFDF">
-- 
cgit v1.2.3-70-g09d2


From d0f5cebc147b48b14e8c5992e459c725aee63cb5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Aug 2023 16:33:02 -0400
Subject: start of adding sidebar annos

---
 src/client/views/nodes/MapBox/MapBox.tsx | 27 +++++++++++++++++----------
 1 file changed, 17 insertions(+), 10 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index bd6adaecb..e3a7e8a64 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -23,7 +23,7 @@ import { Colors } from '../../global/globalEnums';
 import { MarqueeAnnotator } from '../../MarqueeAnnotator';
 import { Annotation } from '../../pdf/Annotation';
 import { SidebarAnnos } from '../../SidebarAnnos';
-import { DocumentView } from '../DocumentView';
+import { DocumentView, OpenWhere } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { PinProps, PresBox } from '../trails';
 import './MapBox.scss';
@@ -351,6 +351,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
     };
 
+    OnClick = () => {
+        !this.layoutDoc.layout_showSidebar && this.toggleSidebar();
+        setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false))); // give time for sidebarRef to be created
+    };
+
     sidebarDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
     };
@@ -515,8 +520,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // creates button onclick removes the doc from annotations
 
         // pan to pushpin location
-        this.dataDoc.latitude = pinDoc.lat;
-        this.dataDoc.longitude = pinDoc.lng;
+        this.dataDoc.latitude = pinDoc.latitude;
+        this.dataDoc.longitude = pinDoc.longitude;
+
+        this.OnClick();
     };
 
     /**
@@ -799,13 +806,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {this.annotationLayer}
 
                     <div className="mapBox-searchbar">
-                            <EditableText
-                                // editing
-                                setVal={(newText: string | number) => typeof newText === 'string' && this.searchbarOnEdit(newText)}
-                                onEnter={e => this.bingSearch()}
-                                placeholder={this.bingSearchBarContents}
-                                textAlign="center"
-                            />
+                        <EditableText
+                            // editing
+                            setVal={(newText: string | number) => typeof newText === 'string' && this.searchbarOnEdit(newText)}
+                            onEnter={e => this.bingSearch()}
+                            placeholder={this.bingSearchBarContents}
+                            textAlign="center"
+                        />
                         <IconButton
                             icon={
                                 <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="magnifying-glass" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" color="#DFDFDF">
-- 
cgit v1.2.3-70-g09d2


From 67da701d844b2b2f086b635d6383debeab78c2bf Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Aug 2023 16:42:17 -0400
Subject: comments

---
 src/client/views/nodes/MapBox/MapBox.tsx | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index e3a7e8a64..329de5792 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -523,6 +523,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.dataDoc.latitude = pinDoc.latitude;
         this.dataDoc.longitude = pinDoc.longitude;
 
+        /// this should SELECT, not center
+
         this.OnClick();
     };
 
@@ -614,6 +616,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Returns doc w/ relevant info
      */
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
+        /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
         const anchor = Docs.Create.MapanchorDocument({
             title: 'MapAnchor:' + this.rootDoc.title,
             config_latitude: NumCast(this.dataDoc.latitude),
-- 
cgit v1.2.3-70-g09d2


From bdb78d5f0bd0e9e2ee9f46119b9b42735e9dc8a7 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Mon, 21 Aug 2023 16:50:02 -0400
Subject: Pre-anchor menu work

---
 src/client/views/nodes/MapBox/MapBox.tsx | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 329de5792..be2defa9b 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -351,7 +351,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
     };
 
-    OnClick = () => {
+    createNoteAnnotation = () => {
         !this.layoutDoc.layout_showSidebar && this.toggleSidebar();
         setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false))); // give time for sidebarRef to be created
     };
@@ -510,6 +510,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (pinDoc) this.removePushpin(pinDoc, pin);
         else this._bingMap.current.entities.remove(pin);
     };
+
+    // The pin that is selected
+    selectedPin:Doc | undefined;
+
     /*
      * Pushpin onclick
      */
@@ -520,12 +524,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // creates button onclick removes the doc from annotations
 
         // pan to pushpin location
-        this.dataDoc.latitude = pinDoc.latitude;
-        this.dataDoc.longitude = pinDoc.longitude;
+        // this.dataDoc.latitude = pinDoc.latitude;
+        // this.dataDoc.longitude = pinDoc.longitude;
+        this.selectedPin = pinDoc
 
         /// this should SELECT, not center
 
-        this.OnClick();
+        this.createNoteAnnotation();
     };
 
     /**
@@ -619,8 +624,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
         const anchor = Docs.Create.MapanchorDocument({
             title: 'MapAnchor:' + this.rootDoc.title,
-            config_latitude: NumCast(this.dataDoc.latitude),
-            config_longitude: NumCast(this.dataDoc.longitude),
+            config_latitude: NumCast(this.selectedPin ? this.selectedPin.latitude : this.dataDoc.latitude),
+            config_longitude: NumCast(this.selectedPin ? this.selectedPin.longitude : this.dataDoc.longitude),
             config_mapZoom: NumCast(this.dataDoc.mapZoom),
             config_mapType: StrCast(this.dataDoc.mapType),
             // preslocationToLookAt:this.dataDoc.locationToLookAt,
-- 
cgit v1.2.3-70-g09d2


From e724cf830b773d22bada7c319f5c2527b2ffeae7 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Tue, 22 Aug 2023 16:31:00 -0400
Subject: Everything working, except for offset of placing pins and
 mapanchormenu when sidebar is open

---
 src/client/util/CurrentUserUtils.ts             |   2 +-
 src/client/views/MainView.tsx                   |   2 +
 src/client/views/nodes/MapBox/MapAnchorMenu.tsx | 355 ++++--------------------
 src/client/views/nodes/MapBox/MapBox.tsx        | 162 +++++++----
 src/client/views/nodes/MapBox/MapPushpinBox.tsx |   2 +-
 5 files changed, 168 insertions(+), 355 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 25c8f511b..b63d501de 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -272,7 +272,7 @@ export class CurrentUserUtils {
             {key: "Webpage",    creator: opts => Docs.Create.WebDocument("",opts),          opts: { _width: 400, _height: 512, _nativeWidth: 850, data_useCors: true, }},
             {key: "Comparison", creator: Docs.Create.ComparisonDocument,                    opts: { _width: 300, _height: 300 }},
             {key: "Audio",      creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, }},
-            {key: "Map",        creator: opts => Docs.Create.MapDocument([], opts),         opts: { _width: 800, _height: 600, _layout_fitWidth: true, _layout_showSidebar: true,  }},
+            {key: "Map",        creator: opts => Docs.Create.MapDocument([], opts),         opts: { _width: 800, _height: 600, _layout_fitWidth: true, }},
             {key: "Screengrab", creator: Docs.Create.ScreenshotDocument,                    opts: { _width: 400, _height: 200 }},
             {key: "WebCam",     creator: opts => Docs.Create.WebCamDocument("", opts),      opts: { _width: 400, _height: 200, recording:true, isSystem: true, cloneFieldFilter: new List<string>(["isSystem"]) }},
             {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10}, scripts: {onClick: FollowLinkScript()?.script.originalScript ?? ""}},
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index e376c4fdf..b088157e5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -57,6 +57,7 @@ import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
 import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
 import { LinkDocPreview } from './nodes/LinkDocPreview';
+import { MapAnchorMenu } from './nodes/MapBox/MapAnchorMenu';
 import { RadialMenu } from './nodes/RadialMenu';
 import { TaskCompletionBox } from './nodes/TaskCompletedBox';
 import { OverlayView } from './OverlayView';
@@ -1004,6 +1005,7 @@ export class MainView extends React.Component {
                 <ContextMenu />
                 <RadialMenu />
                 <AnchorMenu />
+                <MapAnchorMenu/>
                 <DashFieldViewMenu />
                 <MarqueeOptionsMenu />
                 <OverlayView />
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index 798905bcd..439c1f14f 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -3,18 +3,18 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState } from 'react-color';
-import { Doc, Opt } from '../../../fields/Doc';
-import { returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from '../../../Utils';
-import { SelectionManager } from '../../util/SelectionManager';
-import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu"
-import { LinkPopup } from '../linking/LinkPopup';
-import { gptAPICall, GPTCallType } from '../../apis/gpt/GPT';
-import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup';
+import { Doc, Opt } from '../../../../fields/Doc';
+import { returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from '../../../../Utils';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu"
+import { LinkPopup } from '../../linking/LinkPopup';
+import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT';
+// import { GPTPopup, GPTPopupMode } from './../../GPTPopup/GPTPopup';
 import { EditorView } from 'prosemirror-view';
 import './MapAnchorMenu.scss';
 import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
-import { StrCast } from '../../../fields/Types';
-import { DocumentType } from '../../documents/DocumentTypes';
+import { StrCast } from '../../../../fields/Types';
+import { DocumentType } from '../../../documents/DocumentTypes';
 
 @observer
 export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -24,73 +24,22 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     private _disposer2: IReactionDisposer | undefined;
     private _commentCont = React.createRef<HTMLButtonElement>();
 
-    @observable private highlightColor: string = 'rgba(245, 230, 95, 0.616)';
 
-    @observable public Status: 'marquee' | 'annotation' | '' = '';
 
-    // GPT additions
-    @observable private GPTpopupText: string = '';
-    @observable private loadingGPT: boolean = false;
-    @observable private showGPTPopup: boolean = false;
-    @observable private GPTMode: GPTPopupMode = GPTPopupMode.SUMMARY;
-    @observable private selectedText: string = '';
-    @observable private editorView?: EditorView;
-    @observable private textDoc?: Doc;
-    @observable private highlightRange: number[] | undefined;
-    private selectionRange: number[] | undefined;
-
-    @action
-    setGPTPopupVis = (vis: boolean) => {
-        this.showGPTPopup = vis;
-    };
-    @action
-    setGPTMode = (mode: GPTPopupMode) => {
-        this.GPTMode = mode;
-    };
-
-    @action
-    setGPTPopupText = (txt: string) => {
-        this.GPTpopupText = txt;
-    };
-
-    @action
-    setLoading = (loading: boolean) => {
-        this.loadingGPT = loading;
-    };
-
-    @action
-    setHighlightRange(r: number[] | undefined) {
-        this.highlightRange = r;
-    }
-
-    @action
-    public setSelectedText = (txt: string) => {
-        this.selectedText = txt;
-    };
-
-    @action
-    public setEditorView = (editor: EditorView) => {
-        this.editorView = editor;
-    };
-
-    @action
-    public setTextDoc = (textDoc: Doc) => {
-        this.textDoc = textDoc;
-    };
 
     public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search
 
-    public OnCrop: (e: PointerEvent) => void = unimplementedFunction;
-    public OnClick: (e: PointerEvent) => void = unimplementedFunction;
-    public OnAudio: (e: PointerEvent) => void = unimplementedFunction;
-    public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
-    public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
+    public Center: () => void = unimplementedFunction;
+    // public OnClick: (e: PointerEvent) => void = unimplementedFunction;
+    // public OnAudio: (e: PointerEvent) => void = unimplementedFunction;
+    // public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
+    // public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
     public Highlight: (color: string, isTargetToggler: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => Opt<Doc> = (color: string, isTargetToggler: boolean) => undefined;
     public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => undefined;
     public Delete: () => void = unimplementedFunction;
-    public PinToPres: () => void = unimplementedFunction;
-    public MakeTargetToggle: () => void = unimplementedFunction;
-    public ShowTargetTrail: () => void = unimplementedFunction;
+    public LinkNote: () => void = unimplementedFunction;
+    // public MakeTargetToggle: () => void = unimplementedFunction;
+    // public ShowTargetTrail: () => void = unimplementedFunction;
     public IsTargetToggler: () => boolean = returnFalse;
     public get Active() {
         return this._left > 0;
@@ -113,8 +62,6 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             () => this._opacity,
             opacity => {
                 if (!opacity) {
-                    this.setGPTPopupVis(false);
-                    this.setGPTPopupText('');
                 }
             },
             { fireImmediately: true }
@@ -122,259 +69,62 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         this._disposer = reaction(
             () => SelectionManager.Views().slice(),
             selected => {
-                this.setGPTPopupVis(false);
-                this.setGPTPopupText('');
                 MapAnchorMenu.Instance.fadeOut(true);
             }
         );
     }
+    // audioDown = (e: React.PointerEvent) => {
+    //     setupMoveUpEvents(this, e, returnFalse, returnFalse, e => this.OnAudio?.(e));
+    // };
+
+    // cropDown = (e: React.PointerEvent) => {
+    //     setupMoveUpEvents(
+    //         this,
+    //         e,
+    //         (e: PointerEvent) => {
+    //             this.StartCropDrag(e, this._commentCont.current!);
+    //             return true;
+    //         },
+    //         returnFalse,
+    //         e => this.OnCrop?.(e)
+    //     );
+    // };
+
+
+    static top = React.createRef<HTMLDivElement>();
+    // public get Top(){
+    //     return this.top
+    // }
 
-    /**
-     * Invokes the API with the selected text and stores it in the summarized text.
-     * @param e pointer down event
-     */
-    gptSummarize = async (e: React.PointerEvent) => {
-        this.setHighlightRange(undefined);
-        this.setGPTPopupVis(true);
-        this.setGPTMode(GPTPopupMode.SUMMARY);
-        this.setLoading(true);
-
-        try {
-            const res = await gptAPICall(this.selectedText, GPTCallType.SUMMARY);
-            if (res) {
-                this.setGPTPopupText(res);
-            } else {
-                this.setGPTPopupText('Something went wrong.');
-            }
-        } catch (err) {
-            console.error(err);
-        }
-
-        this.setLoading(false);
-    };
-
-    /**
-     * Makes a GPT call to edit selected text.
-     * @returns nothing
-     */
-    gptEdit = async () => {
-        if (!this.editorView) return;
-        this.setHighlightRange(undefined);
-        const state = this.editorView.state;
-        const sel = state.selection;
-        const fullText = state.doc.textBetween(0, this.editorView.state.doc.content.size, ' \n');
-        const selectedText = state.doc.textBetween(sel.from, sel.to);
-
-        this.setGPTPopupVis(true);
-        this.setGPTMode(GPTPopupMode.EDIT);
-        this.setLoading(true);
-
-        try {
-            let res = await gptAPICall(selectedText, GPTCallType.EDIT);
-            // let res = await this.mockGPTCall();
-            if (!res) return;
-            res = res.trim();
-            const resultText = fullText.slice(0, sel.from - 1) + res + fullText.slice(sel.to - 1);
-
-            if (res) {
-                this.setGPTPopupText(resultText);
-                this.setHighlightRange([sel.from - 1, sel.from - 1 + res.length]);
-            } else {
-                this.setGPTPopupText('Something went wrong.');
-            }
-        } catch (err) {
-            console.error(err);
-        }
-
-        this.setLoading(false);
-    };
-
-    /**
-     * Replaces text suggestions from GPT.
-     */
-    replaceText = (replacement: string) => {
-        if (!this.editorView || !this.textDoc) return;
-        this.textDoc.text = replacement;
-    };
-
-    pointerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(
-            this,
-            e,
-            (e: PointerEvent) => {
-                this.StartDrag(e, this._commentCont.current!);
-                return true;
-            },
-            returnFalse,
-            e => this.OnClick?.(e)
-        );
-    };
-
-    audioDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, returnFalse, e => this.OnAudio?.(e));
-    };
-
-    cropDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(
-            this,
-            e,
-            (e: PointerEvent) => {
-                this.StartCropDrag(e, this._commentCont.current!);
-                return true;
-            },
-            returnFalse,
-            e => this.OnCrop?.(e)
-        );
-    };
-
-    @action
-    highlightClicked = (e: React.MouseEvent) => {
-        this.Highlight(this.highlightColor, false, undefined, true);
-        MapAnchorMenu.Instance.fadeOut(true);
-    };
-
-    @computed get highlighter() {
-        return (
-            <Group>
-                <IconButton
-                    icon={<FontAwesomeIcon icon="highlighter" style={{ transition: 'transform 0.1s', transform: 'rotate(-45deg)' }} />}
-                    tooltip={'Click to Highlight'}
-                    onClick={this.highlightClicked}
-                    colorPicker={this.highlightColor}
-                    color={StrCast(Doc.UserDoc().userColor)}
-                />
-                <ColorPicker selectedColor={this.highlightColor} setFinalColor={this.changeHighlightColor} setSelectedColor={this.changeHighlightColor} size={Size.XSMALL} />
-            </Group>
-        );
-    }
-
-    @action changeHighlightColor = (color: string) => {
-        const col: ColorState = {
-            hex: color,
-            hsl: { a: 0, h: 0, s: 0, l: 0, source: '' },
-            hsv: { a: 0, h: 0, s: 0, v: 0, source: '' },
-            rgb: { a: 0, r: 0, b: 0, g: 0, source: '' },
-            oldHue: 0,
-            source: '',
-        };
-        this.highlightColor = Utils.colorString(col);
-    };
-
-    /**
-     * Returns whether the selected text can be summarized. The goal is to have
-     * all selected text available to summarize but its only supported for pdf and web ATM.
-     * @returns Whether the GPT icon for summarization should appear
-     */
-    canSummarize = (): boolean => {
-        const docs = SelectionManager.Docs();
-        if (docs.length > 0) {
-            return docs.some(doc => doc.type === DocumentType.PDF || doc.type === DocumentType.WEB);
-        }
-        return false;
-    };
-
-    /**
-     * Returns whether the selected text can be edited.
-     * @returns Whether the GPT icon for summarization should appear
-     */
-    canEdit = (): boolean => {
-        const docs = SelectionManager.Docs();
-        if (docs.length > 0) {
-            return docs.some(doc => doc.type === 'rtf');
-        }
-        return false;
-    };
 
     render() {
-        const buttons =
-            this.Status === 'marquee' ? (
-                <>
-                    {this.highlighter}
-                    <IconButton
-                        tooltip="Drag to Place Annotation" //
-                        onPointerDown={this.pointerDown}
-                        icon={<FontAwesomeIcon icon="comment-alt" />}
-                        color={StrCast(Doc.UserDoc().userColor)}
-                    />
-                    {/* GPT Summarize icon only shows up when text is highlighted, not on marquee selection*/}
-                    {MapAnchorMenu.Instance.StartCropDrag === unimplementedFunction && this.canSummarize() && (
-                        <IconButton
-                            tooltip="Summarize with AI" //
-                            onPointerDown={this.gptSummarize}
-                            icon={<FontAwesomeIcon icon="comment-dots" size="lg" />}
-                            color={StrCast(Doc.UserDoc().userColor)}
-                        />
-                    )}
-                    <GPTPopup
-                        key="gptpopup"
-                        visible={this.showGPTPopup}
-                        text={this.GPTpopupText}
-                        highlightRange={this.highlightRange}
-                        loading={this.loadingGPT}
-                        callSummaryApi={this.gptSummarize}
-                        callEditApi={this.gptEdit}
-                        replaceText={this.replaceText}
-                        mode={this.GPTMode}
-                    />
-                    {MapAnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
-                        <IconButton
-                            tooltip="Click to Record Annotation" //
-                            onPointerDown={this.audioDown}
-                            icon={<FontAwesomeIcon icon="microphone" />}
-                            color={StrCast(Doc.UserDoc().userColor)}
-                        />
-                    )}
-                    {this.canEdit() && (
-                        <IconButton
-                            tooltip="AI edit suggestions" //
-                            onPointerDown={this.gptEdit}
-                            icon={<FontAwesomeIcon icon="pencil-alt" />}
-                            color={StrCast(Doc.UserDoc().userColor)}
-                        />
-                    )}
-                    <Popup
-                        tooltip="Find document to link to selected text" //
-                        type={Type.PRIM}
-                        icon={<FontAwesomeIcon icon={'search'} />}
-                        popup={<LinkPopup key="popup" linkCreateAnchor={this.onMakeAnchor} />}
-                        color={StrCast(Doc.UserDoc().userColor)}
-                    />
-                    {MapAnchorMenu.Instance.StartCropDrag === unimplementedFunction ? null : (
-                        <IconButton
-                            tooltip="Click/Drag to create cropped image" //
-                            onPointerDown={this.cropDown}
-                            icon={<FontAwesomeIcon icon="image" />}
-                            color={StrCast(Doc.UserDoc().userColor)}
-                        />
-                    )}
-                </>
-            ) : (
+        const buttons =(
                 <>
-                    {this.Delete !== returnFalse && (
+                    {(
                         <IconButton
-                            tooltip="Remove Link Anchor" //
+                            tooltip="Delete Pin" //
                             onPointerDown={this.Delete}
                             icon={<FontAwesomeIcon icon="trash-alt" />}
                             color={StrCast(Doc.UserDoc().userColor)}
                         />
                     )}
-                    {this.PinToPres !== returnFalse && (
+                    {(
                         <IconButton
-                            tooltip="Pin to Presentation" //
-                            onPointerDown={this.PinToPres}
+                            tooltip="Link Note to Pin" //
+                            onPointerDown={this.LinkNote}
                             icon={<FontAwesomeIcon icon="map-pin" />}
                             color={StrCast(Doc.UserDoc().userColor)}
                         />
                     )}
-                    {this.ShowTargetTrail !== returnFalse && (
+                    {(
                         <IconButton
-                            tooltip="Show Linked Trail" //
-                            onPointerDown={this.ShowTargetTrail}
+                            tooltip="Center on pin" //
+                            onPointerDown={this.Center}
                             icon={<FontAwesomeIcon icon="taxi" />}
                             color={StrCast(Doc.UserDoc().userColor)}
                         />
                     )}
-                    {this.IsTargetToggler !== returnFalse && (
+                    {/* {this.IsTargetToggler !== returnFalse && (
                         <Toggle
                             tooltip={'Make target visibility toggle on click'}
                             type={Type.PRIM}
@@ -384,10 +134,13 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                             icon={<FontAwesomeIcon icon="thumbtack" />}
                             color={StrCast(Doc.UserDoc().userColor)}
                         />
-                    )}
+                    )} */}
                 </>
             );
 
-        return this.getElement(buttons);
+        return this.getElement(<div ref={MapAnchorMenu.top} style={{width:"100%", display:"flex"}}>
+        {buttons}
+        </div>
+        );
     }
 }
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index be2defa9b..14ecc910e 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -5,6 +5,7 @@ import { Button, EditableText, IconButton, Type } from 'browndash-components';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { TbHeartMinus } from 'react-icons/tb';
 import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
 import { Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
@@ -26,6 +27,7 @@ import { SidebarAnnos } from '../../SidebarAnnos';
 import { DocumentView, OpenWhere } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { PinProps, PresBox } from '../trails';
+import { MapAnchorMenu } from './MapAnchorMenu';
 import './MapBox.scss';
 // amongus
 /**
@@ -116,9 +118,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private toggleAddMarker = false;
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
 
-    @observable _showSidebar = false;
     @computed get SidebarShown() {
-        return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
+        return this.layoutDoc._layout_showSidebar ? true : false;
     }
 
     static _canAnnotate = true;
@@ -299,11 +300,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     const fullWidth = this.layoutDoc[Width]();
                     const mapWidth = fullWidth - this.sidebarWidth();
                     if (this.sidebarWidth() + localDelta[0] > 0) {
-                        this._showSidebar = true;
+                        this.layoutDoc._layout_showSidebar = true;
                         this.layoutDoc._width = fullWidth + localDelta[0];
                         this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
                     } else {
-                        this._showSidebar = false;
+                        this.layoutDoc._layout_showSidebar = false;
                         this.layoutDoc._width = mapWidth;
                         this.layoutDoc._layout_sidebarWidthPercent = '0%';
                     }
@@ -505,13 +506,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     /*
      * Pushpin dblclick
      */
-    @action
-    pushpinDblClicked = (pin: any, pinDoc?: Doc) => {
-        if (pinDoc) this.removePushpin(pinDoc, pin);
-        else this._bingMap.current.entities.remove(pin);
-    };
+    // @action
+    // pushpinDblClicked = (pin: any, pinDoc?: Doc) => {
+    //     if (pinDoc) this.removePushpin(pinDoc);
+    //     else this._bingMap.current.entities.remove(pin);
+    // };
 
     // The pin that is selected
+    @observable
     selectedPin:Doc | undefined;
 
     /*
@@ -519,18 +521,37 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      */
     @action
     pushpinClicked = (pinDoc: Doc) => {
-        // TODO:
-        // if (sidebarannos is not open) open sidebarannos
-        // creates button onclick removes the doc from annotations
+        if (this.selectedPin) {
+            const temp = this.selectedPin;
+            this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp));
+            const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(temp.latitude,temp.longitude));
+            this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(temp as Doc));
+            this._bingMap.current.entities.push(newpin);
+            this.map_docToPinMap.set(temp, newpin);
+        }
+
+        this.selectedPin = pinDoc;
 
-        // pan to pushpin location
-        // this.dataDoc.latitude = pinDoc.latitude;
-        // this.dataDoc.longitude = pinDoc.longitude;
-        this.selectedPin = pinDoc
+        this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin));
+        const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude,this.selectedPin.longitude), {
+            color: 'green',
+        })
+        this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(this.selectedPin as Doc));
+        this._bingMap.current.entities.push(newpin);
+        this.map_docToPinMap.set(this.selectedPin, newpin);
 
-        /// this should SELECT, not center
+        MapAnchorMenu.Instance.Delete = this.deleteSelectedPin
+        MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
+        MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation;
+
+        const point = this._bingMap.current.tryLocationToPixel(new this.MicrosoftMaps.Location(this.selectedPin.latitude,this.selectedPin.longitude))
+        const x = point.x + this.props.PanelWidth() / 2;
+        const y = point.y + this.props.PanelHeight() / 2 +32
+        const cpt = this.props.ScreenToLocalTransform().inverse().transformPoint(x, y);
+        MapAnchorMenu.Instance.jumpTo(cpt[0] - this.sidebarWidth()/this.panelWidth()*200, cpt[1], true);
+        document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true)
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
 
-        this.createNoteAnnotation();
     };
 
     /**
@@ -545,10 +566,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      */
     @action
     mapOnClick = (e: { location: { latitude: any; longitude: any } }) => {
-        if (this.placePinOn) {
-            this.createPushpin(e.location.latitude, e.location.longitude);
-            // this.addAllPins();
-            // this.placePinOn = false;
+        if (this.selectedPin) {
+            const temp = this.selectedPin;
+            this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp));
+            const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(temp.latitude,temp.longitude));
+            this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(temp as Doc));
+            this._bingMap.current.entities.push(newpin);
+            this.map_docToPinMap.set(temp, newpin);
+
+            this.selectedPin=undefined;
+            this.MicrosoftMaps.Events.removeEventListener(this._bingMap.current, 'click', this.mapOnClick);
         }
     };
 
@@ -585,26 +612,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.dataDoc.latitude = location.latitude;
         this.dataDoc.longitude = location.longitude;
         this.dataDoc.mapZoom = this._bingMap.current.getZoom();
-        // this.dataDoc.mapType = this._bingMap.current.getMapTypeId();
-        // this.bingSearchBarContents=location.name;
-
-        // Centers on the searched location
-        // this._bingMap.current.panTo(new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude));
-
-        // this._bingMap.current.setView({
-        //     center: new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude),
-        // });
-
         // Creates a temporary pin but does not add it to the dataDoc
-        var temp = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
-            color: 'blue',
-        });
-
-        this.MicrosoftMaps.Events.addHandler(temp, 'dblclick', (e: any) => this.pushpinDblClicked(temp));
-        if (temp != this.searched_pin || this.searched_pin == null) {
-            this._bingMap.current.entities.push(temp);
-            this.searched_pin = temp;
-        }
+        this.createPushpin(this.dataDoc.latitude, this.dataDoc.longitude)
     };
 
     /**
@@ -624,8 +633,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
         const anchor = Docs.Create.MapanchorDocument({
             title: 'MapAnchor:' + this.rootDoc.title,
-            config_latitude: NumCast(this.selectedPin ? this.selectedPin.latitude : this.dataDoc.latitude),
-            config_longitude: NumCast(this.selectedPin ? this.selectedPin.longitude : this.dataDoc.longitude),
+            config_latitude: NumCast(this.selectedPin?.latitude ??  this.dataDoc.latitude),
+            config_longitude: NumCast(this.selectedPin?.longitude ?? this.dataDoc.longitude),
             config_mapZoom: NumCast(this.dataDoc.mapZoom),
             config_mapType: StrCast(this.dataDoc.mapType),
             // preslocationToLookAt:this.dataDoc.locationToLookAt,
@@ -642,6 +651,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this.rootDoc;
     };
 
+    map_docToPinMap  = new Map<Doc, any>();
     /*
      *   Input: pin doc
      *   Adds MicrosoftMaps Pushpin to the map (render)
@@ -658,7 +668,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this._bingMap.current.entities.push(pushPin);
 
         this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin));
-        this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pushPin, pin));
+        // this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pushPin, pin));
+        this.map_docToPinMap.set(pin,pushPin);
     };
 
     @observable
@@ -666,24 +677,72 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     /*
      *   Input: pin doc
-     *   Removes MicrosoftMaps Pushpin to the map (render)
+     *   Removes pin from annotations
      */
     @action
-    removePushpin = (pinDoc: Doc, pin: any) => {
+    removePushpin = (pinDoc: Doc) => {
         // this.allMapPushpins
         // this.allMapPushpins.map(pin => this.addPushpin(pin));
         // this._bingMap.current.entities.clear();
-        this._bingMap.current.entities.remove(pin);
+        
         this.removeDocument(pinDoc, this.annotationKey);
 
         // this.dataDoc[this.annotationKey]
     };
+    /*
+     * Removes pushpin from map render
+    */
+    deletePushpin = (pinDoc:Doc)=>{
+        this._bingMap.current.entities.remove(this.map_docToPinMap.get(pinDoc));
+        this.map_docToPinMap.delete(pinDoc);
+        this.selectedPin=undefined;
+    }
+
+    @action
+    deleteSelectedPin = undoable(()=>{
+        if (this.selectedPin){
+            this.removePushpin(this.selectedPin)
+            
+        }
+        MapAnchorMenu.Instance.fadeOut(true);
+       document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true)
+    }, "delete pin");
+
+    tryHideMapAnchorMenu = (e:PointerEvent) =>{
+        let target = document.elementFromPoint(e.x, e.y);
+
+        while (target != null) {
+            if (target === MapAnchorMenu.top.current) {
+                return;
+            }
+            target = target.parentElement;
+        }
+        e.stopPropagation();
+        e.preventDefault();
+        MapAnchorMenu.Instance.fadeOut(true);
+        document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true)
+
+    }
+
+    // tryhidemenu = e => if( e.parent... == mapanchormenu.top.currrent) do nothing; else hide menu
+
+    @action
+    centerOnSelectedPin = () =>{
+        if (this.selectedPin){
+            this.dataDoc.latitude = this.selectedPin.latitude;
+            this.dataDoc.longitude = this.selectedPin.longitude;
+            
+            
+        }
+        MapAnchorMenu.Instance.fadeOut(true);
+        document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu)
+    }
 
     /**
      * View options for bing maps
      */
     bingViewOptions = {
-        center: { latitude: this.dataDoc.latitude ?? defaultCenter.lat, longitude: this.dataDoc.longitude ?? defaultCenter.lng },
+        // center: { latitude: this.dataDoc.latitude ?? defaultCenter.lat, longitude: this.dataDoc.longitude ?? defaultCenter.lng },
         zoom: this.dataDoc.latitude ?? 10,
         mapTypeId: 'grayscale',
     };
@@ -734,10 +793,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (!this._bingMap.current) {
             alert('NO Map!?');
         }
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', undoable(this.mapOnClick, 'Added Pin to Map'));
         this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.updateLayout, 'Map Layout Change'));
         this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change'));
-        //this.updateLayout();
+        // this.updateLayout();
         // this.updateMapType();
         this._disposer.location = reaction(
             () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }),
@@ -756,13 +814,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     // Keeps track of when dragging a pin onto map
     draggingPin = false;
     dragToggle = (e: React.PointerEvent) => {
-        console.log('DRAGGING TOGGLE');
+        // console.log('DRAGGING TOGGLE');
         document.addEventListener('drop', this.dropPin, true);
         document.addEventListener('pointermove', this.pinMove, true);
         e.stopPropagation();
     };
     pinMove = (e: PointerEvent) => {
-        console.log('MOVING');
+        // console.log('MOVING');
         e.stopPropagation();
     };
     dropPin = (e: DragEvent) => {
diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
index d28209ea1..66fe1ce53 100644
--- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx
+++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
@@ -19,7 +19,7 @@ export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.mapBoxView.addPushpin(this.rootDoc);
     }
     componentWillUnmount() {
-        // this.mapBoxView.removePushpin(this.rootDoc);
+        this.mapBoxView.deletePushpin(this.rootDoc);
     }
 
     @computed get mapBoxView() {
-- 
cgit v1.2.3-70-g09d2


From 1bb554c64e235c89db47b7ca5bb86a5edfb13ec8 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Wed, 23 Aug 2023 13:44:31 -0400
Subject: Stuck

---
 src/client/views/nodes/MapBox/MapBox.tsx | 34 ++++++++++++++++++++------------
 1 file changed, 21 insertions(+), 13 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 14ecc910e..7269a38f0 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -278,7 +278,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * @returns
      */
     sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
-        if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
+        // if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         return this.removeDocument(doc, sidebarKey);
     };
@@ -516,11 +516,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable
     selectedPin:Doc | undefined;
 
-    /*
-     * Pushpin onclick
-     */
     @action
-    pushpinClicked = (pinDoc: Doc) => {
+    deselectPin=()=>{
         if (this.selectedPin) {
             const temp = this.selectedPin;
             this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp));
@@ -528,9 +525,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(temp as Doc));
             this._bingMap.current.entities.push(newpin);
             this.map_docToPinMap.set(temp, newpin);
+            this.selectedPin=undefined;
+
+            // setTimeout(() => this._sidebarRef.current?.makeDocUnfiltered(this._sidebarRef.current.));
         }
+    }
 
+    /*
+     * Pushpin onclick
+     */
+    @action
+    pushpinClicked = (pinDoc: Doc) => {
+        this.deselectPin();
         this.selectedPin = pinDoc;
+        
 
         this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin));
         const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude,this.selectedPin.longitude), {
@@ -552,6 +560,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true)
         this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
 
+
+        // Filter sidebar:
+        // if sidebar is open, filter.
+        if (this.layoutDoc._layout_showSidebar){
+
+        }
+
     };
 
     /**
@@ -567,14 +582,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     mapOnClick = (e: { location: { latitude: any; longitude: any } }) => {
         if (this.selectedPin) {
-            const temp = this.selectedPin;
-            this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp));
-            const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(temp.latitude,temp.longitude));
-            this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(temp as Doc));
-            this._bingMap.current.entities.push(newpin);
-            this.map_docToPinMap.set(temp, newpin);
-
-            this.selectedPin=undefined;
+            this.deselectPin();
             this.MicrosoftMaps.Events.removeEventListener(this._bingMap.current, 'click', this.mapOnClick);
         }
     };
-- 
cgit v1.2.3-70-g09d2


From edd84f5eaab0ac0498604d43764ec84868b979b0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 23 Aug 2023 21:35:10 -0400
Subject: fixed placement of pushpins when sidebar is opened.

---
 package-lock.json                        | 91 +++++++++++++++++++++++++++++-
 src/client/views/nodes/MapBox/MapBox.tsx | 95 ++++++++++++++++----------------
 2 files changed, 134 insertions(+), 52 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/package-lock.json b/package-lock.json
index 11e7e7db9..093759145 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7063,12 +7063,97 @@
                                         "strip-ansi": "^7.0.1"
                                     }
                                 },
+                                "string-width-cjs": {
+                                    "version": "npm:string-width@4.2.3",
+                                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                                    "requires": {
+                                        "emoji-regex": "^8.0.0",
+                                        "is-fullwidth-code-point": "^3.0.0",
+                                        "strip-ansi": "^6.0.1"
+                                    },
+                                    "dependencies": {
+                                        "ansi-regex": {
+                                            "version": "5.0.1",
+                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                                        },
+                                        "emoji-regex": {
+                                            "version": "8.0.0",
+                                            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                                            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+                                        },
+                                        "strip-ansi": {
+                                            "version": "6.0.1",
+                                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                                            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                                            "requires": {
+                                                "ansi-regex": "^5.0.1"
+                                            }
+                                        }
+                                    }
+                                },
                                 "strip-ansi": {
                                     "version": "7.1.0",
                                     "bundled": true,
                                     "requires": {
                                         "ansi-regex": "^6.0.1"
                                     }
+                                },
+                                "strip-ansi-cjs": {
+                                    "version": "npm:strip-ansi@6.0.1",
+                                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                                    "requires": {
+                                        "ansi-regex": "^5.0.1"
+                                    },
+                                    "dependencies": {
+                                        "ansi-regex": {
+                                            "version": "5.0.1",
+                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                                        }
+                                    }
+                                },
+                                "wrap-ansi-cjs": {
+                                    "version": "npm:wrap-ansi@7.0.0",
+                                    "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+                                    "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+                                    "requires": {
+                                        "ansi-styles": "^4.0.0",
+                                        "string-width": "^4.1.0",
+                                        "strip-ansi": "^6.0.0"
+                                    },
+                                    "dependencies": {
+                                        "ansi-regex": {
+                                            "version": "5.0.1",
+                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                                        },
+                                        "emoji-regex": {
+                                            "version": "8.0.0",
+                                            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                                            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+                                        },
+                                        "string-width": {
+                                            "version": "4.2.3",
+                                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                                            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                                            "requires": {
+                                                "emoji-regex": "^8.0.0",
+                                                "is-fullwidth-code-point": "^3.0.0",
+                                                "strip-ansi": "^6.0.1"
+                                            }
+                                        },
+                                        "strip-ansi": {
+                                            "version": "6.0.1",
+                                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                                            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                                            "requires": {
+                                                "ansi-regex": "^5.0.1"
+                                            }
+                                        }
+                                    }
                                 }
                             }
                         },
@@ -8573,7 +8658,7 @@
                             }
                         },
                         "string-width-cjs": {
-                            "version": "npm:string-width@4.2.3",
+                            "version": "npm:string-width-cjs@4.2.3",
                             "bundled": true,
                             "requires": {
                                 "emoji-regex": "^8.0.0",
@@ -8596,7 +8681,7 @@
                             }
                         },
                         "strip-ansi-cjs": {
-                            "version": "npm:strip-ansi@6.0.1",
+                            "version": "npm:strip-ansi-cjs@6.0.1",
                             "bundled": true,
                             "requires": {
                                 "ansi-regex": "^5.0.1"
@@ -8755,7 +8840,7 @@
                             }
                         },
                         "wrap-ansi-cjs": {
-                            "version": "npm:wrap-ansi@7.0.0",
+                            "version": "npm:wrap-ansi-cjs@7.0.0",
                             "bundled": true,
                             "requires": {
                                 "ansi-styles": "^4.0.0",
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 7269a38f0..654d446a2 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -514,22 +514,22 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     // The pin that is selected
     @observable
-    selectedPin:Doc | undefined;
+    selectedPin: Doc | undefined;
 
     @action
-    deselectPin=()=>{
+    deselectPin = () => {
         if (this.selectedPin) {
             const temp = this.selectedPin;
             this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp));
-            const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(temp.latitude,temp.longitude));
+            const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(temp.latitude, temp.longitude));
             this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(temp as Doc));
             this._bingMap.current.entities.push(newpin);
             this.map_docToPinMap.set(temp, newpin);
-            this.selectedPin=undefined;
+            this.selectedPin = undefined;
 
             // setTimeout(() => this._sidebarRef.current?.makeDocUnfiltered(this._sidebarRef.current.));
         }
-    }
+    };
 
     /*
      * Pushpin onclick
@@ -538,35 +538,31 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     pushpinClicked = (pinDoc: Doc) => {
         this.deselectPin();
         this.selectedPin = pinDoc;
-        
 
         this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin));
-        const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude,this.selectedPin.longitude), {
+        const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude), {
             color: 'green',
-        })
+        });
         this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(this.selectedPin as Doc));
         this._bingMap.current.entities.push(newpin);
         this.map_docToPinMap.set(this.selectedPin, newpin);
 
-        MapAnchorMenu.Instance.Delete = this.deleteSelectedPin
+        MapAnchorMenu.Instance.Delete = this.deleteSelectedPin;
         MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
         MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation;
 
-        const point = this._bingMap.current.tryLocationToPixel(new this.MicrosoftMaps.Location(this.selectedPin.latitude,this.selectedPin.longitude))
+        const point = this._bingMap.current.tryLocationToPixel(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude));
         const x = point.x + this.props.PanelWidth() / 2;
-        const y = point.y + this.props.PanelHeight() / 2 +32
+        const y = point.y + this.props.PanelHeight() / 2 + 32;
         const cpt = this.props.ScreenToLocalTransform().inverse().transformPoint(x, y);
-        MapAnchorMenu.Instance.jumpTo(cpt[0] - this.sidebarWidth()/this.panelWidth()*200, cpt[1], true);
-        document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true)
+        MapAnchorMenu.Instance.jumpTo(cpt[0] - (this.sidebarWidth() / this.panelWidth()) * 200, cpt[1], true);
+        document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
         this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
 
-
         // Filter sidebar:
         // if sidebar is open, filter.
-        if (this.layoutDoc._layout_showSidebar){
-
+        if (this.layoutDoc._layout_showSidebar) {
         }
-
     };
 
     /**
@@ -621,7 +617,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.dataDoc.longitude = location.longitude;
         this.dataDoc.mapZoom = this._bingMap.current.getZoom();
         // Creates a temporary pin but does not add it to the dataDoc
-        this.createPushpin(this.dataDoc.latitude, this.dataDoc.longitude)
+        this.createPushpin(this.dataDoc.latitude, this.dataDoc.longitude);
     };
 
     /**
@@ -641,7 +637,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
         const anchor = Docs.Create.MapanchorDocument({
             title: 'MapAnchor:' + this.rootDoc.title,
-            config_latitude: NumCast(this.selectedPin?.latitude ??  this.dataDoc.latitude),
+            config_latitude: NumCast(this.selectedPin?.latitude ?? this.dataDoc.latitude),
             config_longitude: NumCast(this.selectedPin?.longitude ?? this.dataDoc.longitude),
             config_mapZoom: NumCast(this.dataDoc.mapZoom),
             config_mapType: StrCast(this.dataDoc.mapType),
@@ -659,7 +655,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this.rootDoc;
     };
 
-    map_docToPinMap  = new Map<Doc, any>();
+    map_docToPinMap = new Map<Doc, any>();
     /*
      *   Input: pin doc
      *   Adds MicrosoftMaps Pushpin to the map (render)
@@ -677,7 +673,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin));
         // this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pushPin, pin));
-        this.map_docToPinMap.set(pin,pushPin);
+        this.map_docToPinMap.set(pin, pushPin);
     };
 
     @observable
@@ -692,31 +688,30 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // this.allMapPushpins
         // this.allMapPushpins.map(pin => this.addPushpin(pin));
         // this._bingMap.current.entities.clear();
-        
+
         this.removeDocument(pinDoc, this.annotationKey);
 
         // this.dataDoc[this.annotationKey]
     };
     /*
      * Removes pushpin from map render
-    */
-    deletePushpin = (pinDoc:Doc)=>{
+     */
+    deletePushpin = (pinDoc: Doc) => {
         this._bingMap.current.entities.remove(this.map_docToPinMap.get(pinDoc));
         this.map_docToPinMap.delete(pinDoc);
-        this.selectedPin=undefined;
-    }
+        this.selectedPin = undefined;
+    };
 
     @action
-    deleteSelectedPin = undoable(()=>{
-        if (this.selectedPin){
-            this.removePushpin(this.selectedPin)
-            
+    deleteSelectedPin = undoable(() => {
+        if (this.selectedPin) {
+            this.removePushpin(this.selectedPin);
         }
         MapAnchorMenu.Instance.fadeOut(true);
-       document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true)
-    }, "delete pin");
+        document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
+    }, 'delete pin');
 
-    tryHideMapAnchorMenu = (e:PointerEvent) =>{
+    tryHideMapAnchorMenu = (e: PointerEvent) => {
         let target = document.elementFromPoint(e.x, e.y);
 
         while (target != null) {
@@ -728,23 +723,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         e.stopPropagation();
         e.preventDefault();
         MapAnchorMenu.Instance.fadeOut(true);
-        document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true)
-
-    }
+        document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
+    };
 
     // tryhidemenu = e => if( e.parent... == mapanchormenu.top.currrent) do nothing; else hide menu
 
     @action
-    centerOnSelectedPin = () =>{
-        if (this.selectedPin){
+    centerOnSelectedPin = () => {
+        if (this.selectedPin) {
             this.dataDoc.latitude = this.selectedPin.latitude;
             this.dataDoc.longitude = this.selectedPin.longitude;
-            
-            
         }
         MapAnchorMenu.Instance.fadeOut(true);
-        document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu)
-    }
+        document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu);
+    };
 
     /**
      * View options for bing maps
@@ -809,11 +801,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }),
             locationObject => {
                 // if (this._bingMap.current)
-                this._bingMap.current?.setView({
-                    mapTypeId: locationObject.mapType,
-                    zoom: locationObject.zoom,
-                    center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng),
-                });
+                try {
+                    locationObject?.zoom &&
+                        this._bingMap.current?.setView({
+                            mapTypeId: locationObject.mapType,
+                            zoom: locationObject.zoom,
+                            center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng),
+                        });
+                } catch (e) {
+                    console.log(e);
+                }
             },
             { fireImmediately: true }
         );
@@ -841,8 +838,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         while (target != null) {
             if (target === this._ref.current) {
                 const cpt = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
-                const x = cpt[0] - this.props.PanelWidth() / 2;
-                const y = cpt[1] - 32 - this.props.PanelHeight() / 2;
+                const x = cpt[0] - (this.props.PanelWidth() - this.sidebarWidth()) / 2;
+                const y = cpt[1] - 32 /* height of search bar */ - this.props.PanelHeight() / 2;
                 const location = this._bingMap.current.tryPixelToLocation(new this.MicrosoftMaps.Point(x, y));
                 this.createPushpin(location.latitude, location.longitude);
                 break;
-- 
cgit v1.2.3-70-g09d2


From 6a85c8d4115a968f563369516d38549745e76303 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Thu, 24 Aug 2023 12:47:57 -0400
Subject: All working except createnote undo, and center on selected pin
 getAnchor

---
 src/client/views/SidebarAnnos.tsx               |  5 ++-
 src/client/views/nodes/MapBox/MapAnchorMenu.tsx |  2 +-
 src/client/views/nodes/MapBox/MapBox.tsx        | 47 ++++++++++++++++++-------
 src/fields/Doc.ts                               |  4 +--
 4 files changed, 40 insertions(+), 18 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index cd50865fb..7635d719e 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -225,10 +225,9 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                 <div className="sidebarAnnos-tagList" style={{ height: this.filtersHeight() }} onWheel={e => e.stopPropagation()}>
                     {this.allUsers.map(renderUsers)}
                     {this.allHashtags.map(renderTag)}
-                    {Array.from(this.allMetadata.keys())
+                    {/* {Array.from(this.allMetadata.keys())
                         .sort()
-                        .map(key => renderMeta(key, this.allMetadata.get(key)))}
-                    Hello
+                        .map(key => renderMeta(key, this.allMetadata.get(key)))} */}
                 </div>
 
 
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index 439c1f14f..897bb90a6 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -120,7 +120,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                         <IconButton
                             tooltip="Center on pin" //
                             onPointerDown={this.Center}
-                            icon={<FontAwesomeIcon icon="taxi" />}
+                            icon={<FontAwesomeIcon icon="compress-arrows-alt" />}
                             color={StrCast(Doc.UserDoc().userColor)}
                         />
                     )}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 654d446a2..dbb38e763 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -14,6 +14,7 @@ import { ScriptField } from '../../../../fields/ScriptField';
 import { NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
+import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
@@ -352,10 +353,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
     };
 
-    createNoteAnnotation = () => {
+    createNoteAnnotation = undoable(() => {
         !this.layoutDoc.layout_showSidebar && this.toggleSidebar();
-        setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false))); // give time for sidebarRef to be created
-    };
+        setTimeout(() =>{
+          const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false));
+          if (note && this.selectedPin) {
+            note.latitude = this.selectedPin.latitude;
+            note.longitude = this.selectedPin.latitude;
+          }
+        }); // give time for sidebarRef to be created
+    }, "create linked note");
 
     sidebarDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
@@ -488,7 +495,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Creates Pushpin doc and adds it to the list of annotations
      */
     @action
-    createPushpin = (latitude: number, longitude: number) => {
+    createPushpin = undoable((latitude: number, longitude: number) => {
         // Stores the pushpin as a MapMarkerDocument
         const mapMarker = Docs.Create.PushpinDocument(
             NumCast(latitude),
@@ -501,7 +508,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.addDocument(mapMarker, this.annotationKey);
 
         // mapMarker.infoWindowOpen = true;
-    };
+    },"createpin");
 
     /*
      * Pushpin dblclick
@@ -519,6 +526,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     deselectPin = () => {
         if (this.selectedPin) {
+            // Removes filter
+            Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "remove");
+            Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.longitude, "remove");
+
             const temp = this.selectedPin;
             this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp));
             const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(temp.latitude, temp.longitude));
@@ -527,10 +538,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this.map_docToPinMap.set(temp, newpin);
             this.selectedPin = undefined;
 
-            // setTimeout(() => this._sidebarRef.current?.makeDocUnfiltered(this._sidebarRef.current.));
+            
         }
+        
     };
 
+    getView = async (doc: Doc) => {
+        if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar();
+        return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
+    };
     /*
      * Pushpin onclick
      */
@@ -539,6 +555,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.deselectPin();
         this.selectedPin = pinDoc;
 
+        Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "match");
+        Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.latitude, "match");
+
         this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin));
         const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude), {
             color: 'green',
@@ -552,17 +571,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation;
 
         const point = this._bingMap.current.tryLocationToPixel(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude));
-        const x = point.x + this.props.PanelWidth() / 2;
+        const x = point.x + (this.props.PanelWidth() - this.sidebarWidth()) / 2;
         const y = point.y + this.props.PanelHeight() / 2 + 32;
         const cpt = this.props.ScreenToLocalTransform().inverse().transformPoint(x, y);
-        MapAnchorMenu.Instance.jumpTo(cpt[0] - (this.sidebarWidth() / this.panelWidth()) * 200, cpt[1], true);
+        MapAnchorMenu.Instance.jumpTo(cpt[0], cpt[1], true);
+
         document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
         this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
 
         // Filter sidebar:
-        // if sidebar is open, filter.
-        if (this.layoutDoc._layout_showSidebar) {
-        }
+
     };
 
     /**
@@ -573,7 +591,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     /**
-     * Map OnClick ~> creates a pushpin
+     * Map OnClick
      */
     @action
     mapOnClick = (e: { location: { latitude: any; longitude: any } }) => {
@@ -700,11 +718,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this._bingMap.current.entities.remove(this.map_docToPinMap.get(pinDoc));
         this.map_docToPinMap.delete(pinDoc);
         this.selectedPin = undefined;
+
     };
 
     @action
     deleteSelectedPin = undoable(() => {
         if (this.selectedPin) {
+            // Removes filter
+            Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "remove");
+            Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.longitude, "remove");
+
             this.removePushpin(this.selectedPin);
         }
         MapAnchorMenu.Instance.fadeOut(true);
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 6d4f4180d..7ba4f0e6f 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1490,8 +1490,8 @@ export namespace Doc {
         runInAction(() => {
             for (let i = 0; i < childFilters.length; i++) {
                 const fields = childFilters[i].split(FilterSep); // split key:value:modifier
-                if (fields[0] === key && (fields[1] === value || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) {
-                    if (fields[2] === modifiers && modifiers && fields[1] === value) {
+                if (fields[0] === key && (fields[1] === value.toString() || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) {
+                    if (fields[2] === modifiers && modifiers && fields[1] === value.toString()) {
                         if (toggle) modifiers = 'remove';
                         else return;
                     }
-- 
cgit v1.2.3-70-g09d2


From 2c46608d3207a8463907b0e1904d9b3026d6d1c8 Mon Sep 17 00:00:00 2001
From: Eric <ericmabr@gmail.com>
Date: Sat, 26 Aug 2023 12:33:47 -0400
Subject: Final1

---
 src/client/util/DocumentManager.ts        |  2 +-
 src/client/views/SidebarAnnos.tsx         |  1 +
 src/client/views/nodes/MapBox/MapBox.tsx  | 77 ++++++++++++++++++++++++++-----
 src/client/views/nodes/trails/PresBox.tsx |  4 +-
 4 files changed, 70 insertions(+), 14 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 7c3b5be05..5b627c2f3 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -310,7 +310,7 @@ export class DocumentManager {
         if (viewSpec && docView) {
             if (docView.ComponentView instanceof FormattedTextBox) docView.ComponentView?.focus(viewSpec, options);
             PresBox.restoreTargetDocView(docView, viewSpec, options.zoomTime ?? 500);
-            Doc.linkFollowHighlight(docView.rootDoc, undefined, options.effect);
+            Doc.linkFollowHighlight(viewSpec ? [docView.rootDoc, viewSpec]: docView.rootDoc, undefined, options.effect);
             if (options.playAudio) DocumentManager.playAudioAnno(docView.rootDoc);
             if (options.toggleTarget && (!options.didMove || docView.rootDoc.hidden)) docView.rootDoc.hidden = !docView.rootDoc.hidden;
             if (options.effect) docView.rootDoc[Animation] = options.effect;
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 7635d719e..520485a71 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -87,6 +87,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
 
         const taggedContent = this.childFilters()
             .filter(data => data.split(':')[0])
+            .filter(data => data.split(':')[0] !== 'latitude' && data.split(':')[0] !== 'longitude')
             .map(data => {
                 const key = data.split(':')[0];
                 const val = Field.Copy(this.allMetadata.get(key));
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index dbb38e763..93020354d 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -7,7 +7,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { TbHeartMinus } from 'react-icons/tb';
 import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
-import { Width } from '../../../../fields/DocSymbols';
+import { Highlight, Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { ScriptField } from '../../../../fields/ScriptField';
@@ -133,6 +133,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     componentWillUnmount(): void {
+        this.deselectPin();
         Object.keys(this._disposer).forEach(key => this._disposer[key]?.());
     }
     // iterate allMarkers to size, center, and zoom map to contain all markers
@@ -353,17 +354,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
     };
 
-    createNoteAnnotation = undoable(() => {
+    createNoteAnnotation = () => {
         !this.layoutDoc.layout_showSidebar && this.toggleSidebar();
-        setTimeout(() =>{
-          const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false));
-          if (note && this.selectedPin) {
-            note.latitude = this.selectedPin.latitude;
-            note.longitude = this.selectedPin.latitude;
-          }
-        }); // give time for sidebarRef to be created
-    }, "create linked note");
 
+        setTimeout(undoable(() => {
+            const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false));
+            if (note && this.selectedPin) {
+              note.latitude = this.selectedPin.latitude;
+              note.longitude = this.selectedPin.latitude;
+            }
+          },"create note annotation"))
+    
+    }
     sidebarDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
     };
@@ -818,7 +820,60 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.updateLayout, 'Map Layout Change'));
         this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change'));
-        // this.updateLayout();
+        this.updateLayout();
+        this._disposer.highlight = reaction( () => this.allMapPushpins.map(doc => doc[Highlight]),
+        () => this.allMapPushpins.forEach(doc => {
+
+            // if(doc[Highlight]){
+            //     this.deselectPin();
+            //     this.selectedPin = doc;
+
+            //     Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "match");
+            //     Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.latitude, "match");
+
+            //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin));
+            //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude), {
+            //         color: 'green',
+            //     });
+            //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(this.selectedPin as Doc));
+            //     this._bingMap.current.entities.push(newpin);
+            //     this.map_docToPinMap.set(this.selectedPin, newpin);
+
+            //     MapAnchorMenu.Instance.Delete = this.deleteSelectedPin;
+            //     MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
+            //     MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation;
+            // }
+            // if (doc[Highlight]) {
+            //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc));
+            //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {
+            //         color: 'orange',
+            //     });
+            //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc));
+            //     this._bingMap.current.entities.push(newpin);
+            //     this.map_docToPinMap.set(doc, newpin);
+
+            // } 
+            // if (this.map_docToPinMap.get(doc).getColor() == 'orange' && !doc[Highlight]) {
+            //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc));
+            //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {});
+            //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc));
+            //     this._bingMap.current.entities.push(newpin);
+            //     this.map_docToPinMap.set(doc, newpin);
+
+            // } 
+            // else if (this.map_docToPinMap.get(doc).getColor() != 'orange' && doc[Highlight]) {
+            //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc));
+            //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {
+            //         color: 'orange',
+            //     });
+            //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc));
+            //     this._bingMap.current.entities.push(newpin);
+            //     this.map_docToPinMap.set(doc, newpin);
+            // }
+
+            
+        })
+        , {fireImmediately: true})
         // this.updateMapType();
         this._disposer.location = reaction(
             () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }),
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index a94f1f04b..afd9bccab 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -661,8 +661,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 pinDoc.config_yRange = undefined; //targetDoc?.yrange;
             }
             if (pinProps.pinData.map) {
-                pinDoc.config_latitude = targetDoc?.latitude;
-                pinDoc.config_longitude = targetDoc?.longitude;
+                // pinDoc.config_latitude = targetDoc?.latitude;
+                // pinDoc.config_longitude = targetDoc?.longitude;
                 pinDoc.config_mapZoom = targetDoc?.mapZoom;
                 pinDoc.config_mapType = targetDoc?.mapType;
                 //...
-- 
cgit v1.2.3-70-g09d2


From 534ca5e695032d598156fab6ab04ef2ecb1f3f9b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 26 Aug 2023 16:32:55 -0400
Subject: fixed map crashing when deslecting a pin

---
 package-lock.json                        |  91 ++++++++++++++++++-
 src/client/views/nodes/MapBox/MapBox.tsx | 150 ++++++++++++++-----------------
 2 files changed, 155 insertions(+), 86 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/package-lock.json b/package-lock.json
index 356224577..00410e3be 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7094,12 +7094,97 @@
                                         "strip-ansi": "^7.0.1"
                                     }
                                 },
+                                "string-width-cjs": {
+                                    "version": "npm:string-width@4.2.3",
+                                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                                    "requires": {
+                                        "emoji-regex": "^8.0.0",
+                                        "is-fullwidth-code-point": "^3.0.0",
+                                        "strip-ansi": "^6.0.1"
+                                    },
+                                    "dependencies": {
+                                        "ansi-regex": {
+                                            "version": "5.0.1",
+                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                                        },
+                                        "emoji-regex": {
+                                            "version": "8.0.0",
+                                            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                                            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+                                        },
+                                        "strip-ansi": {
+                                            "version": "6.0.1",
+                                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                                            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                                            "requires": {
+                                                "ansi-regex": "^5.0.1"
+                                            }
+                                        }
+                                    }
+                                },
                                 "strip-ansi": {
                                     "version": "7.1.0",
                                     "bundled": true,
                                     "requires": {
                                         "ansi-regex": "^6.0.1"
                                     }
+                                },
+                                "strip-ansi-cjs": {
+                                    "version": "npm:strip-ansi@6.0.1",
+                                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                                    "requires": {
+                                        "ansi-regex": "^5.0.1"
+                                    },
+                                    "dependencies": {
+                                        "ansi-regex": {
+                                            "version": "5.0.1",
+                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                                        }
+                                    }
+                                },
+                                "wrap-ansi-cjs": {
+                                    "version": "npm:wrap-ansi@7.0.0",
+                                    "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+                                    "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+                                    "requires": {
+                                        "ansi-styles": "^4.0.0",
+                                        "string-width": "^4.1.0",
+                                        "strip-ansi": "^6.0.0"
+                                    },
+                                    "dependencies": {
+                                        "ansi-regex": {
+                                            "version": "5.0.1",
+                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                                        },
+                                        "emoji-regex": {
+                                            "version": "8.0.0",
+                                            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                                            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+                                        },
+                                        "string-width": {
+                                            "version": "4.2.3",
+                                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                                            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                                            "requires": {
+                                                "emoji-regex": "^8.0.0",
+                                                "is-fullwidth-code-point": "^3.0.0",
+                                                "strip-ansi": "^6.0.1"
+                                            }
+                                        },
+                                        "strip-ansi": {
+                                            "version": "6.0.1",
+                                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                                            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                                            "requires": {
+                                                "ansi-regex": "^5.0.1"
+                                            }
+                                        }
+                                    }
                                 }
                             }
                         },
@@ -8604,7 +8689,7 @@
                             }
                         },
                         "string-width-cjs": {
-                            "version": "npm:string-width@4.2.3",
+                            "version": "npm:string-width-cjs@4.2.3",
                             "bundled": true,
                             "requires": {
                                 "emoji-regex": "^8.0.0",
@@ -8627,7 +8712,7 @@
                             }
                         },
                         "strip-ansi-cjs": {
-                            "version": "npm:strip-ansi@6.0.1",
+                            "version": "npm:strip-ansi-cjs@6.0.1",
                             "bundled": true,
                             "requires": {
                                 "ansi-regex": "^5.0.1"
@@ -8786,7 +8871,7 @@
                             }
                         },
                         "wrap-ansi-cjs": {
-                            "version": "npm:wrap-ansi@7.0.0",
+                            "version": "npm:wrap-ansi-cjs@7.0.0",
                             "bundled": true,
                             "requires": {
                                 "ansi-styles": "^4.0.0",
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 93020354d..48bc02f84 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -357,15 +357,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     createNoteAnnotation = () => {
         !this.layoutDoc.layout_showSidebar && this.toggleSidebar();
 
-        setTimeout(undoable(() => {
-            const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false));
-            if (note && this.selectedPin) {
-              note.latitude = this.selectedPin.latitude;
-              note.longitude = this.selectedPin.latitude;
-            }
-          },"create note annotation"))
-    
-    }
+        setTimeout(
+            undoable(() => {
+                const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false));
+                if (note && this.selectedPin) {
+                    note.latitude = this.selectedPin.latitude;
+                    note.longitude = this.selectedPin.latitude;
+                }
+            }, 'create note annotation')
+        );
+    };
     sidebarDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
     };
@@ -510,7 +511,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.addDocument(mapMarker, this.annotationKey);
 
         // mapMarker.infoWindowOpen = true;
-    },"createpin");
+    }, 'createpin');
 
     /*
      * Pushpin dblclick
@@ -529,8 +530,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     deselectPin = () => {
         if (this.selectedPin) {
             // Removes filter
-            Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "remove");
-            Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.longitude, "remove");
+            Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
+            Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
 
             const temp = this.selectedPin;
             this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp));
@@ -539,10 +540,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this._bingMap.current.entities.push(newpin);
             this.map_docToPinMap.set(temp, newpin);
             this.selectedPin = undefined;
-
-            
         }
-        
     };
 
     getView = async (doc: Doc) => {
@@ -557,8 +555,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.deselectPin();
         this.selectedPin = pinDoc;
 
-        Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "match");
-        Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.latitude, "match");
+        Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
+        Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.latitude, 'match');
 
         this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin));
         const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude), {
@@ -579,10 +577,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         MapAnchorMenu.Instance.jumpTo(cpt[0], cpt[1], true);
 
         document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
-
         // Filter sidebar:
-
     };
 
     /**
@@ -596,13 +591,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Map OnClick
      */
     @action
-    mapOnClick = (e: { location: { latitude: any; longitude: any } }) => {
-        if (this.selectedPin) {
-            this.deselectPin();
-            this.MicrosoftMaps.Events.removeEventListener(this._bingMap.current, 'click', this.mapOnClick);
-        }
-    };
-
+    mapOnClick = (e: { location: { latitude: any; longitude: any } }) => this.deselectPin();
     /*
      * Updates values of layout doc to match the current map
      */
@@ -720,15 +709,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this._bingMap.current.entities.remove(this.map_docToPinMap.get(pinDoc));
         this.map_docToPinMap.delete(pinDoc);
         this.selectedPin = undefined;
-
     };
 
     @action
     deleteSelectedPin = undoable(() => {
         if (this.selectedPin) {
             // Removes filter
-            Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "remove");
-            Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.longitude, "remove");
+            Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
+            Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
 
             this.removePushpin(this.selectedPin);
         }
@@ -818,62 +806,58 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (!this._bingMap.current) {
             alert('NO Map!?');
         }
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
         this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.updateLayout, 'Map Layout Change'));
         this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change'));
         this.updateLayout();
-        this._disposer.highlight = reaction( () => this.allMapPushpins.map(doc => doc[Highlight]),
-        () => this.allMapPushpins.forEach(doc => {
-
-            // if(doc[Highlight]){
-            //     this.deselectPin();
-            //     this.selectedPin = doc;
-
-            //     Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "match");
-            //     Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.latitude, "match");
-
-            //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin));
-            //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude), {
-            //         color: 'green',
-            //     });
-            //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(this.selectedPin as Doc));
-            //     this._bingMap.current.entities.push(newpin);
-            //     this.map_docToPinMap.set(this.selectedPin, newpin);
-
-            //     MapAnchorMenu.Instance.Delete = this.deleteSelectedPin;
-            //     MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
-            //     MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation;
-            // }
-            // if (doc[Highlight]) {
-            //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc));
-            //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {
-            //         color: 'orange',
-            //     });
-            //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc));
-            //     this._bingMap.current.entities.push(newpin);
-            //     this.map_docToPinMap.set(doc, newpin);
-
-            // } 
-            // if (this.map_docToPinMap.get(doc).getColor() == 'orange' && !doc[Highlight]) {
-            //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc));
-            //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {});
-            //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc));
-            //     this._bingMap.current.entities.push(newpin);
-            //     this.map_docToPinMap.set(doc, newpin);
-
-            // } 
-            // else if (this.map_docToPinMap.get(doc).getColor() != 'orange' && doc[Highlight]) {
-            //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc));
-            //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {
-            //         color: 'orange',
-            //     });
-            //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc));
-            //     this._bingMap.current.entities.push(newpin);
-            //     this.map_docToPinMap.set(doc, newpin);
-            // }
-
-            
-        })
-        , {fireImmediately: true})
+        this._disposer.highlight = reaction(
+            () => this.allMapPushpins.map(doc => doc[Highlight]),
+            () =>
+                this.allMapPushpins.forEach(doc => {
+                    // if(doc[Highlight]){
+                    //     this.deselectPin();
+                    //     this.selectedPin = doc;
+                    //     Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "match");
+                    //     Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.latitude, "match");
+                    //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin));
+                    //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude), {
+                    //         color: 'green',
+                    //     });
+                    //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(this.selectedPin as Doc));
+                    //     this._bingMap.current.entities.push(newpin);
+                    //     this.map_docToPinMap.set(this.selectedPin, newpin);
+                    //     MapAnchorMenu.Instance.Delete = this.deleteSelectedPin;
+                    //     MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
+                    //     MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation;
+                    // }
+                    // if (doc[Highlight]) {
+                    //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc));
+                    //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {
+                    //         color: 'orange',
+                    //     });
+                    //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc));
+                    //     this._bingMap.current.entities.push(newpin);
+                    //     this.map_docToPinMap.set(doc, newpin);
+                    // }
+                    // if (this.map_docToPinMap.get(doc).getColor() == 'orange' && !doc[Highlight]) {
+                    //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc));
+                    //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {});
+                    //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc));
+                    //     this._bingMap.current.entities.push(newpin);
+                    //     this.map_docToPinMap.set(doc, newpin);
+                    // }
+                    // else if (this.map_docToPinMap.get(doc).getColor() != 'orange' && doc[Highlight]) {
+                    //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc));
+                    //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {
+                    //         color: 'orange',
+                    //     });
+                    //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc));
+                    //     this._bingMap.current.entities.push(newpin);
+                    //     this.map_docToPinMap.set(doc, newpin);
+                    // }
+                }),
+            { fireImmediately: true }
+        );
         // this.updateMapType();
         this._disposer.location = reaction(
             () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }),
-- 
cgit v1.2.3-70-g09d2


From c973e8cbd4aabb545ea580b213e7f60d41cf2b20 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 26 Aug 2023 17:21:42 -0400
Subject: added pin highligting when following a link

---
 src/client/views/nodes/MapBox/MapBox.tsx | 102 +++++++++++++------------------
 1 file changed, 43 insertions(+), 59 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 48bc02f84..62d622fd6 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -2,6 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { GoogleMapProps, Marker } from '@react-google-maps/api';
 import BingMapsReact from 'bingmaps-react';
 import { Button, EditableText, IconButton, Type } from 'browndash-components';
+import { docs_v1 } from 'googleapis';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -11,7 +12,7 @@ import { Highlight, Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { ScriptField } from '../../../../fields/ScriptField';
-import { NumCast, StrCast } from '../../../../fields/Types';
+import { DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { DocumentManager } from '../../../util/DocumentManager';
@@ -358,13 +359,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         !this.layoutDoc.layout_showSidebar && this.toggleSidebar();
 
         setTimeout(
-            undoable(() => {
-                const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false));
-                if (note && this.selectedPin) {
-                    note.latitude = this.selectedPin.latitude;
-                    note.longitude = this.selectedPin.latitude;
-                }
-            }, 'create note annotation')
+            undoable(
+                action(() => {
+                    const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false));
+                    if (note && this.selectedPin) {
+                        note.latitude = this.selectedPin.latitude;
+                        note.longitude = this.selectedPin.latitude;
+                    }
+                }),
+                'create note annotation'
+            )
         );
     };
     sidebarDown = (e: React.PointerEvent) => {
@@ -558,13 +562,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
         Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.latitude, 'match');
 
-        this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin));
-        const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude), {
-            color: 'green',
-        });
-        this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(this.selectedPin as Doc));
-        this._bingMap.current.entities.push(newpin);
-        this.map_docToPinMap.set(this.selectedPin, newpin);
+        this.recolorPin(this.selectedPin, 'green');
 
         MapAnchorMenu.Instance.Delete = this.deleteSelectedPin;
         MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
@@ -653,9 +651,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             // preslocationToLookAt:this.dataDoc.locationToLookAt,
             // presType:
             layout_unrendered: true,
-            annotationOn: this.rootDoc,
         });
         if (anchor) {
+            anchor.mapPin = this.selectedPin;
             if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
             /* addAsAnnotation &&*/ this.addDocument(anchor);
             PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc);
@@ -665,6 +663,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     map_docToPinMap = new Map<Doc, any>();
+    map_pinHighlighted = new Map<Doc, boolean>();
     /*
      *   Input: pin doc
      *   Adds MicrosoftMaps Pushpin to the map (render)
@@ -794,6 +793,22 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.bingSearchBarContents = newText;
     };
 
+    recolorPin = (pin: Doc, color?: string) => {
+        this._bingMap.current.entities.remove(this.map_docToPinMap.get(pin));
+        this.map_docToPinMap.delete(pin);
+        const newpin = new this.MicrosoftMaps.Pushpin(
+            new this.MicrosoftMaps.Location(pin.latitude, pin.longitude),
+            color
+                ? {
+                      color,
+                  }
+                : {}
+        );
+        this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(pin));
+        this._bingMap.current.entities.push(newpin);
+        this.map_docToPinMap.set(pin, newpin);
+    };
+
     /*
      * Called when BingMap is first rendered
      * Initializes starting values
@@ -813,49 +828,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this._disposer.highlight = reaction(
             () => this.allMapPushpins.map(doc => doc[Highlight]),
             () =>
-                this.allMapPushpins.forEach(doc => {
-                    // if(doc[Highlight]){
-                    //     this.deselectPin();
-                    //     this.selectedPin = doc;
-                    //     Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "match");
-                    //     Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.latitude, "match");
-                    //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin));
-                    //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude), {
-                    //         color: 'green',
-                    //     });
-                    //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(this.selectedPin as Doc));
-                    //     this._bingMap.current.entities.push(newpin);
-                    //     this.map_docToPinMap.set(this.selectedPin, newpin);
-                    //     MapAnchorMenu.Instance.Delete = this.deleteSelectedPin;
-                    //     MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
-                    //     MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation;
-                    // }
-                    // if (doc[Highlight]) {
-                    //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc));
-                    //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {
-                    //         color: 'orange',
-                    //     });
-                    //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc));
-                    //     this._bingMap.current.entities.push(newpin);
-                    //     this.map_docToPinMap.set(doc, newpin);
-                    // }
-                    // if (this.map_docToPinMap.get(doc).getColor() == 'orange' && !doc[Highlight]) {
-                    //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc));
-                    //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {});
-                    //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc));
-                    //     this._bingMap.current.entities.push(newpin);
-                    //     this.map_docToPinMap.set(doc, newpin);
-                    // }
-                    // else if (this.map_docToPinMap.get(doc).getColor() != 'orange' && doc[Highlight]) {
-                    //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc));
-                    //     const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {
-                    //         color: 'orange',
-                    //     });
-                    //     this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc));
-                    //     this._bingMap.current.entities.push(newpin);
-                    //     this.map_docToPinMap.set(doc, newpin);
-                    // }
-                }),
+                this.allMapPushpins
+                    .map(doc => ({ doc, pushpin: DocCast(doc.mapPin) }))
+                    .filter(pair => pair.pushpin)
+                    .forEach(({ doc, pushpin }) => {
+                        if (doc[Highlight] && !this.map_pinHighlighted.get(pushpin)) {
+                            this.recolorPin(pushpin, 'orange');
+                            this.map_pinHighlighted.set(pushpin, true);
+                        } else if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) {
+                            this.recolorPin(pushpin);
+                            this.map_pinHighlighted.delete(pushpin);
+                        }
+                    }),
             { fireImmediately: true }
         );
         // this.updateMapType();
-- 
cgit v1.2.3-70-g09d2


From 0a813fdf7d73018ad5248d87fecbd9e55f3dc2d7 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 26 Aug 2023 21:25:24 -0400
Subject: many fixes to map search bar, dragging pushpin, highlighting pushpins
 on link following

---
 src/client/documents/Documents.ts         |   8 +-
 src/client/views/nodes/MapBox/MapBox.tsx  | 552 +++++++++---------------------
 src/client/views/nodes/MapBox/MapBox2.tsx |   4 +-
 src/client/views/nodes/trails/PresBox.tsx |  16 +-
 4 files changed, 170 insertions(+), 410 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index af8cc07ed..d5d6fb2ba 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -158,6 +158,7 @@ export class DocumentOptions {
     _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
     latitude?: NUMt = new NumInfo('latitude coordinate for map views');
     longitude?: NUMt = new NumInfo('longitude coordinate for map views');
+    map?: STRt = new StrInfo('text location of map');
     _timecodeToShow?: NUMt = new NumInfo('the time that a document should be displayed (e.g., when an annotation shows up as a video plays)');
     _timecodeToHide?: NUMt = new NumInfo('the time that a document should be hidden');
     _width?: NUMt = new NumInfo('displayed width of a document');
@@ -295,8 +296,9 @@ export class DocumentOptions {
 
     config_latitude?: NUMt = new NumInfo('latitude of a map'); // latitude of a map
     config_longitude?: NUMt = new NumInfo('longitude of map'); // longitude of map
-    config_mapZoom?: NUMt = new NumInfo('zoom of map'); // zoom of map
-    config_mapType?: string;
+    config_map_zoom?: NUMt = new NumInfo('zoom of map'); // zoom of map
+    config_map_type?: string;
+    config_map?: string;
     config_panX?: NUMt = new NumInfo('panX saved as a view spec');
     config_panY?: NUMt = new NumInfo('panY saved as a view spec');
     config_viewScale?: NUMt = new NumInfo('viewScale saved as a view Spec');
@@ -527,7 +529,7 @@ export namespace Docs {
                 DocumentType.MAP,
                 {
                     layout: { view: MapBox, dataField: defaultDataKey },
-                    options: { _height: 600, _width: 800, nativeDimModifiable: true, systemIcon: 'BsFillPinMapFill' },
+                    options: { map: '', _height: 600, _width: 800, nativeDimModifiable: true, systemIcon: 'BsFillPinMapFill' },
                 },
             ],
             [
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 62d622fd6..94944b83d 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,19 +1,14 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { GoogleMapProps, Marker } from '@react-google-maps/api';
 import BingMapsReact from 'bingmaps-react';
 import { Button, EditableText, IconButton, Type } from 'browndash-components';
-import { docs_v1 } from 'googleapis';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { TbHeartMinus } from 'react-icons/tb';
 import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
 import { Highlight, Width } from '../../../../fields/DocSymbols';
-import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
-import { ScriptField } from '../../../../fields/ScriptField';
 import { DocCast, NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
@@ -24,9 +19,8 @@ import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
 import { MarqueeAnnotator } from '../../MarqueeAnnotator';
-import { Annotation } from '../../pdf/Annotation';
 import { SidebarAnnos } from '../../SidebarAnnos';
-import { DocumentView, OpenWhere } from '../DocumentView';
+import { DocumentView } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { PinProps, PresBox } from '../trails';
 import { MapAnchorMenu } from './MapAnchorMenu';
@@ -45,29 +39,8 @@ import './MapBox.scss';
  * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
  */
 
-// const _global = (window /* browser */ || global /* node */) as any;
-
-const mapContainerStyle = {
-    height: '100%',
-};
-
-const defaultCenter = {
-    lat: 42.360081,
-    lng: -71.058884,
-};
-
-const mapOptions = {
-    fullscreenControl: false,
-};
-
 const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/  and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey>
 
-// const script = document.createElement('script');
-// script.defer = true;
-// script.async = true;
-// script.src = `https://maps.googleapis.com/maps/api/js?key=${bingApiKey}&libraries=places,drawing`;
-// document.head.appendChild(script);
-
 /**
  * Consider integrating later: allows for drawing, circling, making shapes on map
  */
@@ -85,169 +58,49 @@ const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing
 // });
 
 @observer
-export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
-    private _dropDisposer?: DragManager.DragDropDisposer;
-    private _disposers: { [name: string]: IReactionDisposer } = {};
-    private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
-    @observable private _overlayAnnoInfo: Opt<Doc>;
-    showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
+export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(MapBox, fieldKey);
     }
-    public get SidebarKey() {
-        return this.fieldKey + '_sidebar';
-    }
+    private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
+    private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+    private _sidebarRef = React.createRef<SidebarAnnos>();
+    private _ref: React.RefObject<HTMLDivElement> = React.createRef();
+    private _disposers: { [key: string]: IReactionDisposer } = {};
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
-    @computed get inlineTextAnnotations() {
-        return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
-    }
 
-    @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
-    @observable private selectedPlace: Doc | undefined;
-    @observable private markerMap: { [id: string]: google.maps.Marker } = {};
-    @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
     @observable private _marqueeing: number[] | undefined;
-    @observable private inputRef = React.createRef<HTMLInputElement>();
-    @observable private searchMarkers: google.maps.Marker[] = [];
-    @observable private searchBox = undefined as any; //  new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
+    @computed get inlineTextAnnotations() {
+        return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
+    }
     @computed get allSidebarDocs() {
         return DocListCast(this.dataDoc[this.SidebarKey]);
     }
     @computed get allMapMarkers() {
         return DocListCast(this.dataDoc[this.annotationKey]);
     }
-    @observable private toggleAddMarker = false;
-    private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
-
     @computed get SidebarShown() {
         return this.layoutDoc._layout_showSidebar ? true : false;
     }
+    @computed get sidebarWidthPercent() {
+        return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
+    }
+    @computed get sidebarColor() {
+        return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
+    }
+    @computed get SidebarKey() {
+        return this.fieldKey + '_sidebar';
+    }
 
-    static _canAnnotate = true;
-    static _hadSelection: boolean = false;
-    private _sidebarRef = React.createRef<SidebarAnnos>();
-    private _ref: React.RefObject<HTMLDivElement> = React.createRef();
-    private _disposer: { [key: string]: IReactionDisposer } = {};
     componentDidMount() {
         this.props.setContentView?.(this);
     }
 
     componentWillUnmount(): void {
         this.deselectPin();
-        Object.keys(this._disposer).forEach(key => this._disposer[key]?.());
+        Object.keys(this._disposers).forEach(key => this._disposers[key]?.());
     }
-    // iterate allMarkers to size, center, and zoom map to contain all markers
-    private fitBounds = (map: google.maps.Map) => {
-        const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
-        const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
-        !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds()));
-    };
-
-    /**
-     * Load and render all map markers
-     * @param marker
-     * @param place
-     */
-    @action
-    private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
-        place[Id] ? (this.markerMap[place[Id]] = marker) : null;
-    };
-
-    /**
-     * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true
-     * @param e
-     * @param place
-     */
-    @action
-    private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
-        // set which place was clicked
-        this.selectedPlace = place;
-        // place.infoWindowOpen = true;
-    };
-
-    /**
-     * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list
-     * @param position - the LatLng position where the marker is placed
-     * @param map
-     */
-    @action
-    private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
-        const marker = new google.maps.Marker({
-            position: position,
-            map: map,
-        });
-        map.panTo(position);
-        const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
-        this.addDocument(mapMarker, this.annotationKey);
-    };
-
-    _loadPending = true;
-
-    @action
-    centered = () => {
-        if (this._loadPending && this._map.getBounds()) {
-            this._loadPending = false;
-            this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
-        }
-        this.dataDoc.mapLat = this._map.getCenter()?.lat();
-        this.dataDoc.mapLng = this._map.getCenter()?.lng();
-    };
-
-    @action
-    zoomChanged = () => {
-        if (this._loadPending && this._map.getBounds()) {
-            this._loadPending = false;
-            this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
-        }
-        this.dataDoc.mapZoom = this._map.getZoom();
-    };
-
-    /**
-     * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
-     * add a customized temporary marker on the map
-     */
-    @action
-    private handlePlaceChanged = () => {
-        const place = this.searchBox.getPlace();
-
-        if (!place.geometry || !place.geometry.location) {
-            // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed
-            window.alert("No details available for input: '" + place.name + "'");
-            return;
-        }
-
-        // zoom in on the location of the search result
-        if (place.geometry.viewport) {
-            this._map.fitBounds(place.geometry.viewport);
-        } else {
-            this._map.setCenter(place.geometry.location);
-            this._map.setZoom(17);
-        }
-
-        // customize icon => customized icon for the nature of the location selected
-        const icon = {
-            url: place.icon as string,
-            size: new google.maps.Size(71, 71),
-            origin: new google.maps.Point(0, 0),
-            anchor: new google.maps.Point(17, 34),
-            scaledSize: new google.maps.Size(25, 25),
-        };
-
-        // put temporary cutomized marker on searched location
-        this.searchMarkers.forEach(marker => {
-            marker.setMap(null);
-        });
-        this.searchMarkers = [];
-        this.searchMarkers.push(
-            new window.google.maps.Marker({
-                map: this._map,
-                icon,
-                title: place.name,
-                position: place.geometry.location,
-            })
-        );
-    };
 
     /**
      * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts
@@ -256,7 +109,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * @returns
      */
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
-        console.log('print all sidebar Docs');
         if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         docs.forEach(doc => {
@@ -280,11 +132,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * @param sidebarKey
      * @returns
      */
-    sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
-        // if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
-        const docs = doc instanceof Doc ? [doc] : doc;
-        return this.removeDocument(doc, sidebarKey);
-    };
+    sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => this.removeDocument(doc, sidebarKey);
 
     /**
      * Toggle sidebar onclick the tiny comment button on the top right corner
@@ -317,14 +165,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map')
         );
     };
-
-    sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
-    @computed get layout_sidebarWidthPercent() {
-        return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
-    }
-    @computed get sidebarColor() {
-        return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
-    }
+    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
 
     /**
      * Handles toggle of sidebar on click the little comment button
@@ -349,27 +190,27 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     // TODO: Adding highlight box layer to Maps
     @action
     toggleSidebar = () => {
-        //1.2 * w * ? = .2 * w      .2/1.2
         const prevWidth = this.sidebarWidth();
         this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
         this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
     };
 
     createNoteAnnotation = () => {
-        !this.layoutDoc.layout_showSidebar && this.toggleSidebar();
-
-        setTimeout(
-            undoable(
-                action(() => {
-                    const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false));
-                    if (note && this.selectedPin) {
-                        note.latitude = this.selectedPin.latitude;
-                        note.longitude = this.selectedPin.latitude;
-                    }
-                }),
-                'create note annotation'
-            )
+        const createFunc = undoable(
+            action(() => {
+                const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false));
+                if (note && this.selectedPin) {
+                    note.latitude = this.selectedPin.latitude;
+                    note.longitude = this.selectedPin.longitude;
+                    note.map = this.selectedPin.map;
+                }
+            }),
+            'create note annotation'
         );
+        if (!this.layoutDoc.layout_showSidebar) {
+            this.toggleSidebar();
+            setTimeout(createFunc);
+        } else createFunc();
     };
     sidebarDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
@@ -406,37 +247,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false);
     };
 
-    addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
-        return this.addDocument(doc, annotationKey);
-    };
+    addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => this.addDocument(doc, annotationKey);
 
     pointerEvents = () => (this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none');
 
-    @computed get annotationLayer() {
-        return (
-            <div className="mapBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
-                {this.inlineTextAnnotations
-                    .sort((a, b) => NumCast(a.y) - NumCast(b.y))
-                    .map(anno => (
-                        <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />
-                    ))}
-            </div>
-        );
-    }
-
-    // Old get anchor function
-    // getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc;
-
-    /**
-     * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
-     * @returns
-     */
-    private renderMarkers = () => {
-        return this.allMapMarkers.map(place => (
-            <Marker key={place[Id]} position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }} onLoad={marker => this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} />
-        ));
-    };
-
     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
@@ -473,25 +287,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             } else {
                 this._bingSearchManager.geocode({
                     where: query,
-                    callback: action((r: any) => {
-                        res(r.results[0].location);
-                    }),
+                    callback: action((r: any) => res(r.results[0].location)),
                     errorCallback: (e: any) => reject(),
                 });
             }
         });
     };
 
-    /**
-     *
-     *
-     *  ERIC'S BING MAP CODE BELOW
-     *
-     *
-     *
-     **/
     @observable
-    bingSearchBarContents: any = 'enter city/zip/...'; // For Bing Maps: The contents of the Bing search bar (string)
+    bingSearchBarContents: any = this.rootDoc.map; // For Bing Maps: The contents of the Bing search bar (string)
 
     geoDataRequestOptions = {
         entityType: 'PopulatedPlace',
@@ -502,14 +306,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Creates Pushpin doc and adds it to the list of annotations
      */
     @action
-    createPushpin = undoable((latitude: number, longitude: number) => {
+    createPushpin = undoable((latitude: number, longitude: number, map?: string) => {
         // Stores the pushpin as a MapMarkerDocument
         const mapMarker = Docs.Create.PushpinDocument(
             NumCast(latitude),
             NumCast(longitude),
             false,
             [],
-            {}
+            { map: map }
             // ,'pushpinIDamongus'+ this.incrementer++
         );
         this.addDocument(mapMarker, this.annotationKey);
@@ -517,18 +321,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // mapMarker.infoWindowOpen = true;
     }, 'createpin');
 
-    /*
-     * Pushpin dblclick
-     */
-    // @action
-    // pushpinDblClicked = (pin: any, pinDoc?: Doc) => {
-    //     if (pinDoc) this.removePushpin(pinDoc);
-    //     else this._bingMap.current.entities.remove(pin);
-    // };
-
     // The pin that is selected
-    @observable
-    selectedPin: Doc | undefined;
+    @observable selectedPin: Doc | undefined;
 
     @action
     deselectPin = () => {
@@ -544,6 +338,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this._bingMap.current.entities.push(newpin);
             this.map_docToPinMap.set(temp, newpin);
             this.selectedPin = undefined;
+            this.bingSearchBarContents = this.rootDoc.map;
         }
     };
 
@@ -558,9 +353,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     pushpinClicked = (pinDoc: Doc) => {
         this.deselectPin();
         this.selectedPin = pinDoc;
+        this.bingSearchBarContents = pinDoc.map;
 
         Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
-        Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.latitude, 'match');
+        Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match');
 
         this.recolorPin(this.selectedPin, 'green');
 
@@ -575,7 +371,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         MapAnchorMenu.Instance.jumpTo(cpt[0], cpt[1], true);
 
         document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
-        // Filter sidebar:
     };
 
     /**
@@ -589,54 +384,47 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * Map OnClick
      */
     @action
-    mapOnClick = (e: { location: { latitude: any; longitude: any } }) => this.deselectPin();
+    mapOnClick = (e: { location: { latitude: any; longitude: any } }) => {
+        this.props.select(false);
+        this.deselectPin();
+    };
     /*
      * Updates values of layout doc to match the current map
      */
     @action
-    updateLayout = () => {
-        this.dataDoc.latitude = this._bingMap.current.getCenter().latitude;
-        this.dataDoc.longitude = this._bingMap.current.getCenter().longitude;
-        this.dataDoc.mapZoom = this._bingMap.current.getZoom();
-        // if(this.dataDoc.mapType == 'x'){
-        //     this.dataDoc.locationToLookAt
-        // }
-        // this.dataDoc.mapType = new this.MicrosoftMaps.MapTypeId();
+    mapRecentered = () => {
+        if (
+            Math.abs(NumCast(this.dataDoc.latitude) - this._bingMap.current.getCenter().latitude) > 1e-7 || //
+            Math.abs(NumCast(this.dataDoc.longitude) - this._bingMap.current.getCenter().longitude) > 1e-7
+        ) {
+            this.dataDoc.latitude = this._bingMap.current.getCenter().latitude;
+            this.dataDoc.longitude = this._bingMap.current.getCenter().longitude;
+            this.dataDoc.map = '';
+            this.bingSearchBarContents = '';
+        }
+        this.dataDoc.map_zoom = this._bingMap.current.getZoom();
     };
     /*
      * Updates maptype
      */
     @action
-    updateMapType = () => {
-        this.dataDoc.mapType = this._bingMap.current.getMapTypeId();
-    };
+    updateMapType = () => (this.dataDoc.map_type = this._bingMap.current.getMapTypeId());
 
-    searched_pin: any;
     /*
      * For Bing Maps
      * Called by search button's onClick
      * Finds the geocode of the searched contents and sets location to that location
      **/
     @action
-    bingSearch = async () => {
-        const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
-        this.dataDoc.latitude = location.latitude;
-        this.dataDoc.longitude = location.longitude;
-        this.dataDoc.mapZoom = this._bingMap.current.getZoom();
-        // Creates a temporary pin but does not add it to the dataDoc
-        this.createPushpin(this.dataDoc.latitude, this.dataDoc.longitude);
+    bingSearch = () => {
+        return this.bingGeocode(this._bingMap, this.bingSearchBarContents).then(location => {
+            this.dataDoc.latitude = location.latitude;
+            this.dataDoc.longitude = location.longitude;
+            this.dataDoc.map_zoom = this._bingMap.current.getZoom();
+            this.dataDoc.map = this.bingSearchBarContents;
+        });
     };
 
-    /**
-     * Adds all pushpins in dataDoc onto the map (render) - OLD & UNUSED
-     */
-    // @action
-    // addAllPins = () => {
-    //     this._bingMap.current.entities.clear();
-    //     if (this.searched_pin) this._bingMap.current.entities.push(this.searched_pin);
-    //     // this.allMapPushpins.map(pin => this.addPushpin(pin));
-    // };
-
     /*
      * Returns doc w/ relevant info
      */
@@ -644,12 +432,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
         const anchor = Docs.Create.MapanchorDocument({
             title: 'MapAnchor:' + this.rootDoc.title,
+            text: StrCast(this.selectedPin?.map) || StrCast(this.rootDoc.map) || 'map location',
             config_latitude: NumCast(this.selectedPin?.latitude ?? this.dataDoc.latitude),
             config_longitude: NumCast(this.selectedPin?.longitude ?? this.dataDoc.longitude),
-            config_mapZoom: NumCast(this.dataDoc.mapZoom),
-            config_mapType: StrCast(this.dataDoc.mapType),
-            // preslocationToLookAt:this.dataDoc.locationToLookAt,
-            // presType:
+            config_map_zoom: NumCast(this.dataDoc.map_zoom),
+            config_map_type: StrCast(this.dataDoc.map_type),
+            config_map: StrCast(this.selectedPin?.map) || StrCast(this.dataDoc.map),
             layout_unrendered: true,
         });
         if (anchor) {
@@ -684,23 +472,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.map_docToPinMap.set(pin, pushPin);
     };
 
-    @observable
-    pinIsSelected_TEMPORARY: boolean = false; // toggles if remove pin button appears
-
     /*
      *   Input: pin doc
      *   Removes pin from annotations
      */
     @action
-    removePushpin = (pinDoc: Doc) => {
-        // this.allMapPushpins
-        // this.allMapPushpins.map(pin => this.addPushpin(pin));
-        // this._bingMap.current.entities.clear();
+    removePushpin = (pinDoc: Doc) => this.removeDocument(pinDoc, this.annotationKey);
 
-        this.removeDocument(pinDoc, this.annotationKey);
-
-        // this.dataDoc[this.annotationKey]
-    };
     /*
      * Removes pushpin from map render
      */
@@ -725,11 +503,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     tryHideMapAnchorMenu = (e: PointerEvent) => {
         let target = document.elementFromPoint(e.x, e.y);
-
-        while (target != null) {
-            if (target === MapAnchorMenu.top.current) {
-                return;
-            }
+        while (target) {
+            if (target === MapAnchorMenu.top.current) return;
             target = target.parentElement;
         }
         e.stopPropagation();
@@ -738,13 +513,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
     };
 
-    // tryhidemenu = e => if( e.parent... == mapanchormenu.top.currrent) do nothing; else hide menu
-
     @action
     centerOnSelectedPin = () => {
         if (this.selectedPin) {
             this.dataDoc.latitude = this.selectedPin.latitude;
             this.dataDoc.longitude = this.selectedPin.longitude;
+            this.dataDoc.map = this.selectedPin.map ?? '';
+            this.bingSearchBarContents = this.selectedPin.map;
         }
         MapAnchorMenu.Instance.fadeOut(true);
         document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu);
@@ -777,33 +552,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         },
     };
 
-    /**
-     * Toggles mode for placing a pin down on map
-     */
-    @observable
-    placePinOn = false;
-    @action
-    togglePlacePin = () => {
-        if (this.placePinOn) this.placePinOn = false;
-        else this.placePinOn = true;
-    };
-
     @action
-    searchbarOnEdit = (newText: string) => {
-        this.bingSearchBarContents = newText;
-    };
+    searchbarOnEdit = (newText: string) => (this.bingSearchBarContents = newText);
 
     recolorPin = (pin: Doc, color?: string) => {
         this._bingMap.current.entities.remove(this.map_docToPinMap.get(pin));
         this.map_docToPinMap.delete(pin);
-        const newpin = new this.MicrosoftMaps.Pushpin(
-            new this.MicrosoftMaps.Location(pin.latitude, pin.longitude),
-            color
-                ? {
-                      color,
-                  }
-                : {}
-        );
+        const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.latitude, pin.longitude), color ? { color } : {});
         this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(pin));
         this._bingMap.current.entities.push(newpin);
         this.map_docToPinMap.set(pin, newpin);
@@ -822,29 +577,36 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             alert('NO Map!?');
         }
         this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.updateLayout, 'Map Layout Change'));
+        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.mapRecentered, 'Map Layout Change'));
         this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change'));
-        this.updateLayout();
-        this._disposer.highlight = reaction(
+
+        this._disposers.mapLocation = reaction(
+            () => this.rootDoc.map,
+            mapLoc => (this.bingSearchBarContents = mapLoc),
+            { fireImmediately: true }
+        );
+        this._disposers.highlight = reaction(
             () => this.allMapPushpins.map(doc => doc[Highlight]),
-            () =>
-                this.allMapPushpins
-                    .map(doc => ({ doc, pushpin: DocCast(doc.mapPin) }))
-                    .filter(pair => pair.pushpin)
-                    .forEach(({ doc, pushpin }) => {
-                        if (doc[Highlight] && !this.map_pinHighlighted.get(pushpin)) {
-                            this.recolorPin(pushpin, 'orange');
-                            this.map_pinHighlighted.set(pushpin, true);
-                        } else if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) {
-                            this.recolorPin(pushpin);
-                            this.map_pinHighlighted.delete(pushpin);
-                        }
-                    }),
+            () => {
+                const allPins = this.allMapPushpins.map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })).filter(pair => pair.pushpin);
+                allPins.forEach(({ doc, pushpin }) => {
+                    if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) {
+                        this.recolorPin(pushpin);
+                        this.map_pinHighlighted.delete(pushpin);
+                    }
+                });
+                allPins.forEach(({ doc, pushpin }) => {
+                    if (doc[Highlight] && !this.map_pinHighlighted.get(pushpin)) {
+                        this.recolorPin(pushpin, 'orange');
+                        this.map_pinHighlighted.set(pushpin, true);
+                    }
+                });
+            },
             { fireImmediately: true }
         );
-        // this.updateMapType();
-        this._disposer.location = reaction(
-            () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }),
+
+        this._disposers.location = reaction(
+            () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.map_zoom, mapType: this.rootDoc.map_type }),
             locationObject => {
                 // if (this._bingMap.current)
                 try {
@@ -862,43 +624,50 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
     };
 
-    // Keeps track of when dragging a pin onto map
-    draggingPin = false;
     dragToggle = (e: React.PointerEvent) => {
-        // console.log('DRAGGING TOGGLE');
-        document.addEventListener('drop', this.dropPin, true);
-        document.addEventListener('pointermove', this.pinMove, true);
-        e.stopPropagation();
-    };
-    pinMove = (e: PointerEvent) => {
-        // console.log('MOVING');
-        e.stopPropagation();
-    };
-    dropPin = (e: DragEvent) => {
-        e.stopPropagation();
-        e.preventDefault();
-        document.removeEventListener('drop', this.dropPin, true);
-        document.removeEventListener('pointermove', this.pinMove, true);
-        let target = document.elementFromPoint(e.x, e.y);
+        let dragClone: HTMLDivElement | undefined;
 
-        while (target != null) {
-            if (target === this._ref.current) {
-                const cpt = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
-                const x = cpt[0] - (this.props.PanelWidth() - this.sidebarWidth()) / 2;
-                const y = cpt[1] - 32 /* height of search bar */ - this.props.PanelHeight() / 2;
-                const location = this._bingMap.current.tryPixelToLocation(new this.MicrosoftMaps.Point(x, y));
-                this.createPushpin(location.latitude, location.longitude);
-                break;
+        setupMoveUpEvents(
+            e,
+            e,
+            e => {
+                if (!dragClone) {
+                    dragClone = this._dragRef.current?.cloneNode(true) as HTMLDivElement;
+                    dragClone.style.position = 'absolute';
+                    dragClone.style.zIndex = '10000';
+                    DragManager.Root().appendChild(dragClone);
+                }
+                dragClone.style.transform = `translate(${e.clientX - 15}px, ${e.clientY - 15}px)`;
+                return false;
+            },
+            e => {
+                if (!dragClone) return;
+                DragManager.Root().removeChild(dragClone);
+                let target = document.elementFromPoint(e.x, e.y);
+                while (target) {
+                    if (target === this._ref.current) {
+                        const cpt = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
+                        const x = cpt[0] - (this.props.PanelWidth() - this.sidebarWidth()) / 2;
+                        const y = cpt[1] - 32 /* height of search bar */ - this.props.PanelHeight() / 2;
+                        const location = this._bingMap.current.tryPixelToLocation(new this.MicrosoftMaps.Point(x, y));
+                        this.createPushpin(location.latitude, location.longitude);
+                        break;
+                    }
+                    target = target.parentElement;
+                }
+            },
+            e => {
+                const createPin = () => this.createPushpin(this.rootDoc.latitude, this.rootDoc.longitude, this.rootDoc.map);
+                if (this.bingSearchBarContents) {
+                    this.bingSearch().then(createPin);
+                } else createPin();
             }
-            target = target.parentElement;
-        }
+        );
     };
 
-    searchbarKeyDown = (e: any) => {
-        if (e.key === 'Enter') {
-            this.bingSearch();
-        }
-    };
+    searchbarKeyDown = (e: any) => e.key === 'Enter' && this.bingSearch();
+
+    _dragRef = React.createRef<HTMLDivElement>();
     render() {
         const renderAnnotations = (childFilters?: () => string[]) => null;
         return (
@@ -908,26 +677,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     onWheel={e => e.stopPropagation()}
                     onPointerDown={async e => {
                         e.button === 0 && !e.ctrlKey && e.stopPropagation();
-                        // just a simple test of bing maps geocode api
-                        // const loc = await this.bingGeocode(this._bingMap, 'Philadelphia, PA');
-                        // this._bingMap.current.setView({
-                        //     mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
-                        //     center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
-                        //     zoom: 15,
-                        // });
                     }}
-                    style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
+                    style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
                     <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                     {renderAnnotations(this.opaqueFilter)}
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
-                    {this.annotationLayer}
 
                     <div className="mapBox-searchbar">
                         <EditableText
                             // editing
                             setVal={(newText: string | number) => typeof newText === 'string' && this.searchbarOnEdit(newText)}
                             onEnter={e => this.bingSearch()}
-                            placeholder={this.bingSearchBarContents}
+                            placeholder={this.bingSearchBarContents || 'enter city/zip/...'}
                             textAlign="center"
                         />
                         <IconButton
@@ -939,41 +700,34 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 </svg>
                             }
                             onClick={this.bingSearch}
-                            onDoubleClick={function noRefCheck() {}}
-                            onPointerDown={function noRefCheck() {}}
-                            onPointerDownCapture={function noRefCheck() {}}
-                            onPointerMove={function noRefCheck() {}}
-                            onPointerMoveCapture={function noRefCheck() {}}
-                            onPointerUp={function noRefCheck() {}}
                             type={Type.TERT}
                         />
-                        <div draggable={true} style={{ width: 30, height: 30 }} onPointerDown={this.dragToggle}>
+                        <div style={{ width: 30, height: 30 }} ref={this._dragRef} onPointerDown={this.dragToggle}>
                             <Button tooltip="drag to place a pushpin" icon={<FontAwesomeIcon size={'lg'} icon={'bullseye'} />} />
                         </div>
                     </div>
 
                     <BingMapsReact
-                        onMapReady={this.bingMapReady}
+                        onMapReady={this.bingMapReady} //
                         bingMapsKey={bingApiKey}
                         height="100%"
                         mapOptions={this.bingMapOptions}
                         width="100%"
                         viewOptions={this.bingViewOptions}
-                        onPointerUp
-                        {...() => (this.draggingPin = false)}></BingMapsReact>
+                    />
                     <div>
                         {!this._mapReady
                             ? null
                             : this.allMapPushpins.map(pushpin => (
                                   <DocumentView
                                       {...this.props}
+                                      renderDepth={this.props.renderDepth + 1}
                                       Document={pushpin}
                                       DataDoc={undefined}
                                       PanelWidth={returnOne}
                                       PanelHeight={returnOne}
                                       NativeWidth={returnOne}
                                       NativeHeight={returnOne}
-                                      onClick={() => new ScriptField(undefined)}
                                       onKey={undefined}
                                       onDoubleClick={undefined}
                                       onBrowseClick={undefined}
@@ -983,7 +737,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                       isDocumentActive={returnFalse}
                                       isContentActive={returnFalse}
                                       addDocTab={returnFalse}
-                                      ScreenToLocalTransform={() => new Transform(0, 0, 0)}
+                                      ScreenToLocalTransform={Transform.Identity}
                                       fitContentsToBox={undefined}
                                       focus={returnOne}
                                   />
@@ -1019,7 +773,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     )}
                 </div>
                 {/* </LoadScript > */}
-                <div className="mapBox-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+                <div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
                         {...this.props}
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index f42dec12c..a54bdcd5e 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -260,7 +260,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // } else {
         //     alert("Your geolocation is not supported by browser.")
         // };
-        map.setZoom(NumCast(this.dataDoc.mapZoom, 2.5));
+        map.setZoom(NumCast(this.dataDoc.map_zoom, 2.5));
         map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
         setTimeout(() => {
             if (this._loadPending && this._map.getBounds()) {
@@ -292,7 +292,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this._loadPending = false;
             this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
         }
-        this.dataDoc.mapZoom = this._map.getZoom();
+        this.dataDoc.map_zoom = this._map.getZoom();
     };
 
     /**
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index afd9bccab..6ff22e929 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -471,12 +471,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 Doc.SetInPlace(bestTarget, 'longitude', NumCast(activeItem.config_longitude), true);
                 changed = true;
             }
-            if (bestTarget.zoom !== activeItem.config_mapZoom) {
-                Doc.SetInPlace(bestTarget, 'mapZoom', NumCast(activeItem.config_mapZoom), true);
+            if (bestTarget.zoom !== activeItem.config_map_zoom) {
+                Doc.SetInPlace(bestTarget, 'map_zoom', NumCast(activeItem.config_map_zoom), true);
                 changed = true;
             }
-            if (bestTarget.mapType !== activeItem.config_mapType) {
-                Doc.SetInPlace(bestTarget, 'mapType', StrCast(activeItem.config_mapType), true);
+            if (bestTarget.map_type !== activeItem.config_map_type) {
+                Doc.SetInPlace(bestTarget, 'map_type', StrCast(activeItem.config_map_type), true);
+                changed = true;
+            }
+            if (bestTarget.map !== activeItem.config_map) {
+                Doc.SetInPlace(bestTarget, 'map', StrCast(activeItem.config_map), true);
                 changed = true;
             }
         }
@@ -663,8 +667,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (pinProps.pinData.map) {
                 // pinDoc.config_latitude = targetDoc?.latitude;
                 // pinDoc.config_longitude = targetDoc?.longitude;
-                pinDoc.config_mapZoom = targetDoc?.mapZoom;
-                pinDoc.config_mapType = targetDoc?.mapType;
+                pinDoc.config_map_zoom = targetDoc?.map_zoom;
+                pinDoc.config_map_type = targetDoc?.map_type;
                 //...
             }
             if (pinProps.pinData.poslayoutview)
-- 
cgit v1.2.3-70-g09d2


From a142ff5e6ce017686e9bf418c502417d8cea5cad Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 26 Aug 2023 22:48:39 -0400
Subject: fixed map anchors to be configs, not maps! fixed doc decorations to
 not allow clicking on them when showNothing is set. increased font size of
 map search.

---
 src/client/documents/Documents.ts         |  3 ---
 src/client/views/DocumentDecorations.tsx  |  8 +++++---
 src/client/views/nodes/MapBox/MapBox.scss |  1 +
 src/client/views/nodes/MapBox/MapBox.tsx  | 25 ++++++++++++++++++++++---
 4 files changed, 28 insertions(+), 9 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d5d6fb2ba..9ae55d7f9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1108,9 +1108,6 @@ export namespace Docs {
                 id
             );
         }
-        export function MapanchorDocument(options: DocumentOptions = {}, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.MAP), options?.data, options, id);
-        }
 
         export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _type_collection: CollectionViewType.Linear }, id);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index f3daf3ffa..90425f264 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -80,8 +80,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             // show decorations whenever pointer moves outside of selection bounds.
             'pointermove',
             action(e => {
-                if (this.Bounds.x !== Number.MAX_VALUE && (this.Bounds.x > e.clientX || this.Bounds.r < e.clientX || this.Bounds.y > e.clientY || this.Bounds.b < e.clientY)) {
-                    this._showNothing = false;
+                if (this.Bounds.x || this.Bounds.y || this.Bounds.r || this.Bounds.b) {
+                    if (this.Bounds.x !== Number.MAX_VALUE && (this.Bounds.x > e.clientX || this.Bounds.r < e.clientX || this.Bounds.y > e.clientY || this.Bounds.b < e.clientY)) {
+                        this._showNothing = false;
+                    }
                 }
             })
         );
@@ -885,7 +887,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         );
 
         return (
-            <div className={`documentDecorations${colorScheme}`} style={{ opacity: this._showNothing ? 0.1 : undefined }}>
+            <div className={`documentDecorations${colorScheme}`} style={{ display: this._showNothing ? 'none' : undefined }}>
                 <div
                     className="documentDecorations-background"
                     style={{
diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index 464646a23..242677231 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -17,6 +17,7 @@
         width: calc(100% - 40px);
         .editableText-container {
             width: 100%;
+            font-size: 16px !important;
         }
         input {
             width: 100%;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 94944b83d..284e598c5 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -5,7 +5,7 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
-import { Highlight, Width } from '../../../../fields/DocSymbols';
+import { DocCss, Highlight, Width } from '../../../../fields/DocSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from '../../../../Utils';
@@ -62,6 +62,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(MapBox, fieldKey);
     }
+    private _dragRef = React.createRef<HTMLDivElement>();
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _sidebarRef = React.createRef<SidebarAnnos>();
@@ -430,7 +431,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      */
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
-        const anchor = Docs.Create.MapanchorDocument({
+        const anchor = Docs.Create.ConfigDocument({
             title: 'MapAnchor:' + this.rootDoc.title,
             text: StrCast(this.selectedPin?.map) || StrCast(this.rootDoc.map) || 'map location',
             config_latitude: NumCast(this.selectedPin?.latitude ?? this.dataDoc.latitude),
@@ -667,8 +668,26 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     searchbarKeyDown = (e: any) => e.key === 'Enter' && this.bingSearch();
 
-    _dragRef = React.createRef<HTMLDivElement>();
+    static _firstRender = true;
+    static _rerenderDelay = 0;
+    _rerenderTimeout: any;
     render() {
+        // bcz:  no idea what's going on here, but bings maps have some kind of bug
+        // such that we need to delay rendering a second map on startup until the first map is rendered.
+        this.rootDoc[DocCss];
+        if (MapBox._firstRender) {
+            MapBox._firstRender = false;
+            MapBox._rerenderDelay = 500;
+        } else if (MapBox._rerenderDelay) {
+            // prettier-ignore
+            this._rerenderTimeout = this._rerenderTimeout ??
+                setTimeout(action(() => {
+                    MapBox._rerenderDelay = 0;
+                    this.rootDoc[DocCss] = this.rootDoc[DocCss] + 1;
+                }), MapBox._rerenderDelay);
+            return null;
+        }
+
         const renderAnnotations = (childFilters?: () => string[]) => null;
         return (
             <div className="mapBox" ref={this._ref}>
-- 
cgit v1.2.3-70-g09d2


From bf1777b93be0707e17e3b3c0ca6c965facebfe14 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 27 Aug 2023 02:50:01 -0400
Subject: fixed filters to filter by linkedTo instead of lat/lng.  made links
 to pushpins when anything is added to sidebar and pushpin is selected

---
 src/client/documents/Documents.ts        |  32 +++++---
 src/client/views/MainView.tsx            |  40 +++++++++-
 src/client/views/SidebarAnnos.tsx        |   8 +-
 src/client/views/nodes/MapBox/MapBox.tsx | 127 ++++++++++++++++---------------
 src/client/views/pdf/PDFViewer.tsx       |   2 +-
 src/fields/Doc.ts                        |   6 +-
 src/fields/documentSchemas.ts            |  11 ++-
 7 files changed, 140 insertions(+), 86 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index add12896e..ceee8c76f 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1075,8 +1075,8 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.MAP), new List(documents), options);
         }
 
-        export function PushpinDocument(lat: number, lng: number, infoWindowOpen: boolean, documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.PUSHPIN), new List(documents), { latitude: lat, longitude: lng, infoWindowOpen, ...options }, id);
+        export function PushpinDocument(latitude: number, longitude: number, infoWindowOpen: boolean, documents: Array<Doc>, options: DocumentOptions, id?: string) {
+            return InstanceFromProto(Prototypes.get(DocumentType.PUSHPIN), new List(documents), { latitude, longitude, infoWindowOpen, ...options }, id);
         }
 
         // shouldn't ever need to create a KVP document-- instead set the LayoutTemplateString to be a KeyValueBox for the DocumentView (see addDocTab in TabDocView)
@@ -1097,9 +1097,6 @@ export namespace Docs {
         export function HTMLMarkerDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _type_collection: CollectionViewType.Freeform }, id);
         }
-        export function MapMarkerDocument(lat: number, lng: number, infoWindowOpen: boolean, documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { latitude: lat, longitude: lng, infoWindowOpen, ...options, _type_collection: CollectionViewType.Freeform }, id);
-        }
 
         export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
             return InstanceFromProto(
@@ -1274,10 +1271,23 @@ export namespace DocUtils {
                   if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) {
                       return false;
                   }
-
                   for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== Utils.noDragsDocFilter.split(Doc.FilterSep)[0])) {
                       const facet = filterFacets[facetKey];
 
+                      let links = true;
+                      const linkedTo = filterFacets['-linkedTo'] && Array.from(Object.keys(filterFacets['-linkedTo']))?.[0];
+                      const linkedToField = filterFacets['-linkedTo']?.[linkedTo];
+                      const allLinks = linkedTo && linkedToField ? LinkManager.Instance.getAllRelatedLinks(d) : [];
+                      // prettier-ignore
+                      if (linkedTo) {
+                          if (allLinks.some(d => linkedTo === Field.toScriptString(DocCast(DocCast(d.link_anchor_1)?.[linkedToField]))) || //
+                           allLinks.some(d => linkedTo === Field.toScriptString(DocCast(DocCast(d.link_anchor_2)?.[linkedToField]))))
+                          {
+                              links = true;
+                          } 
+                          else links = false
+                      }
+
                       // facets that match some value in the field of the document (e.g. some text field)
                       const matches = Object.keys(facet).filter(value => value !== 'cookies' && facet[value] === 'match');
 
@@ -1293,7 +1303,7 @@ export namespace DocUtils {
                       // facets that have an x next to them
                       const xs = Object.keys(facet).filter(value => facet[value] === 'x');
 
-                      if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true;
+                      if (!linkedTo && !unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true;
                       const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value));
                       const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value));
                       const satisfiesExistsFacets = !exists.length ? true : exists.some(value => d[facetKey] !== undefined);
@@ -1312,11 +1322,11 @@ export namespace DocUtils {
                             });
                       // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria
                       if (parentCollection?.childFilters_boolean === 'OR') {
-                          if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
+                          if (links && satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
                       }
                       // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria
                       else {
-                          if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
+                          if (!links || !satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
                       }
                   }
                   return parentCollection?.childFilters_boolean === 'OR' ? false : true;
@@ -1800,8 +1810,8 @@ export namespace DocUtils {
                 const longitude = result.exifData?.data?.GPSLongitude;
                 const longitudeDirection = result.exifData?.data?.GPSLongitudeRef;
                 if (latitude !== undefined && longitude !== undefined && latitudeDirection !== undefined && longitudeDirection !== undefined) {
-                    proto.lat = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection);
-                    proto.lng = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection);
+                    proto.latitude = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection);
+                    proto.longitude = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection);
                 }
             }
             if (Upload.isVideoInformation(result)) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 344a401f3..2a9f0cc02 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -60,6 +60,7 @@ import { ImageBox } from './nodes/ImageBox';
 import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
 import { LinkDocPreview } from './nodes/LinkDocPreview';
 import { MapAnchorMenu } from './nodes/MapBox/MapAnchorMenu';
+import { MapBox } from './nodes/MapBox/MapBox';
 import { RadialMenu } from './nodes/RadialMenu';
 import { TaskCompletionBox } from './nodes/TaskCompletedBox';
 import { OverlayView } from './OverlayView';
@@ -956,6 +957,42 @@ export class MainView extends React.Component {
     @computed get linkDocPreview() {
         return LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : null;
     }
+    @observable mapBoxHackBool = false;
+    @computed get mapBoxHack() {
+        return this.mapBoxHackBool ? null : (
+            <MapBox
+                ref={action((r: any) => r && (this.mapBoxHackBool = true))}
+                fieldKey="data"
+                select={returnFalse}
+                isSelected={returnFalse}
+                Document={this.headerBarDoc}
+                DataDoc={undefined}
+                addDocTab={returnFalse}
+                pinToPres={emptyFunction}
+                docViewPath={returnEmptyDoclist}
+                styleProvider={DefaultStyleProvider}
+                rootSelected={returnTrue}
+                addDocument={returnFalse}
+                removeDocument={returnFalse}
+                fitContentsToBox={returnTrue}
+                isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
+                isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
+                ScreenToLocalTransform={Transform.Identity}
+                childHideResizeHandles={returnTrue}
+                childDragAction="move"
+                dontRegisterView={true}
+                PanelWidth={this.headerBarDocWidth}
+                PanelHeight={this.headerBarDocHeight}
+                renderDepth={0}
+                focus={emptyFunction}
+                whenChildContentsActiveChanged={emptyFunction}
+                bringToFront={emptyFunction}
+                childFilters={returnEmptyFilter}
+                childFiltersByRanges={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+            />
+        );
+    }
 
     render() {
         return (
@@ -1011,7 +1048,7 @@ export class MainView extends React.Component {
                 <ContextMenu />
                 <RadialMenu />
                 <AnchorMenu />
-                <MapAnchorMenu/>
+                <MapAnchorMenu />
                 <DashFieldViewMenu />
                 <MarqueeOptionsMenu />
                 <TimelineMenu />
@@ -1020,6 +1057,7 @@ export class MainView extends React.Component {
                 {this.snapLines}
                 <LightboxView key="lightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
                 <OverlayView />
+                {this.mapBoxHack}
                 <GPTPopup key="gptpopup" />
                 <GenerativeFill imageEditorOpen={ImageBox.imageEditorOpen} imageEditorSource={ImageBox.imageEditorSource} imageRootDoc={ImageBox.imageRootDoc} addDoc={ImageBox.addDoc} />
                 {/* <NewLightboxView key="newLightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> */}
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 520485a71..c48a7241a 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -66,7 +66,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         return '_' + this.sidebarKey + '_childFilters';
     }
 
-    anchorMenuClick = (anchor: Doc) => {
+    anchorMenuClick = (anchor: Doc, filterExlusions?: string[]) => {
         const startup = StrListCast(this.props.rootDoc.childFilters)
             .map(filter => filter.split(':')[0])
             .join(' ');
@@ -79,6 +79,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
             _layout_autoHeight: true,
             _text_fontSize: StrCast(Doc.UserDoc().fontSize),
             _text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
+            target: 'HELLO' as any,
         });
         FormattedTextBox.SelectOnLoad = target[Id];
         FormattedTextBox.DontSelectInitialText = true;
@@ -87,7 +88,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
 
         const taggedContent = this.childFilters()
             .filter(data => data.split(':')[0])
-            .filter(data => data.split(':')[0] !== 'latitude' && data.split(':')[0] !== 'longitude')
+            .filter(data => !filterExlusions?.includes(data.split(':')[0]))
             .map(data => {
                 const key = data.split(':')[0];
                 const val = Field.Copy(this.allMetadata.get(key));
@@ -188,7 +189,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     };
     render() {
         const renderTag = (tag: string) => {
-            const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`tags:${tag}:check`);
+            const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`tags::${tag}::check`);
             return (
                 <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, 'tags', tag, 'check', true, this.sidebarKey, e.shiftKey)}>
                     {tag}
@@ -231,7 +232,6 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         .map(key => renderMeta(key, this.allMetadata.get(key)))} */}
                 </div>
 
-
                 <div style={{ width: '100%', height: `calc(100% - 38px)`, position: 'relative' }}>
                     <CollectionStackingView
                         {...this.props}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 284e598c5..6f552953d 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -4,14 +4,16 @@ import { Button, EditableText, IconButton, Type } from 'browndash-components';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
 import { DocCss, Highlight, Width } from '../../../../fields/DocSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { Docs } from '../../../documents/Documents';
+import { Docs, DocUtils } from '../../../documents/Documents';
+import { DocumentType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
+import { LinkManager } from '../../../util/LinkManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoable, UndoManager } from '../../../util/UndoManager';
@@ -72,15 +74,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @observable private _marqueeing: number[] | undefined;
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
-    @computed get inlineTextAnnotations() {
-        return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
-    }
     @computed get allSidebarDocs() {
         return DocListCast(this.dataDoc[this.SidebarKey]);
     }
-    @computed get allMapMarkers() {
+    // this list contains pushpins and configs
+    @computed get allAnnotations() {
         return DocListCast(this.dataDoc[this.annotationKey]);
     }
+    @computed get allPushpins() {
+        return this.allAnnotations.filter(anno => anno.type === DocumentType.PUSHPIN);
+    }
     @computed get SidebarShown() {
         return this.layoutDoc._layout_showSidebar ? true : false;
     }
@@ -113,14 +116,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         docs.forEach(doc => {
-            if (doc.lat !== undefined && doc.lng !== undefined) {
-                const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng);
-                if (existingMarker) {
-                    Doc.AddDocToList(existingMarker, 'data', doc);
-                } else {
-                    const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
-                    this.addDocument(marker, this.annotationKey);
-                }
+            let existingPin = this.allPushpins.find(pin => pin.latitude === doc.latitude && pin.longitude === doc.longitude) ?? this.selectedPin;
+            if (doc.latitude !== undefined && doc.longitude !== undefined && !existingPin) {
+                existingPin = this.createPushpin(NumCast(doc.latitude), NumCast(doc.longitude), StrCast(doc.map));
+            }
+            if (existingPin) {
+                setTimeout(() => {
+                    // we use a timeout in case this is called from the sidebar which may have just added a link that hasn't made its way into th elink manager yet
+                    if (!LinkManager.Instance.getAllRelatedLinks(doc).some(link => DocCast(link.link_anchor_1)?.mapPin === existingPin || DocCast(link.link_anchor_2)?.mapPin === existingPin)) {
+                        const anchor = this.getAnchor(true, undefined, existingPin);
+                        anchor && DocUtils.MakeLink(anchor, doc, { link_relationship: 'link to map location' });
+                        doc.latitude = existingPin?.latitude;
+                        doc.longitude = existingPin?.longitude;
+                    }
+                });
             }
         }); //add to annotation list
 
@@ -199,7 +208,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     createNoteAnnotation = () => {
         const createFunc = undoable(
             action(() => {
-                const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false));
+                const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false), ['latitude', 'longitude', '-linkedTo']);
                 if (note && this.selectedPin) {
                     note.latitude = this.selectedPin.latitude;
                     note.longitude = this.selectedPin.longitude;
@@ -309,7 +318,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     createPushpin = undoable((latitude: number, longitude: number, map?: string) => {
         // Stores the pushpin as a MapMarkerDocument
-        const mapMarker = Docs.Create.PushpinDocument(
+        const pushpin = Docs.Create.PushpinDocument(
             NumCast(latitude),
             NumCast(longitude),
             false,
@@ -317,8 +326,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             { map: map }
             // ,'pushpinIDamongus'+ this.incrementer++
         );
-        this.addDocument(mapMarker, this.annotationKey);
-
+        this.addDocument(pushpin, this.annotationKey);
+        return pushpin;
         // mapMarker.infoWindowOpen = true;
     }, 'createpin');
 
@@ -331,6 +340,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             // Removes filter
             Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
             Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
+            Doc.setDocFilter(this.rootDoc, '-linkedTo', Field.toString(DocCast(this.selectedPin.mapPin)), 'removeAll');
 
             const temp = this.selectedPin;
             this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp));
@@ -356,8 +366,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.selectedPin = pinDoc;
         this.bingSearchBarContents = pinDoc.map;
 
-        Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
-        Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match');
+        // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
+        // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match');
+        Doc.setDocFilter(this.rootDoc, '-linkedTo', Field.toScriptString(this.selectedPin), 'mapPin' as any);
 
         this.recolorPin(this.selectedPin, 'green');
 
@@ -374,13 +385,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
     };
 
-    /**
-     * Returns a list of Pushpin docs
-     */
-    @computed get allMapPushpins() {
-        return DocListCast(this.dataDoc[this.annotationKey]);
-    }
-
     /**
      * Map OnClick
      */
@@ -429,20 +433,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     /*
      * Returns doc w/ relevant info
      */
-    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps, existingPin?: Doc) => {
         /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
         const anchor = Docs.Create.ConfigDocument({
             title: 'MapAnchor:' + this.rootDoc.title,
             text: StrCast(this.selectedPin?.map) || StrCast(this.rootDoc.map) || 'map location',
-            config_latitude: NumCast(this.selectedPin?.latitude ?? this.dataDoc.latitude),
-            config_longitude: NumCast(this.selectedPin?.longitude ?? this.dataDoc.longitude),
+            config_latitude: NumCast((existingPin ?? this.selectedPin)?.latitude ?? this.dataDoc.latitude),
+            config_longitude: NumCast((existingPin ?? this.selectedPin)?.longitude ?? this.dataDoc.longitude),
             config_map_zoom: NumCast(this.dataDoc.map_zoom),
             config_map_type: StrCast(this.dataDoc.map_type),
-            config_map: StrCast(this.selectedPin?.map) || StrCast(this.dataDoc.map),
+            config_map: StrCast((existingPin ?? this.selectedPin)?.map) || StrCast(this.dataDoc.map),
             layout_unrendered: true,
         });
         if (anchor) {
-            anchor.mapPin = this.selectedPin;
+            anchor.mapPin = existingPin ?? this.selectedPin;
             if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
             /* addAsAnnotation &&*/ this.addDocument(anchor);
             PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc);
@@ -495,6 +499,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             // Removes filter
             Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
             Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
+            Doc.setDocFilter(this.rootDoc, '-linkedTo', Field.toString(DocCast(this.selectedPin.mapPin)), 'removeAll');
 
             this.removePushpin(this.selectedPin);
         }
@@ -587,16 +592,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             { fireImmediately: true }
         );
         this._disposers.highlight = reaction(
-            () => this.allMapPushpins.map(doc => doc[Highlight]),
+            () => this.allAnnotations.map(doc => doc[Highlight]),
             () => {
-                const allPins = this.allMapPushpins.map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })).filter(pair => pair.pushpin);
-                allPins.forEach(({ doc, pushpin }) => {
+                const allConfigPins = this.allAnnotations.map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })).filter(pair => pair.pushpin);
+                allConfigPins.forEach(({ doc, pushpin }) => {
                     if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) {
                         this.recolorPin(pushpin);
                         this.map_pinHighlighted.delete(pushpin);
                     }
                 });
-                allPins.forEach(({ doc, pushpin }) => {
+                allConfigPins.forEach(({ doc, pushpin }) => {
                     if (doc[Highlight] && !this.map_pinHighlighted.get(pushpin)) {
                         this.recolorPin(pushpin, 'orange');
                         this.map_pinHighlighted.set(pushpin, true);
@@ -737,30 +742,32 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     <div>
                         {!this._mapReady
                             ? null
-                            : this.allMapPushpins.map(pushpin => (
-                                  <DocumentView
-                                      {...this.props}
-                                      renderDepth={this.props.renderDepth + 1}
-                                      Document={pushpin}
-                                      DataDoc={undefined}
-                                      PanelWidth={returnOne}
-                                      PanelHeight={returnOne}
-                                      NativeWidth={returnOne}
-                                      NativeHeight={returnOne}
-                                      onKey={undefined}
-                                      onDoubleClick={undefined}
-                                      onBrowseClick={undefined}
-                                      childFilters={returnEmptyFilter}
-                                      childFiltersByRanges={returnEmptyFilter}
-                                      searchFilterDocs={returnEmptyDoclist}
-                                      isDocumentActive={returnFalse}
-                                      isContentActive={returnFalse}
-                                      addDocTab={returnFalse}
-                                      ScreenToLocalTransform={Transform.Identity}
-                                      fitContentsToBox={undefined}
-                                      focus={returnOne}
-                                  />
-                              ))}
+                            : this.allAnnotations
+                                  .filter(anno => !anno.layout_unrendered)
+                                  .map(pushpin => (
+                                      <DocumentView
+                                          {...this.props}
+                                          renderDepth={this.props.renderDepth + 1}
+                                          Document={pushpin}
+                                          DataDoc={undefined}
+                                          PanelWidth={returnOne}
+                                          PanelHeight={returnOne}
+                                          NativeWidth={returnOne}
+                                          NativeHeight={returnOne}
+                                          onKey={undefined}
+                                          onDoubleClick={undefined}
+                                          onBrowseClick={undefined}
+                                          childFilters={returnEmptyFilter}
+                                          childFiltersByRanges={returnEmptyFilter}
+                                          searchFilterDocs={returnEmptyDoclist}
+                                          isDocumentActive={returnFalse}
+                                          isContentActive={returnFalse}
+                                          addDocTab={returnFalse}
+                                          ScreenToLocalTransform={Transform.Identity}
+                                          fitContentsToBox={undefined}
+                                          focus={returnOne}
+                                      />
+                                  ))}
                     </div>
                     {/* <MapBoxInfoWindow
                                     key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 395fbd1ca..c3027f51f 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -46,7 +46,7 @@ interface IViewerProps extends FieldViewProps {
     sidebarAddDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean;
     loaded?: (nw: number, nh: number, np: number) => void;
     setPdfViewer: (view: PDFViewer) => void;
-    anchorMenuClick?: () => undefined | ((anchor: Doc, summarize?: boolean) => void);
+    anchorMenuClick?: () => undefined | ((anchor: Doc) => void);
     crop: (region: Doc | undefined, addCrop?: boolean) => Doc | undefined;
 }
 
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 7ba4f0e6f..501114157 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1483,14 +1483,14 @@ export namespace Doc {
     // filters document in a container collection:
     // all documents with the specified value for the specified key are included/excluded
     // based on the modifiers :"check", "x", undefined
-    export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) {
+    export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: 'removeAll' | 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) {
         if (!container) return;
         const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'childFilters';
         const childFilters = StrListCast(container[filterField]);
         runInAction(() => {
             for (let i = 0; i < childFilters.length; i++) {
                 const fields = childFilters[i].split(FilterSep); // split key:value:modifier
-                if (fields[0] === key && (fields[1] === value.toString() || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) {
+                if (fields[0] === key && (fields[1] === value.toString() || modifiers === 'match' || modifiers === 'removeAll' || (fields[2] === 'match' && modifiers === 'remove'))) {
                     if (fields[2] === modifiers && modifiers && fields[1] === value.toString()) {
                         if (toggle) modifiers = 'remove';
                         else return;
@@ -1502,7 +1502,7 @@ export namespace Doc {
             }
             if (!childFilters.length && modifiers === 'match' && value === undefined) {
                 container[filterField] = undefined;
-            } else if (modifiers !== 'remove') {
+            } else if (modifiers !== 'remove' && modifiers !== 'removeAll') {
                 !append && (childFilters.length = 0);
                 childFilters.push(key + FilterSep + value + FilterSep + modifiers);
                 container[filterField] = new List<string>(childFilters);
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index e33a17416..8eeb52709 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -1,8 +1,7 @@
-import { makeInterface, createSchema, listSpec } from './Schema';
-import { ScriptField } from './ScriptField';
-import { Doc } from './Doc';
 import { DateField } from './DateField';
-import { SchemaHeaderField } from './SchemaHeaderField';
+import { Doc } from './Doc';
+import { createSchema, listSpec, makeInterface } from './Schema';
+import { ScriptField } from './ScriptField';
 
 export const documentSchema = createSchema({
     // content properties
@@ -26,8 +25,8 @@ export const documentSchema = createSchema({
     z: 'number', // z "coordinate" - non-zero specifies the overlay layer of a freeformview
     zIndex: 'number', // zIndex of a document in a freeform view
     _layout_scrollTop: 'number', // scroll position of a scrollable document (pdf, text, web)
-    lat: 'number',
-    lng: 'number',
+    latitude: 'number',
+    longitude: 'number',
 
     // appearance properties on the layout
     '_backgroundGrid-spacing': 'number', // the size of the grid for collection views
-- 
cgit v1.2.3-70-g09d2

