Home Reference Source Repository

magicad/magicad.js

import base64 from '../base64/base64';
import {
    _addAuthToken,
    _extendQModelOptions,
    _setDiagramImages,
    _getResourceQueryUrl
} from './magicad-fn';
import OidcAuthService from '../auth/oidcauth';
import UserManagerSettingsFactory from '../auth/oidc-clientsettings-factory';
import SigninService from '../auth/signin';
import X3DViewService from '../x3dviewservice/X3DViewService.js';
import axios from 'axios';
import utils from '../utils';

export default class MagiCAD {
    /**
     * @private
     * @param {*} fingerprintService
     * @param {*} diagramService
     * @param {*} ahuCalculationService
     * @param {*} signinService (optional) Could be created also with init() function
     * @param {*} x3dViewService (optional) Could be created also with init() function
     */
    constructor(fingerprintService, diagramService, ahuCalculationService, signinService, x3dViewService) {
        /**
         * @private
         */
        this.fingerprintService = fingerprintService;
        /**
         * @private
         */
        this.diagramService = diagramService;
        /**
         * @private
         */
        this.ahuCalculationService = ahuCalculationService;
        /**
         * @private
         */
        this.signinService = signinService;
        /**
         * @private
         */
        this.x3dViewService = x3dViewService;
        /**
         * @private
         */
        this.onFindOptimalSpeed = undefined;
        /**
         * @private
         */
        this.onGetDpTot = undefined;
        /**
         * @private
         */
        this.onLoad = undefined;
        /**
         * @private
         */
        this.onLoadError = undefined;
        /**
         * @private
         */
        this.onRenderX3D = undefined;
    }

    /**
     * Initialize MagiCAD Widget SDK
     * 
     * @param {object} options example object:
     * { 
     *  client_id: 'your_client_id', 
     *  popup_redirect_uri: 'callback file url',
     *  enableX3D: true, // default value is true and x3dom library will be loaded
     *  onLoadUser: function(user) { console.log(user.username); }, // Set event handler to get signed in user if any exists e.g. case when page refreshed
     *  onSignin: function(user) { console.log(user.username); }, // Set event handler to perform some action on user signin
     *  onSignout: function() { console.log('user signed out'); }, // Set event handler to perform some action on user signout
     *  onRenderX3D: function() { console.log('x3d rendered'); }, // Set event handler to perform some action after rendering x3d image
     *  onGetDpTot function(result) { console.log(result); }, // Set event handler to perform some action with calculation result
     *  onFindOptimalSpeed: function(result) { console.log(result); } // Set event handler to perform some action with calculation result
     * }
     * @param {object} context (optional)
     */
    init(options, context) {
        // If not given already in constructor
        if (!this.signinService) {
            let factory = new UserManagerSettingsFactory(options);
            let oidcAuthService = new OidcAuthService(factory);
            this.signinService = new SigninService(oidcAuthService);
        }
        if (!this.signinService)
            throw "MagiCAD Widget SDK initialization failed!";

        // Set X3DViewService
        if (options.enableX3D === undefined || options.enableX3D === null) {
            options.enableX3D = true; // default value if not given
        }
        // If not given already in contructor
        if (!this.x3dViewService)
            this.x3dViewService = options.enableX3D ? new X3DViewService() : undefined;

        if (this.signinService) {
            if (typeof options.onLoadUser === 'function') {
                this.signinService && this.signinService.getUser()
                    .then(user => {
                        options.onLoadUser(user.profile);
                    })
                    .catch(err => {
                        options.onLoadUser(null);
                    });
            }
            if (typeof options.onSignin === 'function') {
                this.signinService.onSignin(options.onSignin);
            }
            if (typeof options.onSignout === 'function') {
                this.signinService.onSignout(options.onSignout);
            }
        }

        if (typeof options.onLoad === 'function') {
            this.onLoad = options.onLoad;
        }

        if (typeof options.onLoadError === 'function') {
            this.onLoadError = options.onLoadError;
        }
        if (typeof options.onRenderX3D === 'function') {
            this.onRenderX3D = options.onRenderX3D;
        }
        if (typeof options.onGetDpTot === 'function') {
            this.onGetDpTot = options.onGetDpTot;
        }
        if (typeof options.onFindOptimalSpeed === 'function') {
            this.onFindOptimalSpeed = options.onFindOptimalSpeed;
        }

        if (typeof context === 'object') {
            context.MagiCAD = Object.assign(context.MagiCAD, this);
            context.MagiCAD.__proto__ = this.__proto__;
        }
    }

    /**
     * Return information is user logged in.
     * 
     * @return boolean value is used logged in     
     */
    isLoggedIn() {
        return this.signinService && this.signinService.isLoggedIn();
    }

    /**
     * Signin. Will open MagiCAD login popup.
     * 
     * @return {Promise<void>}
     */
    signin() {
        return this.signinService && this.signinService.signin();
    }

    /**
     * Signout current logged in user
     * 
     * @return {Promise<void>}
     */
    signout() {
        return this.signinService && this.signinService.signout();
    }

    /**
     * Return release info of MagiCAD Widget SDK in example Production 2.35.0     
     */
    getReleaseInfo() {
        return `${systemconfig.ENVIRONMENT} ${systemconfig.RELEASENUMBER}`;
    }

    /**
     * Load and initialize MagiCAD Cloud Javascript Library     
     * 
     * @param  {object} options { productId: string, articleNumber: string, manufacturerId: string }
     * @return {Promise<ProductData>} Successful response will contain product/variant data
     */
    load(options) {
        let userRequest = this.signinService && this.signinService.getUser(),
            fingerprintRequest = this.fingerprintService.getFingerprint();
        let promise = Promise.all([userRequest, fingerprintRequest]).then(([user, sessionId]) => {
            let baseProductUrl = systemconfig.APIURL + systemconfig.PRODUCTPATH;
            let productUrl;
            if (options.productId)
                productUrl = baseProductUrl + options.productId;
            else if (options.articleNumber && options.manufacturerId)
                productUrl = baseProductUrl + options.manufacturerId + '/' + encodeURI(options.articleNumber);
            if (options.logoContainerId)
                this.renderMagiCADLogo(options.logoContainerId);

            return axios.get(productUrl).then((response) => {
                if (!response || response.status !== 200 || !response.data)
                    return null;

                // Finalize Attachments with auth tokens
                response.data.Attachments.forEach((val) => {
                    val.Uri = _addAuthToken(user, sessionId, val.Uri, options.applicationId);
                });
                // Finalize DXF resources with auth tokens
                response.data.DxfUrl = _addAuthToken(user, sessionId, response.data.DxfUrl, options.applicationId);
                return response.data;
            }, (err) => {
                if (this.onLoadError)
                    this.onLoadError(err);
            });
        });
        if (this.onLoad) {
            promise.then(this.onLoad, this.onLoadError);
        }
        return promise;
    }

    /**
     * Return dimension image query url
     * 
     * @param {object} options { QModelId: Optional QModelId of model, QModelParameters: Optional QModelParameters of model, 
     *                           ViewMode: one value of { 0=Rendered, 1=DimensionLabels, 2=DimensionValues },
     *                           ViewPosition: one value of { 0=Default, 1=Front, 2=Right, 3=Left, 4=Top, 5=Bottom, 6=IsometricLeft, 7=IsometricRight } }
     * @returns Dimension image url
     */
    getDimensionImageQueryUrl(options) {
        return _getResourceQueryUrl(options, systemconfig.DIMENSIONSIMAGEQUERYPATH);
    }

    /**
     * 
     * Return image query url
     * 
     * @param {object} options { QModelId: Optional QModelId of model, QModelParameters: Optional QModelParameters of model,
     *                           ViewPosition: one value of { 0=Default, 1=Front, 2=Right, 3=Left, 4=Top, 5=Bottom, 6=IsometricLeft, 7=IsometricRight } }
     * @returns Image url
     */
    getImageQueryUrl(options) {
        return _getResourceQueryUrl(options, systemconfig.IMAGEQUERYPATH);
    }

    /**
     * @private
     * Renders X3D Image container to given container
     * 
     * @param {object} options  {x3dUri: location of x3d, container: identifier of the container for x3d, imageUri: location of image }
     *                          Optionally can be set:
     *                          { QModelId: Optional QModelId of model, QModelParameters: Optional QModelParameters of model, 
     *                            ViewMode: one value of { 0=Rendered, 1=DimensionLabels, 2=DimensionValues }
     * @param {boolean} update Update existing container with new image url
     * @return {Promise} result as promise
     */
    renderX3D(options, update) {
        if (!this.x3dViewService) {
            return new Promise((resolve, reject) => {
                reject("Enable X3D in MagiCAD.init() function");
            });
        }
        let queryObject = _extendQModelOptions(options);
        if (queryObject.QModelId) {
            let x3dQueryPath = systemconfig.APIURL + systemconfig.X3DQUERYPATH;
            let imageQueryPath = systemconfig.APIURL + systemconfig.IMAGEQUERYPATH;

            let x3dQueryUrl = x3dQueryPath + base64.b64EncodeUnicode(JSON.stringify(queryObject));
            let imageQueryUrl = imageQueryPath + base64.b64EncodeUnicode(JSON.stringify(queryObject));
            let queryOptions = {
                x3dUri: x3dQueryUrl,
                containerId: options.containerId,
                imageUri: imageQueryUrl
            };
            let promise = this.x3dViewService.loadX3D(queryOptions, update);
            if (this.onRenderX3D) {
                return promise.then((result) => {
                    this.onRenderX3D();
                    return result;
                });
            }
        }

        let promise = this.x3dViewService.loadX3D(options, update);
        if (this.onRenderX3D) {
            return promise.then((result) => {
                this.onRenderX3D();
                return result;
            });
        }
        return promise;
    }

    /**
     * Render MagiCAD logo to container with specified identified
     * 
     * @param {string} containerId Container identifier
     * @param {boolean} transparent Boolean value should transparent image be used
     */
    renderMagiCADLogo(containerId, transparent) {
        let container = document.getElementById(containerId);
        if (!container)
            return;
        let data = {
            ImageUrl: transparent ?
                systemconfig.MAGICLOUDLOGOTRANSPARENT : systemconfig.MAGICLOUDLOGO,
            Alt: 'Powered by MagiCAD',
            Title: 'Powered by MagiCAD'
        };
        container.innerHTML = `<img src="${data.ImageUrl}" alt="${data.Alt}" title="${data.Title}" />`;
    }

    /**
     * Render MagiCAD logo to container with specified identified
     * @param {*} containerId 
     * @param {*} transparent 
     * @deprecated Use renderMagiCADLogo instead
     */
    renderMagiCloudLogo(containerId, transparent) {
        this.renderMagiCADLogo(containerId, transparent);
    }

    /**
     * Set specified diagram and bind click event to given elementId. Click event will add possible data to given dataElementId
     * @param {object} data Product data
     * @param {object} options example { type: diagramType, elementId: 'el', dataElementId: 'dataEl' } 
     * where diagramType is one of { Ventilation, Heating, Cooling, SupplyAir, ExtractAir, Radiator, Pump }
     */
    setDiagram(data, options) {
        if (!options || !options.elementId || !options.type ||
            !data.Diagrams || !data.Diagrams[options.type])
            return;

        let element = document.getElementById(options.elementId);
        if (!element)
            return;

        element.innerHTML = '';
        let img = utils.createElement('img', {
            src: data.Diagrams[options.type].BaseUrl,
            alt: options.type
        });
        element.appendChild(img);

        // Set diagram data if exists
        let hasData = data.Diagrams[options.type].HasData;
        if (hasData) {
            let baseUrl = data.Diagrams[options.type].BaseUrl;
            let dataElement = document.getElementById(options.dataElementId);
            if (!dataElement)
                return;
            if (options.operationPoint && options.operationPoint.x && options.operationPoint.y &&
                options.operationPoint.x >= 0 && options.operationPoint.y >= 0) {

                let diagram = {
                    URL: baseUrl,
                    DataURL: hasData ? baseUrl + '/data' : null,
                    operationPoint: options.operationPoint,
                    HasData: hasData
                };
                _setDiagramImages(this.diagramService, img, dataElement, diagram, options.type);
            }
            let that = this;
            img.addEventListener('click', (event) => {
                let diagram = that.diagramService.getDiagram({
                    target: img,
                    data: {
                        BaseURL: baseUrl,
                        HasData: hasData
                    },
                    pageX: event.pageX,
                    pageY: event.pageY
                });
                that.diagramService.getOperatingPoint(diagram).then(() => {
                    _setDiagramImages(that.diagramService, img, dataElement, diagram, options.type);
                }, () => {
                    // Failure when performing getOperatingPoint
                });
            });
        }
    }

    /**
     * Get pressure drop of curve at specific flow.
     * @return Example {Succeed: true, Value: 27.870676}
     * @param {string} productId Product identifier (GUID)
     * @param {string} systemTypeId Value = {Unknown, AnyFluid, SupplyFluid, ReturnFluid, ColdWater, HotWater, FireHydrant, HotSupplyFluid, Sprinkler, Sewer, AnyAir, SupplyAir, ExtractAir, OutdoorSupply, OutdoorExhau}
     * @param {number} qv Specified flow
     * @param {number} fanSpeed Specified Fan Speed
     */
    getDpTot(productId, systemTypeId, qv, fanSpeed) {
        let promise = this.ahuCalculationService.getDpTot(productId, systemTypeId, qv, fanSpeed);
        if (this.onGetDpTot) {
            promise.then(this.onGetDpTot);
        }
        return promise;
    }

    /**
     * Find suitable fanSpeed for given operation point.
     * @return Example {PressureDrop: 107.4791, Succeed: true, Value: 6}
     * @param {string} productId Product identifier (GUID)
     * @param {string} systemTypeId Value = {Unknown, AnyFluid, SupplyFluid, ReturnFluid, ColdWater, HotWater, FireHydrant, HotSupplyFluid, Sprinkler, Sewer, AnyAir, SupplyAir, ExtractAir, OutdoorSupply, OutdoorExhau}
     * @param {number} qv Specified flow
     * @param {number} dpTot Pressure drop value
     */
    findOptimalSpeed(productId, systemTypeId, qv, dpTot) {
        let promise = this.ahuCalculationService.findOptimalSpeed(productId, systemTypeId, qv, dpTot);
        if (this.onFindOptimalSpeed) {
            promise.then(this.onFindOptimalSpeed);
        }
        return promise;
    }
}