diff --git a/README.md b/README.md index 9f9a4ef..99cc7f3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# forge-extensions +# aps-extensions [![Node.js](https://img.shields.io/badge/Node.js-4.4.3-blue.svg)](https://nodejs.org/) [![npm](https://img.shields.io/badge/npm-2.15.1-blue.svg)](https://www.npmjs.com/) @@ -26,7 +26,7 @@ This sample is a collection of extensions ready to be reused. Just add reference Extensions were created using a [Basic Skeleton](public/extensions/BasicSkeleton). -This sample is based on the [Learn Forge](http://learnforge.autodesk.io) tutorials in the section *View modelss*. +This sample is based on the [Learn Aps](http://learnforge.autodesk.io) tutorials in the section *View modelss*. ## Thumbnail @@ -36,11 +36,11 @@ This sample is based on the [Learn Forge](http://learnforge.autodesk.io) tutoria Extensions are dynamically loaded and unloaded for testing on the live version. -[forge-extensions.autodesk.io](https://forge-extensions.autodesk.io) +[aps-extensions.autodesk.io](https://aps-extensions.autodesk.io) # Setup -To use this sample, you will need Autodesk developer credentials. Visit the [Forge Developer Portal](https://developer.autodesk.com), sign up for an account, then [create an app](https://developer.autodesk.com/myapps/create). For this new app, use `http://localhost:3000/api/forge/callback/oauth` as the Callback URL, although it is not used on a 2-legged flow. Finally, take note of the **Client ID** and **Client Secret**. +To use this sample, you will need Autodesk developer credentials. Visit the [APS Developer Portal](https://developer.autodesk.com), sign up for an account, then [create an app](https://developer.autodesk.com/myapps/create). For this new app, use `http://localhost:3000/api/aps/callback/oauth` as the Callback URL, although it is not used on a 2-legged flow. Finally, take note of the **Client ID** and **Client Secret**. ## Run locally @@ -48,22 +48,22 @@ Install [NodeJS](https://nodejs.org). Clone this project or download it. It's recommended to install [GitHub Desktop](https://desktop.github.com/). To clone it via command line, use the following (**Terminal** on MacOSX/Linux, **Git Shell** on Windows): - git clone https://github.com/autodesk-forge/forge-extensions.git + git clone https://github.com/autodesk-platform-services/aps-extensions.git To run it, install the required packages, set the enviroment variables with your client ID & Secret and finally start it. Via command line, navigate to the folder where this repository was cloned to and use the following commands: Mac OSX/Linux (Terminal) npm install - export FORGE_CLIENT_ID=<> - export FORGE_CLIENT_SECRET=<> + export APS_CLIENT_ID=<> + export APS_CLIENT_SECRET=<> npm start Windows (use **Node.js command line** from the Start menu) npm install - set FORGE_CLIENT_ID=<> - set FORGE_CLIENT_SECRET=<> + set APS_CLIENT_ID=<> + set APS_CLIENT_SECRET=<> npm start Open the browser: [http://localhost:3000](http://localhost:3000). @@ -82,7 +82,7 @@ ExtensionName[Folder] | |->assets[folder] |->config.json -Refer the [BasicSkeleton Extension](https://github.com/autodesk-forge/forge-extensions/tree/master/public/extensions/BasicSkeleton) for boilerplate code. +Refer the [BasicSkeleton Extension](https://github.com/autodesk-platform-services/aps-extensions/tree/main/public/extensions/BasicSkeleton) for boilerplate code. 2) Each extension folder should be self-contained code, so that it's easily shareable between projects. Extension[Folder]/config.json is meant for keeping the config of an extension and for sharing. @@ -104,7 +104,7 @@ Extension config schema: "includeinlist":"true or false" } -Example: [IconMarkupExtension config.json](https://github.com/autodesk-forge/forge-extensions/blob/master/public/extensions/IconMarkupExtension/config.json) +Example: [IconMarkupExtension config.json](https://github.com/autodesk-platform-services/aps-extensions/blob/main/public/extensions/IconMarkupExtension/config.json) > Note: If your extension relies on event Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT to load, in load function check if the data is already loaded, if not only then add the event listener, below code shows the structure.
@@ -127,22 +127,22 @@ class MyExtension extends Autodesk.Viewing.Extension {
     ...
 }
 
-Example: [IconMarkupExtension load function](https://github.com/autodesk-forge/forge-extensions/blob/master/public/extensions/IconMarkupExtension/contents/main.js#L26) +Example: [IconMarkupExtension load function](https://github.com/autodesk-platform-services/aps-extensions/blob/main/public/extensions/IconMarkupExtension/contents/main.js#L26) > Note: After the new extension is added, please restart the server. -# Understanding extensionloader and using it in forge app +# Understanding extensionloader and using it in aps app -The way loose coupling between extensions and forge app is achived is with custom event, if you want to use extensionloader in your forge app, follow the three steps: +The way loose coupling between extensions and aps app is achived is with custom event, if you want to use extensionloader in your aps app, follow the three steps: -1) Copy paste the [extensions](https://github.com/autodesk-forge/forge-extensions/tree/master/public/extensions) in public folder of your app or in the folder where the index file resides. +1) Copy paste the [extensions](https://github.com/autodesk-platform-services/aps-extensions/tree/main/public/extensions) in public folder of your app or in the folder where the index file resides. 2) Include below script in index.html file
 
 
-3) Here's the linking part between the app and the extensionloader, in viewer [onDocumentLoadSuccess](https://github.com/autodesk-forge/forge-extensions/blob/master/public/js/ForgeViewer.js#L35) function, emit an event to inform the extensionloader that viewer has loaded the model with the below [code](https://github.com/autodesk-forge/forge-extensions/blob/master/public/js/ForgeViewer.js#L39): +3) Here's the linking part between the app and the extensionloader, in viewer [onDocumentLoadSuccess](https://github.com/autodesk-platform-services/aps-extensions/blob/main/public/js/ApsViewer.js#L35) function, emit an event to inform the extensionloader that viewer has loaded the model with the below [code](https://github.com/autodesk-platform-services/aps-extensions/blob/main/public/js/ApsViewer.js#L39):
 var ViewerInstance = new CustomEvent("viewerinstance", {detail: {viewer: viewer}});      
 document.dispatchEvent(ViewerInstance);
@@ -172,7 +172,7 @@ To unload extension:
 
 ## Packages used
 
-The [Autodesk Forge](https://www.npmjs.com/package/forge-apis) packages are included by default. Some other non-Autodesk packages are used, including [express](https://www.npmjs.com/package/express) and [multer](https://www.npmjs.com/package/multer) for upload.
+The [Autodesk Platform Services](https://www.npmjs.com/package/forge-apis) packages are included by default. Some other non-Autodesk packages are used, including [express](https://www.npmjs.com/package/express) and [multer](https://www.npmjs.com/package/multer) for upload.
 
 # Tips & tricks
 
@@ -199,7 +199,7 @@ Please see the [LICENSE](LICENSE) file for full details.
 
 ## Written by
 
-The [Forge Advocates](http://forge.autodesk.com) team:
+The [APS Advocates](http://forge.autodesk.com) team:
 
 * Varun Patil [@VarunPatil578](https://twitter.com/VarunPatil578)
 * Petr Broz [@ipetrbroz](https://twitter.com/ipetrbroz)
diff --git a/config.js b/config.js
index 63417b0..c008938 100644
--- a/config.js
+++ b/config.js
@@ -1,6 +1,6 @@
 /////////////////////////////////////////////////////////////////////
 // Copyright (c) Autodesk, Inc. All rights reserved
-// Written by Forge Partner Development
+// Written by APS Partner Development
 //
 // Permission to use, copy, modify, and distribute this software in
 // object code form for any purpose and without fee is hereby granted,
@@ -16,13 +16,13 @@
 // UNINTERRUPTED OR ERROR FREE.
 /////////////////////////////////////////////////////////////////////
 
-// Autodesk Forge configuration
+// Autodesk Platform Services configuration
 module.exports = {
     // Set environment variables or hard-code here
     credentials: {
-        client_id: process.env.FORGE_CLIENT_ID,
-        client_secret: process.env.FORGE_CLIENT_SECRET,
-        callback_url: process.env.FORGE_CALLBACK_URL
+        client_id: process.env.APS_CLIENT_ID,
+        client_secret: process.env.APS_CLIENT_SECRET,
+        callback_url: process.env.APS_CALLBACK_URL
     },
     scopes: {
         // Required scopes for the server-side application
diff --git a/public/css/main.css b/public/css/main.css
index 49db692..f53c450 100644
--- a/public/css/main.css
+++ b/public/css/main.css
@@ -18,7 +18,7 @@ body {
   height: calc(100vh - 150px);
 }
 
-#forgeViewer {
+#apsViewer {
   width: 100%;
 }
 
diff --git a/public/extensions/AecGridsExtension/README.md b/public/extensions/AecGridsExtension/README.md
new file mode 100644
index 0000000..a6ba58c
--- /dev/null
+++ b/public/extensions/AecGridsExtension/README.md
@@ -0,0 +1,35 @@
+# AEC Grids
+
+[Demo](https://aps-extensions.autodesk.io/?extension=AecGridsExtensionn)
+
+A APS Viewer extension for loading and rendering Revit Grids by AEC Model Data
+
+![thumbnail](extension.gif)
+
+## Limitation
+
+This extension works only for models that has AEC Model Data and have grids extracted inside its AEC Model Data.
+
+**Note.** Currently, only RVT files produced by Revit 2018 and newer versions will have AEC Model Data after translating by APS Model Derivative API. See [Blog: Consume AEC Data which are from Model Derivative API](https://forge.autodesk.com/blog/consume-aec-data-which-are-model-derivative-api).
+
+## Usage
+
+Enable the extension, click on the Grids button to open AecGridsPanel, and then click on the checkbox to show grids of the level you want to show.
+
+## Setup
+
+Include the JS file on your page. This CDN is compatible with the latest Viewer version (v7).
+
+```xml
+
+
+```
+
+After Viewer is ready, preferable inside `onDocumentLoadSuccess`, load the extension
+
+```javascript
+viewer.loadExtension("AecGridsExtension")
+```
+
+## Author  
+[Eason Kang](https://twitter.com/yiskang)
\ No newline at end of file
diff --git a/public/extensions/AecGridsExtension/config.json b/public/extensions/AecGridsExtension/config.json
new file mode 100644
index 0000000..b3347b3
--- /dev/null
+++ b/public/extensions/AecGridsExtension/config.json
@@ -0,0 +1,40 @@
+{
+    "name": "AecGridsExtension",
+    "displayname": "AEC Grids",
+    "description": "A Forge Viewer extension for loading and rendering Revit Grids by AEC Model Data",
+    "options": {
+        "alertOnDefaultFailure": true,
+        "autoUnloadOnNoAecModelData": true
+    },
+    "editoptions":"true",
+    "viewerversion": "7.*",
+    "loadonstartup": "false",
+    "filestoload": {
+        "cssfiles": [
+            "main.css"
+        ],
+        "jsfiles": [
+            "main.js"
+        ]
+    },
+    "bloglink": "",
+    "includeinlist": "true",
+    "externaldependencies": [
+        {
+            "type": "js",
+            "link": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
+        },
+        {
+            "type": "js",
+            "link": "https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/jstree.min.js"
+        },
+        {
+            "type": "css",
+            "link": "https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/themes/default/style.min.css"
+        },
+        {
+            "type": "css",
+            "link": "https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/themes/default-dark/style.min.css"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/public/extensions/AecGridsExtension/contents/main.css b/public/extensions/AecGridsExtension/contents/main.css
new file mode 100644
index 0000000..21f551b
--- /dev/null
+++ b/public/extensions/AecGridsExtension/contents/main.css
@@ -0,0 +1,42 @@
+.fpd-docking-panel {
+    top: 10px;
+    left: 10px;
+}
+
+.fpd-aec-grids-panel {
+    max-width: 315px;
+    max-height: 280px;
+    width: 315px;
+    height: 280px;
+    left: 10px;
+    top: 225px;
+}
+
+.fpd-aec-grids-panel .docking-panel-footer {
+    z-index: 1;
+}
+
+.adsk-viewing-viewer .adsk-icon-documentModels {
+    background-repeat: no-repeat !important;
+    background-size: 80% !important;
+    margin-left: 4px;
+    margin-top: 3px
+}
+
+.adsk-viewing-viewer.dark-theme .adsk-icon-documentModels {
+    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E %3Ctitle%3Efolder-16%3C/title%3E %3Cg id='assets'%3E %3Cg id='Layer_201' data-name='Layer%25201'%3E %3Cpath fill='%23F4F4F4' d='M0,2.5V4H6.34l1-1-1-1H.5A.5.5,0,0,0,0,2.5ZM0,5v8a1,1,0,0,0,1,1H14a1,1,0,0,0,1-1V4a1,1,0,0,0-1-1H9L7,5Z'/%3E %3C/g%3E %3C/g%3E %3C/svg%3E")
+}
+
+.adsk-viewing-viewer.dark-theme .adsk-icon-documentModels:hover,
+.adsk-viewing-viewer.dark-theme .active .adsk-icon-documentModels {
+    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E %3Ctitle%3Efolder-16%3C/title%3E %3Cg id='assets'%3E %3Cg id='Layer_201' data-name='Layer%25201'%3E %3Cpath fill='%2300BFFF' d='M0,2.5V4H6.34l1-1-1-1H.5A.5.5,0,0,0,0,2.5ZM0,5v8a1,1,0,0,0,1,1H14a1,1,0,0,0,1-1V4a1,1,0,0,0-1-1H9L7,5Z'/%3E %3C/g%3E %3C/g%3E %3C/svg%3E")
+}
+
+.adsk-viewing-viewer.light-theme .adsk-icon-documentModels {
+    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E %3Ctitle%3Efolder-16%3C/title%3E %3Cg id='assets'%3E %3Cg id='Layer_201' data-name='Layer%25201'%3E %3Cpath fill='black' d='M0,2.5V4H6.34l1-1-1-1H.5A.5.5,0,0,0,0,2.5ZM0,5v8a1,1,0,0,0,1,1H14a1,1,0,0,0,1-1V4a1,1,0,0,0-1-1H9L7,5Z'/%3E %3C/g%3E %3C/g%3E %3C/svg%3E")
+}
+
+.adsk-viewing-viewer.light-theme .adsk-icon-documentModels:hover,
+.adsk-viewing-viewer.light-theme .active .adsk-icon-documentModels {
+    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E %3Ctitle%3Efolder-16%3C/title%3E %3Cg id='assets'%3E %3Cg id='Layer_201' data-name='Layer%25201'%3E %3Cpath fill='%2300BFFF' d='M0,2.5V4H6.34l1-1-1-1H.5A.5.5,0,0,0,0,2.5ZM0,5v8a1,1,0,0,0,1,1H14a1,1,0,0,0,1-1V4a1,1,0,0,0-1-1H9L7,5Z'/%3E %3C/g%3E %3C/g%3E %3C/svg%3E")
+}
\ No newline at end of file
diff --git a/public/extensions/AecGridsExtension/contents/main.js b/public/extensions/AecGridsExtension/contents/main.js
new file mode 100644
index 0000000..fb563cd
--- /dev/null
+++ b/public/extensions/AecGridsExtension/contents/main.js
@@ -0,0 +1,555 @@
+/////////////////////////////////////////////////////////////////////
+// Copyright (c) Autodesk, Inc. All rights reserved
+// Written by APS Partner Development
+//
+// Permission to use, copy, modify, and distribute this software in
+// object code form for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appears in all copies and
+// that both that copyright notice and the limited warranty and
+// restricted rights notice below appear in all supporting
+// documentation.
+//
+// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
+// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
+// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.  AUTODESK, INC.
+// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
+// UNINTERRUPTED OR ERROR FREE.
+/////////////////////////////////////////////////////////////////////
+
+class AecGrid {
+    constructor(viewer, options) {
+        this.viewer = viewer;
+        this.modelBuilder = null;
+        this.idPrefix = 100;
+
+        this.options = options || {};
+    }
+
+    async init() {
+        const modelBuilderExt = await this.viewer.loadExtension('Autodesk.Viewing.SceneBuilder');
+        const modelBuilder = await modelBuilderExt.addNewModel({
+            conserveMemory: false,
+            modelNameOverride: 'Grids'
+        });
+
+        this.modelBuilder = modelBuilder;
+
+        const matName = 'grid-line-mat';
+        const linesMaterial = new THREE.LineBasicMaterial({
+            color: 0xff0000,
+            linewidth: 2
+        });
+        this.modelBuilder.addMaterial(matName, linesMaterial);
+        this.linesMaterial = this.modelBuilder.findMaterial(matName);
+    }
+
+    createLabel(params) {
+        const canvas = document.createElement('canvas');
+        const ctx = canvas.getContext('2d');
+        const size = 256;
+        canvas.width = canvas.height = size;
+
+        // draw/fill the circle
+        ctx.beginPath();
+        ctx.arc(canvas.width / 2, canvas.height / 2, canvas.width / 2, 0, 2 * Math.PI);
+        ctx.fillStyle = 'yellow';
+        ctx.fill();
+
+        // draw the number
+        const fontSize = size / 2;
+        ctx.fillStyle = 'black';
+        ctx.font = `${fontSize}px sans-serif`;
+        ctx.textAlign = 'center';
+        ctx.textBaseline = 'middle';
+        ctx.fillText(params.text, canvas.width / 2, canvas.height / 2);
+        const labelBlobUrl = canvas.toDataURL();
+
+        const image = new Image();
+        const texture = new THREE.Texture();
+
+        texture.image = image;
+        image.src = labelBlobUrl;
+        image.onload = function () {
+            texture.needsUpdate = true;
+        };
+
+        const labelDbId = this.idPrefix++;
+        const matName = `label-mat-${labelDbId}`;
+        const material = new THREE.MeshPhongMaterial({ map: texture, side: THREE.DoubleSide, opacity: 0.8, transparent: true });
+        this.modelBuilder.addMaterial(matName, material);
+        const labelMat = this.modelBuilder.findMaterial(matName);
+
+        const circleGeo = new THREE.BufferGeometry().fromGeometry(new THREE.CircleGeometry(3, 32));
+        const circle = new THREE.Mesh(circleGeo, labelMat);
+
+        circle.matrix = new THREE.Matrix4().compose(
+            params.circlePos,
+            new THREE.Quaternion(0, 0, 0, 1),
+            new THREE.Vector3(1, 1, 1)
+        );
+        circle.dbId = labelDbId;
+        this.modelBuilder.addMesh(circle);
+    }
+
+    createGrid(grid) {
+        //draw each segment one by one
+        const segments = grid.segments;
+        const offsetMatrix = this.viewer.model.getModelToViewerTransform() || new THREE.Matrix4().identity();
+
+        for (let i = 0; i < segments.length; i++) {
+            const seg = segments[i];
+            //start and end point
+            const { start, end } = seg.points;
+            const startPoint = new THREE.Vector3(start[0], start[1], start[2]).applyMatrix4(offsetMatrix);
+            const endPoint = new THREE.Vector3(end[0], end[1], end[2]).applyMatrix4(offsetMatrix);
+
+            if (this.options.hasOwnProperty('heightOverride') && !isNaN(this.options.heightOverride)) {
+                startPoint.setZ(this.options.heightOverride);
+                endPoint.setZ(this.options.heightOverride);
+            }
+
+            //grid line
+            const lineGeo = new THREE.BufferGeometry();
+
+            const lineIndices = [];
+            const lineVertices = [];
+            lineVertices.push(...startPoint.toArray());
+            lineVertices.push(...endPoint.toArray());
+            lineIndices.push(0, 1);
+
+            lineGeo.addAttribute('index', new THREE.BufferAttribute(new Uint32Array(lineIndices), 1));
+            lineGeo.addAttribute('position', new THREE.BufferAttribute(new Float32Array(lineVertices), 3));
+            lineGeo.isLines = true;
+
+            var line = new THREE.Mesh(lineGeo, this.linesMaterial);
+            line.dbId = this.idPrefix++;
+            this.modelBuilder.addMesh(line);
+
+            let lineDir = endPoint.clone().sub(startPoint.clone()).normalize();
+            let circlePos = endPoint.clone().add(lineDir.clone().multiplyScalar(3));
+
+            this.createLabel({
+                circlePos,
+                text: grid.label
+            });
+        }
+    }
+
+    async build() {
+        await this.init();
+
+        let aecdata = this.viewer.model.getDocumentNode().getAecModelData();
+        if (!aecdata) {
+            aecdata = await Autodesk.Viewing.Document.getAecModelData(this.viewer.model.getDocumentNode());
+            if (!aecdata)
+                throw new Error('AEC model data not found');
+        }
+
+        const grids = aecdata.grids;
+
+        if (!grids || grids.length <= 0)
+            throw new Error('No Grid data found in this model\'s AEC model data.');
+
+        for (let i = 0; i < grids.length; i++) {
+            const grid = grids[i];
+            this.createGrid(grid);
+        }
+
+        return true;
+    }
+
+    destroy() {
+        this.viewer.impl.unloadModel(this.modelBuilder.model);
+
+        delete this.linesMaterial;
+        this.linesMaterial = null;
+
+        return true;
+    }
+
+    lockSelection() {
+        const dbIds = this.modelBuilder.model.getFragmentList().fragments.fragId2dbId;
+        const model = this.modelBuilder.model;
+        this.viewer.lockSelection(dbIds, true, model);
+    }
+
+    unlockSelection() {
+        const dbIds = this.modelBuilder.model.getFragmentList().fragments.fragId2dbId;
+        const model = this.modelBuilder.model;
+        this.viewer.unlockSelection(dbIds, model);
+    }
+}
+
+const AEC_GRIDS_CHANGED_EVENT = 'aecGridsChangedEvent';
+
+class AecGridsPanel extends Autodesk.Viewing.UI.DockingPanel {
+    constructor(viewer) {
+        const options = {};
+
+        //  Height adjustment for scroll container, offset to height of the title bar and footer by default.
+        if (!options.heightAdjustment)
+            options.heightAdjustment = 70;
+
+        if (!options.marginTop)
+            options.marginTop = 0;
+
+        //options.addFooter = false;
+
+        super(viewer.container, viewer.container.id + 'AecGridsPanel', 'Grids', options);
+
+        this.container.classList.add('fpd-docking-panel');
+        this.container.classList.add('fpd-aec-grids-panel');
+        this.createScrollContainer(options);
+
+        this.viewer = viewer;
+        this.options = options;
+        this.uiCreated = false;
+
+        this.addVisibilityListener((show) => {
+            if (!show) return;
+
+            if (!this.uiCreated)
+                this.createUI();
+        });
+    }
+
+    get levelSelector() {
+        const levelExt = this.viewer.getExtension('Autodesk.AEC.LevelsExtension');
+        return levelExt && levelExt.floorSelector;
+    }
+
+    uninitialize() {
+        super.uninitialize();
+    }
+
+    createUI() {
+        this.uiCreated = true;
+
+        const div = document.createElement('div');
+
+        const treeDiv = document.createElement('div');
+        div.appendChild(treeDiv);
+        this.treeContainer = treeDiv;
+        this.scrollContainer.appendChild(div);
+
+        this.buildTree(this.levelSelector.floorData);
+    }
+
+    findLevelByName(name) {
+        const levelData = this.levelSelector.floorData;
+        return levelData.find(level => level.name.includes(name));
+    }
+
+    hoverLevelByName(name) {
+        const level = this.findLevelByName(name);
+        let levelIdx = level ? level.index : null;
+        if (levelIdx === this.levelSelector.currentFloor) {
+            levelIdx = Autodesk.AEC.FloorSelector.AllFloors;
+        }
+
+        this.levelSelector.rollOverFloor(levelIdx);
+    }
+
+    dehoverLevel() {
+        //this.levelSelector.rollOverFloor(Autodesk.AEC.FloorSelector.NoFloor);
+        this.levelSelector.rollOverFloor();
+        this.viewer.impl.invalidate(false, true, true);
+    }
+
+    buildTree(data) {
+        const nodes = [];
+
+        for (let i = 0; i < data.length; i++) {
+            const node = {
+                id: data[i].index,
+                type: 'levels',
+                text: data[i].name
+            }
+            nodes.push(node);
+        }
+
+        console.log(nodes);
+
+        $(this.treeContainer)
+            .jstree({
+                core: {
+                    data: nodes,
+                    multiple: true,
+                    themes: {
+                        icons: false,
+                        name: 'default-dark'
+                    }
+                },
+                sort: function (a, b) {
+                    const a1 = this.get_node(a);
+                    const b1 = this.get_node(b);
+                    return (a1.text > b1.text) ? 1 : -1;
+                },
+                checkbox: {
+                    keep_selected_style: false,
+                    //three_state: false,
+                    deselect_all: true,
+                    cascade: 'none'
+                },
+                types: {
+                    levels: {},
+                    sheets: {}
+                },
+                plugins: ['types', 'checkbox', 'sort', 'wholerow'],
+            })
+            .on('open_node.jstree', (e, data) => {
+                const node = data.instance.get_node(data.node, true);
+                if (!node) {
+                    return;
+                }
+
+                node.siblings('.jstree-open').each(function () {
+                    data.instance.close_node(this, 0);
+                });
+            })
+            .on('hover_node.jstree', async (e, data) => {
+                let level = null;
+                if (data.node.type === 'levels') {
+                    level = data.node.text;
+                } else {
+                    level = data.instance.get_node(data.node.parent)?.text;
+                }
+
+                this.hoverLevelByName(level);
+            })
+            .on('dehover_node.jstree', async (e, data) => {
+                this.dehoverLevel();
+            })
+            .on('changed.jstree', async (e, data) => {
+                // console.log(e, data);
+                if (!data.node || !data.node.type) {
+                    return;
+                }
+
+                if (data.action === 'select_node') {
+                    if (data.node.type === 'levels') {
+                        const level = data.node.text;
+                        this.viewer.dispatchEvent({
+                            type: AEC_GRIDS_CHANGED_EVENT,
+                            action: 'attach',
+                            level: this.findLevelByName(level)
+                        });
+                    }
+                } else {
+                    if (data.node.type === 'levels') {
+                        const level = data.node.text;
+                        this.viewer.dispatchEvent({
+                            type: AEC_GRIDS_CHANGED_EVENT,
+                            action: 'detach',
+                            level: this.findLevelByName(level)
+                        });
+                    }
+                }
+            });
+    }
+}
+
+/**
+ * APS Viewer extension for loading and rendering Revit Grids by AEC Model Data
+ * @class
+ */
+class AecGridsExtension extends Autodesk.Viewing.Extension {
+    /**
+     * @param {Viewer3D} viewer The APS Viewer instance
+     * @param {Object} options The extension options
+     * @param {function} [options.onFailureCallback=(error, errorMessage) => { console.warn(`[${error.name}]: ${error.message}`); }] A failure callback that will be called when this model doesn't have AEC Model Data or grids data. By default, it will show warning message in the console.
+     * @param {boolean} [options.autoUnloadOnNoAecModelData=true] If false, AecGridsExtension won't be unloaded when this model doesn't have AEC Model Data or grids data.
+     * @param {boolean} [options.alertOnDefaultFailure=false] When the {options.onFailureCallback} is using the default one, this option can be used to control if viewer will show error messages with alert().
+     */
+    constructor(viewer, options) {
+        options = options || {};
+
+        if (!options.onFailureCallback || (typeof options.onFailureCallback !== 'function')) {
+            options.onFailureCallback = (error) => {
+                let msg = `[${error.name}]: ${error.message}`;
+                console.warn(msg);
+
+                if (options.alertOnDefaultFailure == true)
+                    alert(msg);
+            };
+        }
+
+        super(viewer, options);
+
+        this.grids = [];
+        this.uiCreated = false;
+        this.panel = null;
+
+        this.createUI = this.createUI.bind(this);
+        this.onToolbarCreated = this.onToolbarCreated.bind(this);
+        this.onGridChanged = this.onGridChanged.bind(this);
+    }
+
+    createUI() {
+        this.uiCreated = true;
+
+        const viewer = this.viewer;
+
+        const aecGridsPanel = new AecGridsPanel(viewer);
+        viewer.addPanel(aecGridsPanel);
+        this.panel = aecGridsPanel;
+
+        const aecGridsToolButton = new Autodesk.Viewing.UI.Button('toolbar-aecGridsTool');
+        aecGridsToolButton.setToolTip('Grids');
+        aecGridsToolButton.setIcon('adsk-icon-documentModels');
+        aecGridsToolButton.onClick = function () {
+            aecGridsPanel.setVisible(!aecGridsPanel.isVisible());
+        };
+
+        const subToolbar = new Autodesk.Viewing.UI.ControlGroup('toolbar-fpd-tools');
+        subToolbar.addControl(aecGridsToolButton);
+        subToolbar.aecGridsToolButton = aecGridsToolButton;
+        this.subToolbar = subToolbar;
+
+        viewer.toolbar.addControl(this.subToolbar);
+
+        aecGridsPanel.addVisibilityListener(function (visible) {
+            if (visible)
+                viewer.onPanelVisible(aecGridsPanel, viewer);
+
+            aecGridsToolButton.setState(visible ? Autodesk.Viewing.UI.Button.State.ACTIVE : Autodesk.Viewing.UI.Button.State.INACTIVE);
+        });
+    }
+
+    onToolbarCreated() {
+        if (!this.uiCreated)
+            this.createUI();
+    }
+
+    async onGridChanged(event) {
+        try {
+            let grid = null;
+            switch (event.action) {
+                case 'attach':
+                    grid = new AecGrid(this.viewer, { level: event.level, heightOverride: event.level.zMin });
+                    await grid.build();
+                    this.grids.push(grid);
+                    break;
+                case 'detach':
+                    const idx = this.grids.findIndex(g => g.options.level.name === event.level.name);
+                    if (idx <= -1)
+                        return;
+
+                    const removed = this.grids.splice(idx, 1);
+                    grid = removed[0];
+                    grid.destroy();
+                    break;
+            }
+        } catch (ex) {
+            this.handleFailure(ex, ex.message);
+        }
+    }
+
+    handleFailure(msg) {
+        const error = new Error(msg);
+        error.name = 'AecGridsExtensionError';
+
+        const { onFailureCallback } = this.options;
+        onFailureCallback && onFailureCallback(error, msg);
+    }
+
+    async load() {
+        await this.viewer.waitForLoadDone();
+
+        // Pre-load level extension 
+        const aecdata = await Autodesk.Viewing.Document.getAecModelData(this.viewer.model.getDocumentNode());
+        if (!aecdata || !aecdata.grids || aecdata.grids.length <= 0) {
+            let errorMsg = 'This model doesn\'t contain AEC Model Data or No Grid data found in this model\'s AEC Model Data.';
+
+            if (this.options.autoUnloadOnNoAecModelData == false) {
+                this.handleFailure(errorMsg);
+            } else {
+                errorMsg += ' Unloading extension ...';
+                this.handleFailure(errorMsg);
+
+                const onFailureUnloadExtensionHandler = (event) => {
+                    if (event.extensionId != 'AecGridsExtension') return;
+
+                    this.viewer.removeEventListener(
+                        Autodesk.Viewing.EXTENSION_LOADED_EVENT,
+                        onFailureUnloadExtensionHandler,
+                    );
+
+                    console.warn('AecGridsExtension is unloaded since this model doesn\'t contain AEC Model Data.');
+                    this.viewer.unloadExtension(event.extensionId);
+                };
+
+                this.viewer.addEventListener(
+                    Autodesk.Viewing.EXTENSION_LOADED_EVENT,
+                    onFailureUnloadExtensionHandler
+                );
+            }
+
+            return false;
+        }
+
+        await this.viewer.loadExtension('Autodesk.AEC.LevelsExtension', { doNotCreateUI: true });
+
+        this.viewer.addEventListener(
+            AEC_GRIDS_CHANGED_EVENT,
+            this.onGridChanged
+        );
+
+        if (this.viewer.toolbar) {
+            // Toolbar is already available, create the UI
+            this.createUI();
+        }
+
+        return true;
+    }
+
+    unload() {
+        while (this.grids.length > 0) {
+            let grid = this.grids.pop();
+            grid.destroy();
+        }
+
+        if (this.panel) {
+            this.viewer.removePanel(this.panel);
+            this.panel.uninitialize();
+            delete this.panel;
+            this.panel = null;
+        }
+
+        this.viewer.removeEventListener(
+            AEC_GRIDS_CHANGED_EVENT,
+            this.onGridChanged
+        );
+
+        if (this.subToolbar) {
+            this.viewer.toolbar.removeControl(this.subToolbar);
+            delete this.subToolbar.adnSheetsBrowserButton;
+            this.subToolbar.adnSheetsBrowserButton = null;
+            delete this.subToolbar;
+            this.subToolbar = null;
+        }
+
+        return true;
+    }
+
+    // async createGrids() {
+    //     const aecdata = await this.viewer.model.getDocumentNode().getDocument().downloadAecModelData();
+    //     const levels = aecdata.levels;
+
+    //     const offsetMatrix = this.viewer.model.getModelToViewerTransform() || new THREE.Matrix4().identity();
+
+    //     for (let i = 0; i < levels.length; i++) {
+    //         const level = levels[i];
+    //         const elevation = new THREE.Vector3(0, 0, level.elevation).applyMatrix4(offsetMatrix);
+    //         const grid = new AecGrid(this.viewer, { heightOverride: elevation.z });
+    //         await grid.build();
+
+    //         this.grids.push(grid);
+    //     }
+    // }
+}
+
+AutodeskNamespace('AecGridsExtension');
+AecGridsExtension.AEC_GRIDS_CHANGED_EVENT = AEC_GRIDS_CHANGED_EVENT;
+
+Autodesk.Viewing.theExtensionManager.registerExtension('AecGridsExtension', AecGridsExtension);
\ No newline at end of file
diff --git a/public/extensions/AecGridsExtension/extension.gif b/public/extensions/AecGridsExtension/extension.gif
new file mode 100644
index 0000000..19b09dc
Binary files /dev/null and b/public/extensions/AecGridsExtension/extension.gif differ
diff --git a/public/extensions/BasicSkeleton/contents/main.js b/public/extensions/BasicSkeleton/contents/main.js
index 18ed31d..ff28477 100644
--- a/public/extensions/BasicSkeleton/contents/main.js
+++ b/public/extensions/BasicSkeleton/contents/main.js
@@ -1,6 +1,6 @@
 /////////////////////////////////////////////////////////////////////
 // Copyright (c) Autodesk, Inc. All rights reserved
-// Written by Forge Partner Development
+// Written by APS Partner Development
 //
 // Permission to use, copy, modify, and distribute this software in
 // object code form for any purpose and without fee is hereby granted,
diff --git a/public/extensions/CameraRotation/README.md b/public/extensions/CameraRotation/README.md
index 426a979..d02db4d 100644
--- a/public/extensions/CameraRotation/README.md
+++ b/public/extensions/CameraRotation/README.md
@@ -1,6 +1,6 @@
 # Camera Rotation
 
-[Demo](https://forge-extensions.autodesk.io/?extension=CameraRotation)
+[Demo](https://aps-extensions.autodesk.io/?extension=CameraRotation)
 
 This extension can be used for model demo purpose, it just rotates the building or the model for look around.
 
diff --git a/public/extensions/CameraRotation/contents/main.js b/public/extensions/CameraRotation/contents/main.js
index 9b6e89c..b407f6a 100644
--- a/public/extensions/CameraRotation/contents/main.js
+++ b/public/extensions/CameraRotation/contents/main.js
@@ -1,6 +1,6 @@
 /////////////////////////////////////////////////////////////////////
 // Copyright (c) Autodesk, Inc. All rights reserved
-// Written by Forge Partner Development
+// Written by APS Partner Development
 //
 // Permission to use, copy, modify, and distribute this software in
 // object code form for any purpose and without fee is hereby granted,
diff --git a/public/extensions/CustomPropertiesExtension/README.md b/public/extensions/CustomPropertiesExtension/README.md
index be85eb8..72d0b1d 100644
--- a/public/extensions/CustomPropertiesExtension/README.md
+++ b/public/extensions/CustomPropertiesExtension/README.md
@@ -1,6 +1,6 @@
 # Custom Properties Extension
 
-[Demo](https://forge-extensions.autodesk.io/?extension=CustomPropertiesExtension)
+[Demo](https://aps-extensions.autodesk.io/?extension=CustomPropertiesExtension)
 
 Extension showing custom properties in the Properties palette, based on their `dbId`.
 
diff --git a/public/extensions/CustomPropertiesExtension/contents/main.js b/public/extensions/CustomPropertiesExtension/contents/main.js
index 1adc310..4c9c591 100644
--- a/public/extensions/CustomPropertiesExtension/contents/main.js
+++ b/public/extensions/CustomPropertiesExtension/contents/main.js
@@ -1,6 +1,6 @@
 /////////////////////////////////////////////////////////////////////
 // Copyright (c) Autodesk, Inc. All rights reserved
-// Written by Forge Partner Development
+// Written by APS Partner Development
 //
 // Permission to use, copy, modify, and distribute this software in
 // object code form for any purpose and without fee is hereby granted,
diff --git a/public/extensions/DrawToolExtension/README.md b/public/extensions/DrawToolExtension/README.md
index 0e10749..cf5f9ea 100644
--- a/public/extensions/DrawToolExtension/README.md
+++ b/public/extensions/DrawToolExtension/README.md
@@ -1,6 +1,6 @@
 # Draw Tool Extension
 
-[Demo](https://forge-extensions.autodesk.io/?extension=DrawToolExtension)
+[Demo](https://aps-extensions.autodesk.io/?extension=DrawToolExtension)
 
 A simple tool that will allow us to draw boxes and spheres. Please use the Draw Tool in only full screen mode.
 
diff --git a/public/extensions/DrawToolExtension/contents/main.js b/public/extensions/DrawToolExtension/contents/main.js
index a13a5ba..544dd56 100644
--- a/public/extensions/DrawToolExtension/contents/main.js
+++ b/public/extensions/DrawToolExtension/contents/main.js
@@ -1,6 +1,6 @@
 /////////////////////////////////////////////////////////////////////
 // Copyright (c) Autodesk, Inc. All rights reserved
-// Written by Forge Partner Development
+// Written by APS Partner Development
 //
 // Permission to use, copy, modify, and distribute this software in
 // object code form for any purpose and without fee is hereby granted,
diff --git a/public/extensions/GoogleMapsLocator/README.md b/public/extensions/GoogleMapsLocator/README.md
index 9fec0c9..9d6610d 100644
--- a/public/extensions/GoogleMapsLocator/README.md
+++ b/public/extensions/GoogleMapsLocator/README.md
@@ -1,8 +1,8 @@
 # Google Maps Locator
 
-[Demo](https://forge-extensions.autodesk.io/?extension=GoogleMapsLocator)
+[Demo](https://aps-extensions.autodesk.io/?extension=GoogleMapsLocator)
 
-Forge Viewer extension to show location of model on google maps.
+APS Viewer extension to show location of model on google maps.
 
 ![thumbnail](extension.gif)
 
diff --git a/public/extensions/GoogleMapsLocator/contents/main.js b/public/extensions/GoogleMapsLocator/contents/main.js
index 14b2211..ab7f3d2 100644
--- a/public/extensions/GoogleMapsLocator/contents/main.js
+++ b/public/extensions/GoogleMapsLocator/contents/main.js
@@ -1,6 +1,6 @@
 /////////////////////////////////////////////////////////////////////
 // Copyright (c) Autodesk, Inc. All rights reserved
-// Written by Forge Partner Development
+// Written by APS Partner Development
 //
 // Permission to use, copy, modify, and distribute this software in
 // object code form for any purpose and without fee is hereby granted,
diff --git a/public/extensions/IconMarkupExtension/README.md b/public/extensions/IconMarkupExtension/README.md
index 480c5cf..719e239 100644
--- a/public/extensions/IconMarkupExtension/README.md
+++ b/public/extensions/IconMarkupExtension/README.md
@@ -1,6 +1,6 @@
 # Icon Markup Extension
 
-[Demo](https://forge-extensions.autodesk.io/?extension=IconMarkupExtension)
+[Demo](https://aps-extensions.autodesk.io/?extension=IconMarkupExtension)
 
 Extension showing labels on top of elements, based on their `dbId`.
 
diff --git a/public/extensions/IconMarkupExtension/contents/main.js b/public/extensions/IconMarkupExtension/contents/main.js
index db5e983..e77e7ce 100644
--- a/public/extensions/IconMarkupExtension/contents/main.js
+++ b/public/extensions/IconMarkupExtension/contents/main.js
@@ -1,6 +1,6 @@
 /////////////////////////////////////////////////////////////////////
 // Copyright (c) Autodesk, Inc. All rights reserved
-// Written by Forge Partner Development
+// Written by APS Partner Development
 //
 // Permission to use, copy, modify, and distribute this software in
 // object code form for any purpose and without fee is hereby granted,
diff --git a/public/extensions/NestedViewerExtension/README.md b/public/extensions/NestedViewerExtension/README.md
index 1dfcef0..f7cd18a 100644
--- a/public/extensions/NestedViewerExtension/README.md
+++ b/public/extensions/NestedViewerExtension/README.md
@@ -1,8 +1,8 @@
 # NestedViewerExtension
 
-[Demo](https://forge-extensions.autodesk.io/?extension=NestedViewerExtension)
+[Demo](https://aps-extensions.autodesk.io/?extension=NestedViewerExtension)
 
-Forge Viewer extension showing viewables related to the currently loaded model in another instance of the viewer.
+APS Viewer extension showing viewables related to the currently loaded model in another instance of the viewer.
 
 ![thumbnail](extension.gif)
 
diff --git a/public/extensions/PotreeExtension/README.md b/public/extensions/PotreeExtension/README.md
index 04fe091..108055b 100644
--- a/public/extensions/PotreeExtension/README.md
+++ b/public/extensions/PotreeExtension/README.md
@@ -1,19 +1,19 @@
 # PotreeExtension
 
-[Demo](https://forge-extensions.autodesk.io/?extension=PotreeExtension)
+[Demo](https://aps-extensions.autodesk.io/?extension=PotreeExtension)
 
 ![thumbnail](extension.gif)
 
-Forge Viewer extension for loading and rendering Potree models.
+APS Viewer extension for loading and rendering Potree models.
 
-The extension uses [potree-core](https://github.com/tentone/potree-core) library that has been back-ported to three.js version 71 (the one used by the Forge Viewer). The experimental back-ported version of the library can be found in https://github.com/petrbroz/potree-core/tree/experiment/three71.
+The extension uses [potree-core](https://github.com/tentone/potree-core) library that has been back-ported to three.js version 71 (the one used by the APS Viewer). The experimental back-ported version of the library can be found in https://github.com/petrbroz/potree-core/tree/experiment/three71.
 
 Apart from the library itself (available as either _potree.js_, _potree.min.js_, or _potree.module.js_), this folder also includes various decoders in the _workers_ subfolder, and a sample dataset from https://github.com/tentone/potree-core.
 
 ## Usage
 
-- copy this _contents_ folder to your Forge application (the _data_ subfolder is not necessary)
-- include the Potree library and this extension in your HTML, after the Forge Viewer script:
+- copy this _contents_ folder to your APS application (the _data_ subfolder is not necessary)
+- include the Potree library and this extension in your HTML, after the APS Viewer script:
 
 ```html
 
diff --git a/public/extensions/TransformationExtension/README.md b/public/extensions/TransformationExtension/README.md
index 9c06176..6494ee7 100644
--- a/public/extensions/TransformationExtension/README.md
+++ b/public/extensions/TransformationExtension/README.md
@@ -1,6 +1,6 @@
 # Transform
 
-[Demo](https://forge-extensions.autodesk.io/?extension=TransformationExtension)
+[Demo](https://aps-extensions.autodesk.io/?extension=TransformationExtension)
 
 Allows moving elements on the model.
 
diff --git a/public/extensions/TransformationExtension/contents/main.js b/public/extensions/TransformationExtension/contents/main.js
index 51d189e..07dc1ad 100644
--- a/public/extensions/TransformationExtension/contents/main.js
+++ b/public/extensions/TransformationExtension/contents/main.js
@@ -1,6 +1,6 @@
 /////////////////////////////////////////////////////////////////////
 // Copyright (c) Autodesk, Inc. All rights reserved
-// Written by Forge Partner Development
+// Written by APS Partner Development
 //
 // Permission to use, copy, modify, and distribute this software in
 // object code form for any purpose and without fee is hereby granted,
diff --git a/public/extensions/config.json b/public/extensions/config.json
index 3d7a83f..2bb333b 100644
--- a/public/extensions/config.json
+++ b/public/extensions/config.json
@@ -1 +1 @@
-{"Extensions":[{"name":"BasicSkeleton","displayname":"Basic Skeleton","description":"Basic Skeleton of an extension, can be used as boilerplate code.","options":{},"viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":["main.css"],"jsfiles":["main.js"]},"includeinlist":"false"},{"name":"CameraRotation","displayname":"Camera Rotation","description":"Rotates the model continously untill stopped, can be used for demoing a large model.","options":{},"loadonstartup":"false","filestoload":{"cssfiles":["main.css"],"jsfiles":["main.js"]},"gif":"extension.gif","includeinlist":"true"},{"name":"CustomPropertiesExtension","displayname":"Custom Properties","description":"Custom Properties example, info provided in config is showed in Properties palette based on selected object's dbId.","options":{"properties":{"1":{"My Group":{"My Property":"My Property Value","My Second Property":"My Second Property Value"}},"3":{"My Other Group":{"My Other Property":"My Other Property Value"}}}},"editoptions":"true","viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":[],"jsfiles":["main.js"]},"bloglink":"https://github.com/autodesk-forge/forge-extensions/tree/master/public/extensions/CustomPropertiesExtension","includeinlist":"true","gif":"extension.gif","externaldependencies":[]},{"name":"DrawToolExtension","displayname":"Draw Tool","description":"A simple tool that will allow us to draw boxes and spheres.","options":{},"editoptions":"true","viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":["main.css"],"jsfiles":["main.js"]},"bloglink":"https://forge.autodesk.com/blog/custom-tools-forge-viewer","includeinlist":"true","gif":"extension.gif"},{"name":"GoogleMapsLocator","displayname":"GoogleMapsLocator","description":"Show location of model on google maps.","options":{"lattitude":0,"longitude":0},"editoptions":"true","viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":["main.css"],"jsfiles":["main.js"]},"includeinlist":"true","gif":"extension.gif","externaldependencies":[{"type":"css","link":"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css"},{"type":"js","link":"https://maps.googleapis.com/maps/api/js?key="}]},{"name":"IconMarkupExtension","displayname":"Icon Markups","description":"Markups example, based on options provided in config, info is showed on particular object referring dbid.","options":{"icons":[{"dbId":9256,"label":"30°C","css":"fas fa-thermometer-full"},{"dbId":920,"label":"35°C","css":"fas fa-thermometer-full"},{"dbId":1626,"label":"45°C","css":"fas fa-thermometer-empty"}],"button":{"icon":"fa-thermometer-half","tooltip":"Show Temperature"}},"editoptions":"true","viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":["main.css"],"jsfiles":["main.js"]},"bloglink":"https://github.com/autodesk-forge/forge-extensions/tree/master/public/extensions/IconMarkupExtension","includeinlist":"true","gif":"extension.gif","externaldependencies":[{"type":"css","link":"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css"}]},{"name":"NestedViewerExtension","displayname":"Viewer in Viewer","description":"Nested viewer where viewables related to the currently loaded model can be viewed.","options":{"filter":["2d","3d"],"crossSelection":true},"editoptions":"true","viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":["main.css"],"jsfiles":["main.js"]},"gif":"extension.gif","includeinlist":"true","bloglink":"https://github.com/autodesk-forge/forge-extensions/tree/master/public/extensions/NestedViewerExtension/README.md"},{"name":"PotreeExtension","displayname":"Potree Point Clouds","description":"Forge Viewer extension for loading and rendering Potree point clouds.","options":{"url":"/extensions/PotreeExtension/contents/data/lion_takanawa/cloud.js","position":[0,0,0],"scale":[5,5,5]},"editoptions":"true","viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":[],"jsfiles":["potree.js","PotreeExtension.js"]},"gif":"extension.gif","includeinlist":"true","bloglink":""},{"name":"TransformationExtension","displayname":"Transformation","description":"Pupose of this extension is to select an element and move or transform it in any direction.","options":{},"loadonstartup":"false","filestoload":{"jsfiles":["main.js"],"cssfiles":[]},"gif":"extension.gif","includeinlist":"true","externaldependencies":[{"type":"css","link":"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css"}]}],"ListConfig":{"CreateList":"true","ListId":"extensionlist"},"InbuiltExtensions":[{"name":"Autodesk.Fusion360.Animation","default":"false","includeinlist":"false"},{"name":"Autodesk.Section","default":"true"},{"name":"Autodesk.DefaultTools.NavTools","default":"true"},{"name":"Autodesk.Hyperlink","default":"false","includeinlist":"false"},{"name":"Autodesk.Measure","default":"true"},{"name":"Autodesk.Viewing.FusionOrbit","default":"false"},{"name":"Autodesk.BimWalk","default":"true"},{"name":"Autodesk.GoHome","default":"false","includeinlist":"false"},{"name":"Autodesk.Explode","default":"true"},{"name":"Autodesk.FullScreen","default":"true"},{"name":"Autodesk.LayerManager","default":"false","includeinlist":"false"},{"name":"Autodesk.ModelStructure","default":"true"},{"name":"Autodesk.PropertiesManager","default":"true"},{"name":"Autodesk.ViewerSettings","default":"false","includeinlist":"false"},{"name":"Autodesk.Viewing.Wireframes","default":"false","includeinlist":"false"},{"name":"Autodesk.Viewing.ZoomWindow","default":"false"},{"name":"Autodesk.SplitScreen","default":"false"},{"name":"Autodesk.PDF","default":"false","includeinlist":"false"},{"name":"Autodesk.DocumentBrowser","default":"false"},{"name":"Autodesk.Geolocation","default":"false","includeinlist":"false"},{"name":"Autodesk.CrossFadeEffects","default":"false","includeinlist":"false"},{"name":"Autodesk.Edit2D","default":"false","includeinlist":"false"},{"name":"Autodesk.ViewCubeUi","default":"true"},{"name":"Autodesk.BIM360.Minimap","default":"false"},{"name":"Autodesk.BIM360.GestureDocumentNavigation","default":"false","includeinlist":"false"},{"name":"Autodesk.BIM360.RollCamera","default":"false","includeinlist":"false"},{"name":"Autodesk.Viewing.SceneBuilder","default":"false","includeinlist":"false"},{"name":"Autodesk.Snapping","default":"false"},{"name":"Autodesk.Viewing.Popout","default":"false","includeinlist":"false"},{"name":"Autodesk.VisualClusters","default":"false"},{"name":"Autodesk.AEC.LevelsExtension","default":"false","includeinlist":"false"}],"InbuiltExtensionsConfig":{"CreateList":"true","ListId":"inbuiltextensionlist"}}
\ No newline at end of file
+{"Extensions":[{"name":"AecGridsExtension","displayname":"AEC Grids","description":"A Forge Viewer extension for loading and rendering Revit Grids by AEC Model Data","options":{"alertOnDefaultFailure":true,"autoUnloadOnNoAecModelData":true},"editoptions":"true","viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":["main.css"],"jsfiles":["main.js"]},"bloglink":"","includeinlist":"true","externaldependencies":[{"type":"js","link":"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"},{"type":"js","link":"https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/jstree.min.js"},{"type":"css","link":"https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/themes/default/style.min.css"},{"type":"css","link":"https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/themes/default-dark/style.min.css"}]},{"name":"BasicSkeleton","displayname":"Basic Skeleton","description":"Basic Skeleton of an extension, can be used as boilerplate code.","options":{},"viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":["main.css"],"jsfiles":["main.js"]},"includeinlist":"false"},{"name":"CameraRotation","displayname":"Camera Rotation","description":"Rotates the model continously untill stopped, can be used for demoing a large model.","options":{},"loadonstartup":"false","filestoload":{"cssfiles":["main.css"],"jsfiles":["main.js"]},"gif":"extension.gif","includeinlist":"true"},{"name":"CustomPropertiesExtension","displayname":"Custom Properties","description":"Custom Properties example, info provided in config is showed in Properties palette based on selected object's dbId.","options":{"properties":{"1":{"My Group":{"My Property":"My Property Value","My Second Property":"My Second Property Value"}},"3":{"My Other Group":{"My Other Property":"My Other Property Value"}}}},"editoptions":"true","viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":[],"jsfiles":["main.js"]},"bloglink":"https://github.com/autodesk-forge/forge-extensions/tree/master/public/extensions/CustomPropertiesExtension","includeinlist":"true","gif":"extension.gif","externaldependencies":[]},{"name":"DrawToolExtension","displayname":"Draw Tool","description":"A simple tool that will allow us to draw boxes and spheres.","options":{},"editoptions":"true","viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":["main.css"],"jsfiles":["main.js"]},"bloglink":"https://forge.autodesk.com/blog/custom-tools-forge-viewer","includeinlist":"true","gif":"extension.gif"},{"name":"GoogleMapsLocator","displayname":"GoogleMapsLocator","description":"Show location of model on google maps.","options":{"lattitude":0,"longitude":0},"editoptions":"true","viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":["main.css"],"jsfiles":["main.js"]},"includeinlist":"true","gif":"extension.gif","externaldependencies":[{"type":"css","link":"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css"},{"type":"js","link":"https://maps.googleapis.com/maps/api/js?key="}]},{"name":"IconMarkupExtension","displayname":"Icon Markups","description":"Markups example, based on options provided in config, info is showed on particular object referring dbid.","options":{"icons":[{"dbId":9256,"label":"30°C","css":"fas fa-thermometer-full"},{"dbId":920,"label":"35°C","css":"fas fa-thermometer-full"},{"dbId":1626,"label":"45°C","css":"fas fa-thermometer-empty"}],"button":{"icon":"fa-thermometer-half","tooltip":"Show Temperature"}},"editoptions":"true","viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":["main.css"],"jsfiles":["main.js"]},"bloglink":"https://github.com/autodesk-forge/forge-extensions/tree/master/public/extensions/IconMarkupExtension","includeinlist":"true","gif":"extension.gif","externaldependencies":[{"type":"css","link":"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css"}]},{"name":"NestedViewerExtension","displayname":"Viewer in Viewer","description":"Nested viewer where viewables related to the currently loaded model can be viewed.","options":{"filter":["2d","3d"],"crossSelection":true},"editoptions":"true","viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":["main.css"],"jsfiles":["main.js"]},"gif":"extension.gif","includeinlist":"true","bloglink":"https://github.com/autodesk-forge/forge-extensions/tree/master/public/extensions/NestedViewerExtension/README.md"},{"name":"PotreeExtension","displayname":"Potree Point Clouds","description":"Forge Viewer extension for loading and rendering Potree point clouds.","options":{"url":"/extensions/PotreeExtension/contents/data/lion_takanawa/cloud.js","position":[0,0,0],"scale":[5,5,5]},"editoptions":"true","viewerversion":"7.*","loadonstartup":"false","filestoload":{"cssfiles":[],"jsfiles":["potree.js","PotreeExtension.js"]},"gif":"extension.gif","includeinlist":"true","bloglink":""},{"name":"TransformationExtension","displayname":"Transformation","description":"Pupose of this extension is to select an element and move or transform it in any direction.","options":{},"loadonstartup":"false","filestoload":{"jsfiles":["main.js"],"cssfiles":[]},"gif":"extension.gif","includeinlist":"true","externaldependencies":[{"type":"css","link":"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css"}]}],"ListConfig":{"CreateList":"true","ListId":"extensionlist"},"InbuiltExtensions":[{"name":"Autodesk.Fusion360.Animation","default":"false","includeinlist":"false"},{"name":"Autodesk.Section","default":"true"},{"name":"Autodesk.DefaultTools.NavTools","default":"true"},{"name":"Autodesk.Hyperlink","default":"false","includeinlist":"false"},{"name":"Autodesk.Measure","default":"true"},{"name":"Autodesk.Viewing.FusionOrbit","default":"false"},{"name":"Autodesk.BimWalk","default":"true"},{"name":"Autodesk.GoHome","default":"false","includeinlist":"false"},{"name":"Autodesk.Explode","default":"true"},{"name":"Autodesk.FullScreen","default":"true"},{"name":"Autodesk.LayerManager","default":"false","includeinlist":"false"},{"name":"Autodesk.ModelStructure","default":"true"},{"name":"Autodesk.PropertiesManager","default":"true"},{"name":"Autodesk.ViewerSettings","default":"false","includeinlist":"false"},{"name":"Autodesk.Viewing.Wireframes","default":"false","includeinlist":"false"},{"name":"Autodesk.Viewing.ZoomWindow","default":"false"},{"name":"Autodesk.SplitScreen","default":"false"},{"name":"Autodesk.PDF","default":"false","includeinlist":"false"},{"name":"Autodesk.DocumentBrowser","default":"false"},{"name":"Autodesk.Geolocation","default":"false","includeinlist":"false"},{"name":"Autodesk.CrossFadeEffects","default":"false","includeinlist":"false"},{"name":"Autodesk.Edit2D","default":"false","includeinlist":"false"},{"name":"Autodesk.ViewCubeUi","default":"true"},{"name":"Autodesk.BIM360.Minimap","default":"false"},{"name":"Autodesk.BIM360.GestureDocumentNavigation","default":"false","includeinlist":"false"},{"name":"Autodesk.BIM360.RollCamera","default":"false","includeinlist":"false"},{"name":"Autodesk.Viewing.SceneBuilder","default":"false","includeinlist":"false"},{"name":"Autodesk.Snapping","default":"false"},{"name":"Autodesk.Viewing.Popout","default":"false","includeinlist":"false"},{"name":"Autodesk.VisualClusters","default":"false"},{"name":"Autodesk.AEC.LevelsExtension","default":"false","includeinlist":"false"}],"InbuiltExtensionsConfig":{"CreateList":"true","ListId":"inbuiltextensionlist"}}
\ No newline at end of file
diff --git a/public/extensions/extensionloader.js b/public/extensions/extensionloader.js
index cc9825b..3f93ae6 100644
--- a/public/extensions/extensionloader.js
+++ b/public/extensions/extensionloader.js
@@ -1,6 +1,6 @@
 /////////////////////////////////////////////////////////////////////
 // Copyright (c) Autodesk, Inc. All rights reserved
-// Written by Forge Partner Development
+// Written by APS Partner Development
 //
 // Permission to use, copy, modify, and distribute this software in
 // object code form for any purpose and without fee is hereby granted,
diff --git a/public/index.html b/public/index.html
index cac466a..8d58cd3 100644
--- a/public/index.html
+++ b/public/index.html
@@ -2,7 +2,7 @@
 
 
 
-  Forge Extensions
+  APS Extensions
   
   
   
@@ -11,16 +11,17 @@
   
   
   
+  
   
-  
+  
   
   
   
   
   
   
-  
-  
+  
+  
   
   
   
@@ -33,14 +34,14 @@
       
       
@@ -65,7 +66,7 @@
       
       
  Select a model to load viewer and extensions
-
+
@@ -109,7 +110,7 @@ the object and select "Translate". Note: Technically your bucket name is required to be globally unique across the entire platform - to keep things simple with this tutorial your client ID will be prepended by default to your bucket name and in turn masked by the UI so you only have to make sure your bucket name is unique within - your current Forge app. + your current APS app.