Search code examples
javascriptautodesk-forgeautodesk-viewer

Add toolbar buttons to embedded viewer


I was following the tutorial on how to customize the viewer with a publicly shared file described here I would like to add a button to my viewer however I keep getting the error Uncaught (in promise) Extension not found: HandleSelectionExtension. Has it been registered(1)?. Here is the code for my js functions which I included withing my html page:

var viewer;
displayViewer();
//CODE FOR CUSTOMER VIEWER
function displayViewer(){
  //  assemblyname = '';
var embedURLfromA360 = "https://afasystemsinc2.autodesk360.com/shares/public/SH56a43QTfd62c1cd96838ebd0c0ce1c251d?mode=embed";


function getURN(onURNCallback) {
    $.get({
        url: embedURLfromA360.replace('public', 'metadata').replace('mode=embed', ''),
        dataType: 'json',
        success: function (metadata) {
            if (onURNCallback) {
                onURNCallback(metadata.success.body.viewing.views.viewableUrn);
            }
        }
    })
}

function getForgeToken(onTokenCallback) {
    $.post({
        url: embedURLfromA360.replace('public', 'sign').replace('mode=embed', 'oauth2=true'),
        data: '{}',
        success: function (oauth) {
            if (onTokenCallback)
                onTokenCallback(oauth.accessToken, oauth.validitySeconds);
        }
    });
}

getURN(function (urn) {
    var options = {
        env: 'AutodeskProduction',
        getAccessToken: getForgeToken
    };
    var documentId = 'urn:' + urn;
    Autodesk.Viewing.Initializer(options, function onInitialized() {
        Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
    });
});

// used by viewer.addEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, onToolbarCreated)

const onToolbarCreated = (e) => {
    const settingsTools = viewer.toolbar.getControl('settingsTools')
    /*
    settingsTools.removeControl('toolbar-modelStructureTool') 
    settingsTools.removeControl('toolbar-propertiesTool')            
    settingsTools.removeControl('toolbar-settingsTool')          
    settingsTools.removeControl('toolbar-fullscreenTool')           
*/
    viewer.removeEventListener(
        Autodesk.Viewing.TOOLBAR_CREATED_EVENT,
        onToolbarCreated)  
} 

/**
 * Autodesk.Viewing.Document.load() success callback.
 * Proceeds with model initialization.
 */
function onDocumentLoadSuccess(doc) {

    // A document contains references to 3D and 2D viewables.
    var viewables = Autodesk.Viewing.Document.getSubItemsWithProperties(doc.getRootItem(), {
        'type': 'geometry'
    }, true);
    if (viewables.length === 0) {
        console.error('Document contains no viewables.');
        return;
    }

    // Choose any of the avialble viewables
    var initialViewable = viewables[0];
    var svfUrl = doc.getViewablePath(initialViewable);
    var modelOptions = {
        sharedPropertyDbPath: doc.getPropertyDbPath()
    };

   // var viewerDiv = document.getElementById('MyViewerDiv');
 //viewer = new Autodesk.Viewing.Private.GuiViewer3D(viewerDiv);

 viewer = new Autodesk.Viewing.Private.GuiViewer3D(document.getElementById('MyViewerDiv'),{extensions:['HandleSelectionExtension', 'ModelSummaryExtension']});

    // Our customization of the Viewer: we are removing some of the toolbar buttons
    viewer.addEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, onToolbarCreated)
    viewer.start(svfUrl, modelOptions, onLoadModelSuccess, onLoadModelError);
}

/**
 * Autodesk.Viewing.Document.load() failuire callback.
 */
function onDocumentLoadFailure(viewerErrorCode) {}

/**
 * viewer.loadModel() success callback.
 * Invoked after the model's SVF has been initially loaded.
 * It may trigger before any geometry has been downloaded and displayed on-screen.
 */
function onLoadModelSuccess(model) {}

/**
 * viewer.loadModel() failure callback.
 * Invoked when there's an error fetching the SVF file.
 */
function onLoadModelError(viewerErrorCode) {}
}
function view(){
    console.log(viewer);
}

function destroyViewer(){
    console.log(viewer);
    console.log(viewer.contextMenu);
    viewer.finish();
viewer = null;
Autodesk.Viewing.shutdown();
console.log(viewer);
}

var ext = '';
var dim = '';
var assemblyname = '';

function getAssemblyName(){
assemblyname = sessionStorage.getItem("assemblyName");
//var ext = partname.substr(partname.lastIndexOf('.'));
ext = assemblyname.split('.');
dim = ext[0] + ':1';
//dim = ext[0];
console.log(assemblyname);
console.log(dim);
if (dim !== ''){
    isolateSelected();
}
}


//function to get part name from __MachineParts.php
var partname = '';
var extension = '';
var namewithoutextension = '';
function getPartName(){
partname = sessionStorage.getItem("partName");
//var ext = partname.substr(partname.lastIndexOf('.'));
extension = partname.split('.');
namewithoutextension = extension[0] + ':1'
console.log(partname);
console.log(namewithoutextension);
if (namewithoutextension !== ''){
    isolateSelectedPart();
}
}

function isolateSelected(){
    console.log(dim);
    console.log(viewer);
   /* if (viewer == null){
        getItem();
    }*/
    viewer.search(dim, function(dbIds) {
   // viewer.search('"' + 'M-109408 FOLDING PLOUGH:2' + '"', function(dbIds) {
        console.log(dbIds.length);
       if (dbIds.length == 0){
        dim = ext[0] + ':2';
       // isolateSelected();
     }
        getSubset(dbIds, 'label', dim, function(dbIds) {
       // getSubset(dbIds, 'label', 'M-109408 FOLDING PLOUGH:2', function(dbIds) {
       // getSubset(dbIds, property.name, 'M-54439 POST TUBING:1', function(dbIds) {
            //getSubset(dbIds, property.name, property.value, function(dbIds){
            var it = viewer.model.getData().instanceTree;
            //console.log(it);
           for (i = 0; i<dbIds.length; i++){
               var namepart = it.getNodeName(dbIds[i]);
               if (namepart !== undefined){
            console.log(dbIds);
            console.log(namepart);}}
          /*  for (i = 121; i<381;i++){
                var dbId = i;
            var it = NOP_VIEWER.model.getData().instanceTree;
            var name = it.getNodeName(dbId);
            console.log(name);}*/
           // viewer.setBackgroundColor(255,0,0,255,0,0);
         //  viewer.setSelectionColor(new THREE.Color(1,1,1));
        // viewer.setSelectionColor(new THREE.Color(0xFFAB33));
        viewer.setSelectionColor(new THREE.Color(0xFFC000));
          //  viewer.setBackgroundOpacity(1.0);
            viewer.isolate(dbIds)
           viewer.select(dbIds);
            viewer.utilities.fitToView();
            $(window).scrollTop(300);
        })
    }, function(error) {})
}

function isolateSelectedPart(){
    console.log(namewithoutextension);
    if (viewer == null){
        getItem();
    }
    viewer.search(namewithoutextension, function(dbIds) {
   // viewer.search('"' + 'M-109408 FOLDING PLOUGH:2' + '"', function(dbIds) {
        console.log(dbIds.length);
        if (dbIds.length == 0){
            getItem();
        }
        getSubset(dbIds, 'label', namewithoutextension, function(dbIds) {
       // getSubset(dbIds, 'label', 'M-109408 FOLDING PLOUGH:2', function(dbIds) {
       // getSubset(dbIds, property.name, 'M-54439 POST TUBING:1', function(dbIds) {
            //getSubset(dbIds, property.name, property.value, function(dbIds){
            var it = viewer.model.getData().instanceTree;
            //console.log(it);
           for (i = 0; i<dbIds.length; i++){
               var namepart = it.getNodeName(dbIds[i]);
               if (namepart !== undefined){
            console.log(dbIds);
            console.log(namepart);}}
          /*  for (i = 121; i<381;i++){
                var dbId = i;
            var it = NOP_VIEWER.model.getData().instanceTree;
            var name = it.getNodeName(dbId);
            console.log(name);}*/
            viewer.setSelectionColor(new THREE.Color(0xFFC000));
           // viewer.setBackgroundOpacity(1.0);
            viewer.isolate(dbIds)
           viewer.select(dbIds);
            viewer.utilities.fitToView();
            $(window).scrollTop(300);
        })
    }, function(error) {})
}

//function to find the dbid of the part/assembly
function getSubset(dbIds, name, value, callback) {
    console.log("getSubset, dbIds.length before = " + dbIds.length)
    viewer.model.getBulkProperties(dbIds, {
        propFilter: [name],
        ignoreHidden: true
    }, function(data) {
        var newDbIds = []
        for (var key in data) {
            var item = data[key]
           // console.log(item.properties);
            if (item.properties[0].displayValue === value) {
                newDbIds.push(item.dbId)
            }
        }

        console.log("getSubset, dbIds.length after = " + newDbIds.length)

        callback(newDbIds)

    }, function(error) {})
}

class HandleSelectionExtension extends Autodesk.Viewing.Extension {
    constructor(viewer, options) {
        super(viewer, options);
        this._group = null;
        this._button = null;
    }

    load() {
        console.log('HandleSelectionExtension has been loaded');
        return true;
    }

    unload() {
        // Clean our UI elements if we added any
        if (this._group) {
            this._group.removeControl(this._button);
            if (this._group.getNumberOfControls() === 0) {
                this.viewer.toolbar.removeControl(this._group);
            }
        }
        console.log('HandleSelectionExtension has been unloaded');
        return true;
    }

    onToolbarCreated() {
        // Create a new toolbar group if it doesn't exist
        this._group = this.viewer.toolbar.getControl('allMyAwesomeExtensionsToolbar');
        if (!this._group) {
            this._group = new Autodesk.Viewing.UI.ControlGroup('allMyAwesomeExtensionsToolbar');
            this.viewer.toolbar.addControl(this._group);
        }

        // Add a new button to the toolbar group
        this._button = new Autodesk.Viewing.UI.Button('handleSelectionExtensionButton');
        this._button.onClick = (ev) => {
            const selection = this.viewer.getSelection();
this.viewer.clearSelection();
// Anything selected?
if (selection.length > 0) {
    let isolated = [];
    // Iterate through the list of selected dbIds
    selection.forEach((dbId) => {
        // Get properties of each dbId
        this.viewer.getProperties(dbId, (props) => {
            // Output properties to console
            console.log(props);
            console.log(props.name);
            // Ask if want to isolate
          //  if (confirm(`Isolate ${props.name} (${props.externalId})?`)) {
                isolated.push(dbId);
                this.viewer.isolate(isolated);
                //window.open('mailto:[email protected]?subject=subject&body='+props.name);
            //}
        });
    });
} else {
    // If nothing selected, restore
    this.viewer.isolate(0);
}
        };
        this._button.setToolTip('Isolate Part');
        this._button.addClass('handleSelectionExtensionIcon');
        this._group.addControl(this._button);
    }
}

Autodesk.Viewing.theExtensionManager.registerExtension('HandleSelectionExtension', HandleSelectionExtension);

//CODE FOR BUTTON TO ORDER A SPARE PART

class ModelSummaryExtension extends Autodesk.Viewing.Extension {
    constructor(viewer, options) {
        super(viewer, options);
        this._group = null;
        this._button = null;
    }

    load() {
        console.log('ModelSummaryExtension has been loaded');
        return true;
    }

    unload() {
        // Clean our UI elements if we added any
        if (this._group) {
            this._group.removeControl(this._button);
            if (this._group.getNumberOfControls() === 0) {
                this.viewer.toolbar.removeControl(this._group);
            }
        }
        console.log('ModelSummaryExtension has been unloaded');
        return true;
    }

    onToolbarCreated() {
        // Create a new toolbar group if it doesn't exist
        this._group = this.viewer.toolbar.getControl('allMyAwesomeExtensionsToolbar');
        if (!this._group) {
            this._group = new Autodesk.Viewing.UI.ControlGroup('allMyAwesomeExtensionsToolbar');
            this.viewer.toolbar.addControl(this._group);
        }

        // Add a new button to the toolbar group
        this._button = new Autodesk.Viewing.UI.Button('ModelSummaryExtensionButton');
        this._button.onClick = (ev) => {
            // Execute an action here
            // Get current selection
const selection = this.viewer.getSelection();
this.viewer.clearSelection();
// Anything selected?
if (selection.length > 0) {
    let isolated = [];
    // Iterate through the list of selected dbIds
    selection.forEach((dbId) => {
        // Get properties of each dbId
        this.viewer.getProperties(dbId, (props) => {
            // Output properties to console
            console.log(props);
            // Ask if want to isolate
            if (confirm(`Would you like to order a spare of ${props.name} ?`)) {
                isolated.push(dbId);
                this.viewer.isolate(isolated);
                window.open('mailto:[email protected]?subject=subject&body='+props.name);
            }
        });
    });
} else {
    // If nothing selected, restore
    this.viewer.isolate(0);
}
        };
        this._button.setToolTip('Order this Part');
        this._button.addClass('modelSummaryExtensionIcon');
        this._group.addControl(this._button);
    }
}

Autodesk.Viewing.theExtensionManager.registerExtension('ModelSummaryExtension', ModelSummaryExtension);

I have set up additional buttons before without any problems, does this have anything to do with the fact that I'm using a publicly shared model. Both buttons have that error. Any guidance or reason as to why this is happening would be greatly appreciated. Cheers!

UPDATE: I have troubleshooted the program by displaying console.log( Autodesk.Viewing.theExtensionManager.registerExtension('HandleSelectionExtension', HandleSelectionExtension); And I get the following message: "Uncaught ReferenceError: Cannot access 'HandleSelectionExtension' before initialization". Do i need to add a Autodesk.Viewing.Initializer(options, function() {. The tutorial did not include this. If i do need to include it, how would I implement it for the extension to successfully load. Thanks!

EDIT SIMPLE QUESTION: How can I add a viewer button extension to this tutorial.


Solution

  • In your extensions you rely on a v7 specific functionality, the Extension.prototype.onToolbarCreated(toolbar) - see https://forge.autodesk.com/en/docs/viewer/v7/change_history/changelog_v7/migration_guide_v6_to_v7/

    In order to migrate the above code to v7 all I have to change is the implementation of onDocumentLoadSuccess(). Here is the updated version

    function onDocumentLoadSuccess(doc) {
    
      // A document contains references to 3D and 2D viewables.
      var viewables = doc.getRoot().search({
        'type': 'geometry'
      });
      if (viewables.length === 0) {
        console.error('Document contains no viewables.');
        return;
      } 
    
      // Choose any of the avialble viewables
      var initialViewable = viewables[0];
      var svfUrl = doc.getViewablePath(initialViewable);
      var modelOptions = {
        //sharedPropertyDbPath: doc.getPropertyDbPath()
      };
    
      viewer = new Autodesk.Viewing.GuiViewer3D(document.getElementById('MyViewerDiv'),{extensions:['HandleSelectionExtension', 'ModelSummaryExtension']});
    
      // Our customization of the Viewer: we are removing some of the toolbar buttons
      viewer.addEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, onToolbarCreated)
      viewer.start(svfUrl, modelOptions, onLoadModelSuccess, onLoadModelError);
    }
    

    And also make sure you are referencing the v7 resources from your html page:

    <link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/style.min.css" type="text/css">
    <script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/viewer3D.js"></script>