Browse Source

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

David Catuhe 7 năm trước cách đây
mục cha
commit
b127ed625b

+ 5 - 5
Playground/js/frame.js

@@ -1,5 +1,5 @@
 (function () {
-    var snippetUrl = "https://babylonjs-api2.azurewebsites.net/snippets";
+    var snippetUrl = "//babylonjs-api2.azurewebsites.net/snippets";
     var currentSnippetToken;
     var engine;
     var fpsLabel = document.getElementById("fpsLabel");
@@ -43,7 +43,7 @@
         xhr.send(null);
     };
 
-    var showError = function(error) {
+    var showError = function (error) {
         console.warn(error);
     };
 
@@ -61,7 +61,7 @@
             }
 
             var canvas = document.getElementById("renderCanvas");
-            engine = new BABYLON.Engine(canvas, true, {stencil: true});
+            engine = new BABYLON.Engine(canvas, true, { stencil: true });
             BABYLON.Camera.ForceAttachControlToAlwaysPreventDefault = true;
 
             engine.runRenderLoop(function () {
@@ -158,7 +158,7 @@
 
                             if (refresh) {
                                 refresh.addEventListener("click", function () {
-                                compileAndRun(snippetCode);
+                                    compileAndRun(snippetCode);
                                 });
                             }
                         }
@@ -167,7 +167,7 @@
 
                 var hash = location.hash.substr(1);
                 currentSnippetToken = hash.split("#")[0];
-                if(!hash.split("#")[1]) hash += "#0";
+                if (!hash.split("#")[1]) hash += "#0";
 
                 xmlHttp.open("GET", snippetUrl + "/" + hash.replace("#", "/"));
                 xmlHttp.send();

+ 1 - 1
Playground/js/index.js

@@ -75,7 +75,7 @@
             markDirty();
         });
 
-        var snippetUrl = "https://babylonjs-api2.azurewebsites.net/snippets";
+        var snippetUrl = "//babylonjs-api2.azurewebsites.net/snippets";
         var currentSnippetToken;
         var currentSnippetTitle = null;
         var currentSnippetDescription = null;

+ 0 - 40
Viewer/assets/templates/default/defaultTemplate.html

@@ -1,43 +1,3 @@
-<style>
-    loading-screen {
-        position: absolute;
-        z-index: 100;
-        opacity: 1;
-        pointer-events: none;
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        -webkit-transition: opacity 2s ease;
-        -moz-transition: opacity 2s ease;
-        transition: opacity 2s ease;
-    }
-
-    viewer {
-        position: relative;
-        overflow: hidden;
-        /* Start stage */
-        flex: 1;
-        z-index: 1;
-        justify-content: center;
-        align-items: center;
-
-        width: 100%;
-        height: 100%;
-    }
-
-    overlay {
-        position: absolute;
-        z-index: 99;
-        opacity: 0;
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        -webkit-transition: opacity 1s ease;
-        -moz-transition: opacity 1s ease;
-        transition: opacity 1s ease;
-    }
-</style>
-
 <viewer></viewer>
 <loading-screen></loading-screen>
 <overlay></overlay>

+ 12 - 25
Viewer/assets/templates/default/defaultViewer.html

@@ -1,35 +1,22 @@
 <style>
-    .babylonjs-canvas {
+    viewer {
+        position: relative;
+        overflow: hidden;
+        /* Start stage */
         flex: 1;
+        z-index: 1;
+        justify-content: center;
+        align-items: center;
+
         width: 100%;
         height: 100%;
-        touch-action: none;
     }
 
-    nav-bar {
-        position: absolute;
-        height: 160px;
+    .babylonjs-canvas {
+        flex: 1;
         width: 100%;
-        bottom: 0;
-        background-color: rgba(0, 0, 0, 0.3);
-        color: white;
-        transition: 1s;
-        align-items: flex-start;
-        justify-content: space-around;
-        display: flex;
-
-        flex-direction: column;
-    }
-
-    /* Big screens have room for the entire navbar */
-
-    @media screen and (min-width: 768px) {
-        nav-bar {
-            align-items: center;
-            flex-direction: row;
-            justify-content: space-between;
-            height: 80px;
-        }
+        height: 100%;
+        touch-action: none;
     }
 </style>
 

+ 15 - 0
Viewer/assets/templates/default/loadingScreen.html

@@ -1,4 +1,19 @@
 <style>
+    /* Loading Screen element */
+
+    loading-screen {
+        position: absolute;
+        z-index: 100;
+        opacity: 1;
+        pointer-events: none;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        -webkit-transition: opacity 2s ease;
+        -moz-transition: opacity 2s ease;
+        transition: opacity 2s ease;
+    }
+
     img.loading-image {
         -webkit-animation: spin 2s linear infinite;
         animation: spin 2s linear infinite;

+ 26 - 0
Viewer/assets/templates/default/navbar.html

@@ -1,4 +1,30 @@
 <style>
+    nav-bar {
+        position: absolute;
+        height: 160px;
+        width: 100%;
+        bottom: 0;
+        background-color: rgba(0, 0, 0, 0.3);
+        color: white;
+        transition: 1s;
+        align-items: flex-start;
+        justify-content: space-around;
+        display: flex;
+
+        flex-direction: column;
+    }
+
+    /* Big screens have room for the entire navbar */
+
+    @media screen and (min-width: 768px) {
+        nav-bar {
+            align-items: center;
+            flex-direction: row;
+            justify-content: space-between;
+            height: 80px;
+        }
+    }
+
     div.flex-container {
         display: flex;
         width: 100%;

+ 17 - 5
Viewer/assets/templates/default/overlay.html

@@ -1,5 +1,17 @@
 <style>
-    .overlay {
+    overlay {
+        position: absolute;
+        z-index: 99;
+        opacity: 0;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        -webkit-transition: opacity 1s ease;
+        -moz-transition: opacity 1s ease;
+        transition: opacity 1s ease;
+    }
+
+    .overlay-item {
         width: 100%;
         height: 100%;
         display: none;
@@ -8,7 +20,7 @@
         background-color: rgba(121, 121, 121, 0.3);
     }
 
-    error.overlay {
+    error.overlay-item {
         background-color: rgba(121, 121, 121, 1);
     }
 
@@ -29,6 +41,6 @@
 <div id="close-button">
     <img src="{{closeImage}}" alt="{{closeText}}">
 </div>
-<help class="overlay"></help>
-<error class="overlay"></error>
-<share class="overlay"></share>
+<help class="overlay-item"></help>
+<error class="overlay-item"></error>
+<share class="overlay-item"></share>

+ 7 - 4
Viewer/dist/basicExample.html

@@ -18,11 +18,14 @@
 
     <body>
         <babylon model.title="Amazing Rabbit" model.subtitle="BabylonJS" model.thumbnail="https://www.babylonjs.com/img/favicon/apple-icon-144x144.png"
-            model.url="https://playground.babylonjs.com/scenes/Rabbit.babylon" default-viewer="true" templates.nav-bar.params.disable-on-fullscreen="true"></babylon>
+            model.url="https://playground.babylonjs.com/scenes/Rabbit.babylon" camera.behaviors.auto-rotate="0" templates.nav-bar.params.disable-on-fullscreen="true"></babylon>
         <script src="viewer.js"></script>
+        <script>
+            // a simple way of disabling auto init 
+            BabylonViewer.disableInit = true;
+            // Initializing the viewer on specific HTML tags.
+            BabylonViewer.InitTags('babylon');
+        </script>
     </body>
 
-</html>
-<html>
-
 </html>

+ 6 - 6
Viewer/dist/domExample.html

@@ -15,21 +15,21 @@
     </head>
 
     <body>
-        <babylon default-viewer="true">
+        <babylon extends="minimal">
             <model url="https://ugcorigin.s-microsoft.com/12/2e77b8e3-0000-0000-7a48-6505db2f0ef9/952/1508427934473.gltf" title="The Bus!"
                 subtitle="Remix3D" thumbnail="http://d33wubrfki0l68.cloudfront.net/7e08139ddee0ec38005f4232346c7f7386831300/fd934/githubuniverse/remix3d.png">
             </model>
             <camera>
-                <behaviors array="true">
-                    <behavior type="0"></behavior>
+                <behaviors>
+                    <auto-rotate type="0"></auto-rotate>
                 </behaviors>
             </camera>
-            <lights array="true">
-                <light type="1" shadow-enabled="true" position.y="0.5" direction.y="-1" intensity="4.5">
+            <lights>
+                <light1 type="1" shadow-enabled="true" position.y="0.5" direction.y="-1" intensity="4.5">
                     <shadow-config use-blur-exponential-shadow-map="true" use-kernel-blur="true" blur-kernel="64" blur-scale="4">
 
                     </shadow-config>
-                </light>
+                </light1>
             </lights>
         </babylon>
         <script src="viewer.js"></script>

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 944 - 838
Viewer/dist/viewer.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 37 - 157
Viewer/src/configuration/configuration.ts


+ 9 - 6
Viewer/src/configuration/loader.ts

@@ -1,5 +1,6 @@
 import { mapperManager } from './mappers';
-import { ViewerConfiguration, defaultConfiguration } from './configuration';
+import { ViewerConfiguration } from './configuration';
+import { getConfigurationType } from './types';
 
 import * as merge from 'lodash.merge';
 
@@ -11,11 +12,9 @@ export class ConfigurationLoader {
 
         let loadedConfig = merge({}, initConfig);
 
-        if (loadedConfig.defaultViewer) {
-            loadedConfig = merge(loadedConfig, defaultConfiguration);
-        } else {
-            loadedConfig = merge(defaultConfiguration, loadedConfig);
-        }
+        let extendedConfiguration = getConfigurationType(loadedConfig && loadedConfig.extends);
+
+        loadedConfig = merge(extendedConfiguration, loadedConfig);
 
         if (loadedConfig.configuration) {
 
@@ -42,6 +41,10 @@ export class ConfigurationLoader {
         }
     }
 
+    public getConfigurationType(type: string) {
+
+    }
+
     private loadFile(url: string): Promise<any> {
         let cacheReference = this.configurationCache;
         if (cacheReference[url]) {

+ 1 - 1
Viewer/src/configuration/mappers.ts

@@ -22,7 +22,7 @@ class HTMLMapper implements IMapper {
                 //convert html-style to json-style
                 let camelKey = kebabToCamel(key);
                 if (idx === split.length - 1) {
-                    let val: any = attr.nodeValue;
+                    let val: any = attr.nodeValue; // firefox warns nodeValue is deprecated, but I found no sign of it anywhere.
                     if (val === "true") {
                         val = true;
                     } else if (val === "false") {

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 126 - 0
Viewer/src/configuration/types/default.ts


+ 16 - 0
Viewer/src/configuration/types/index.ts

@@ -0,0 +1,16 @@
+import { minimalConfiguration } from './minimal';
+import { defaultConfiguration } from './default';
+
+let getConfigurationType = function (type: string) {
+    switch (type) {
+        case 'default':
+            return defaultConfiguration;
+        case 'minimal':
+            return minimalConfiguration;
+        default:
+            return defaultConfiguration;
+    }
+
+}
+
+export { getConfigurationType, defaultConfiguration, minimalConfiguration }

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 34 - 0
Viewer/src/configuration/types/minimal.ts


+ 9 - 4
Viewer/src/index.ts

@@ -14,10 +14,15 @@ import 'babylonjs-materials';
 
 import { InitTags } from './initializer';
 
-// promise polyfill
-global.Promise = require('es6-promise').Promise;
+// promise polyfill, if needed!
+global.Promise = Promise || require('es6-promise').Promise;
 
-InitTags();
+export let disableInit: boolean = false;
+
+setTimeout(() => {
+    if (disableInit) return;
+    InitTags();
+});
 
 // public API for initialization
-export { AbstractViewer, InitTags };
+export { InitTags };

+ 33 - 21
Viewer/src/templateManager.ts

@@ -9,21 +9,21 @@ export interface ITemplateConfiguration {
     params?: { [key: string]: string | number | boolean | object };
     events?: {
         // pointer events
-        pointerdown?: boolean | Array<string>;
-        pointerup?: boolean | Array<string>;
-        pointermove?: boolean | Array<string>;
-        pointerover?: boolean | Array<string>;
-        pointerout?: boolean | Array<string>;
-        pointerenter?: boolean | Array<string>;
-        pointerleave?: boolean | Array<string>;
-        pointercancel?: boolean | Array<string>;
+        pointerdown?: boolean | { [id: string]: boolean; };
+        pointerup?: boolean | { [id: string]: boolean; };
+        pointermove?: boolean | { [id: string]: boolean; };
+        pointerover?: boolean | { [id: string]: boolean; };
+        pointerout?: boolean | { [id: string]: boolean; };
+        pointerenter?: boolean | { [id: string]: boolean; };
+        pointerleave?: boolean | { [id: string]: boolean; };
+        pointercancel?: boolean | { [id: string]: boolean; };
         //click, just in case
-        click?: boolean | Array<string>;
+        click?: boolean | { [id: string]: boolean; };
         // drag and drop
-        dragstart?: boolean | Array<string>;
-        drop?: boolean | Array<string>;
+        dragstart?: boolean | { [id: string]: boolean; };
+        drop?: boolean | { [id: string]: boolean; };
 
-        [key: string]: boolean | Array<string> | undefined;
+        [key: string]: boolean | { [id: string]: boolean; } | undefined;
     }
 }
 
@@ -83,7 +83,7 @@ export class TemplateManager {
         }
 
         //build the html tree
-        let htmlTree = this.buildHTMLTree(templates).then(htmlTree => {
+        this.buildHTMLTree(templates).then(htmlTree => {
             internalInit(htmlTree, 'main');
         });
     }
@@ -102,6 +102,7 @@ export class TemplateManager {
         let promises = Object.keys(templates).map(name => {
             let template = new Template(name, templates[name]);
             this.templates[name] = template;
+            return template.initPromise;
         });
 
         return Promise.all(promises).then(() => {
@@ -125,7 +126,7 @@ export class TemplateManager {
         return this.containerElement.querySelector('canvas');
     }
 
-    public getTemplate(name: string) {
+    public getTemplate(name: string): Template | undefined {
         return this.templates[name];
     }
 
@@ -196,8 +197,14 @@ export class Template {
 
     public getChildElements(): Array<string> {
         let childrenArray: string[] = [];
-        for (let i = 0; i < this.fragment.children.length; ++i) {
-            childrenArray.push(kebabToCamel(this.fragment.children.item(i).nodeName.toLowerCase()));
+        //Edge and IE don't support frage,ent.children
+        let children = this.fragment.children;
+        if (!children) {
+            // casting to HTMLCollection, as both NodeListOf and HTMLCollection have 'item()' and 'length'.
+            children = <HTMLCollection>this.fragment.querySelectorAll('*');
+        }
+        for (let i = 0; i < children.length; ++i) {
+            childrenArray.push(kebabToCamel(children.item(i).nodeName.toLowerCase()));
         }
         return childrenArray;
     }
@@ -251,7 +258,7 @@ export class Template {
     // TODO - Should events be removed as well? when are templates disposed?
     private registerEvents() {
         if (this._configuration.events) {
-            Object.keys(this._configuration.events).forEach(eventName => {
+            for (let eventName in this._configuration.events) {
                 if (this._configuration.events && this._configuration.events[eventName]) {
                     let functionToFire = (selector, event) => {
                         this.onEventTriggered.notifyObservers({ event: event, template: this, selector: selector });
@@ -260,15 +267,20 @@ export class Template {
                     // if boolean, set the parent as the event listener
                     if (typeof this._configuration.events[eventName] === 'boolean') {
                         this.parent.addEventListener(eventName, functionToFire.bind(this, '#' + this.parent.id), false);
-                    } else {
-                        let selectorsArray: Array<string> = <Array<string>>this._configuration.events[eventName];
-                        selectorsArray.forEach(selector => {
+                    } else if (typeof this._configuration.events[eventName] === 'object') {
+                        let selectorsArray: Array<string> = Object.keys(this._configuration.events[eventName] || {});
+                        // strict null checl is working incorrectly, must override:
+                        let event = this._configuration.events[eventName] || {};
+                        selectorsArray.filter(selector => event[selector]).forEach(selector => {
+                            if (selector.indexOf('#') !== 0) {
+                                selector = '#' + selector;
+                            }
                             let htmlElement = <HTMLElement>this.parent.querySelector(selector);
                             htmlElement && htmlElement.addEventListener(eventName, functionToFire.bind(this, selector), false)
                         });
                     }
                 }
-            });
+            }
         }
     }
 

+ 77 - 49
Viewer/src/viewer/defaultViewer.ts

@@ -25,42 +25,49 @@ export class DefaultViewer extends AbstractViewer {
         // navbar
         let viewerElement = this.templateManager.getTemplate('viewer');
         let navbar = this.templateManager.getTemplate('navBar');
-        let navbarHeight = navbar.parent.clientHeight + 'px';
-
-        let navbarShown: boolean = true;
-        let timeoutCancel /*: number*/;
-
-        let triggerNavbar = function (show: boolean = false, evt: PointerEvent) {
-            // only left-click on no-button.
-            if (evt.button > 0) return;
-            // clear timeout
-            timeoutCancel && clearTimeout(timeoutCancel);
-            // if state is the same, do nothing
-            if (show === navbarShown) return;
-            //showing? simply show it!
-            if (show) {
-                navbar.parent.style.bottom = show ? '0px' : '-' + navbarHeight;
-                navbarShown = show;
-            } else {
-                let visibilityTimeout = 2000;
-                if (navbar.configuration.params && navbar.configuration.params.visibilityTimeout !== undefined) {
-                    visibilityTimeout = <number>navbar.configuration.params.visibilityTimeout;
-                }
-                // not showing? set timeout until it is removed.
-                timeoutCancel = setTimeout(function () {
-                    navbar.parent.style.bottom = '-' + navbarHeight;
+        if (viewerElement && navbar) {
+            let navbarHeight = navbar.parent.clientHeight + 'px';
+
+            let navbarShown: boolean = true;
+            let timeoutCancel /*: number*/;
+
+            let triggerNavbar = function (show: boolean = false, evt: PointerEvent) {
+                // only left-click on no-button.
+                if (!navbar || evt.button > 0) return;
+                // clear timeout
+                timeoutCancel && clearTimeout(timeoutCancel);
+                // if state is the same, do nothing
+                if (show === navbarShown) return;
+                //showing? simply show it!
+                if (show) {
+                    navbar.parent.style.bottom = show ? '0px' : '-' + navbarHeight;
                     navbarShown = show;
-                }, visibilityTimeout);
+                } else {
+                    let visibilityTimeout = 2000;
+                    if (navbar.configuration.params && navbar.configuration.params.visibilityTimeout !== undefined) {
+                        visibilityTimeout = <number>navbar.configuration.params.visibilityTimeout;
+                    }
+                    // not showing? set timeout until it is removed.
+                    timeoutCancel = setTimeout(function () {
+                        if (navbar) {
+                            navbar.parent.style.bottom = '-' + navbarHeight;
+                        }
+                        navbarShown = show;
+                    }, visibilityTimeout);
+                }
             }
-        }
 
 
 
-        viewerElement.parent.addEventListener('pointerout', triggerNavbar.bind(this, false));
-        viewerElement.parent.addEventListener('pointerdown', triggerNavbar.bind(this, true));
-        viewerElement.parent.addEventListener('pointerup', triggerNavbar.bind(this, false));
-        navbar.parent.addEventListener('pointerover', triggerNavbar.bind(this, true))
-        // triggerNavbar(false);
+            viewerElement.parent.addEventListener('pointerout', triggerNavbar.bind(this, false));
+            viewerElement.parent.addEventListener('pointerdown', triggerNavbar.bind(this, true));
+            viewerElement.parent.addEventListener('pointerup', triggerNavbar.bind(this, false));
+            navbar.parent.addEventListener('pointerover', triggerNavbar.bind(this, true))
+            // triggerNavbar(false);
+
+            // events registration
+            this.registerNavbarButtons();
+        }
 
         // close overlay button
         let closeButton = document.getElementById('close-button');
@@ -70,9 +77,6 @@ export class DefaultViewer extends AbstractViewer {
             })
         }
 
-        // events registration
-        this.registerNavbarButtons();
-
         return super.onTemplatesLoaded();
     }
 
@@ -80,7 +84,11 @@ export class DefaultViewer extends AbstractViewer {
         let isFullscreen = false;
 
         let navbar = this.templateManager.getTemplate('navBar');
-        let viewerElement = this.templateManager.getTemplate('viewer').parent;
+        let viewerTemplate = this.templateManager.getTemplate('viewer');
+        if (!navbar || !viewerTemplate) return;
+
+        let viewerElement = viewerTemplate.parent;
+
 
         navbar.onEventTriggered.add((data) => {
             switch (data.event.type) {
@@ -90,7 +98,7 @@ export class DefaultViewer extends AbstractViewer {
                         switch (data.selector) {
                             case '#fullscreen-button':
                                 if (!isFullscreen) {
-                                    let requestFullScreen = viewerElement.requestFullscreen || viewerElement.webkitRequestFullscreen; // || viewerElement.parent.msRequestFullscreen || viewerElement.parent.mozRequestFullScreen 
+                                    let requestFullScreen = viewerElement.requestFullscreen || viewerElement.webkitRequestFullscreen || (<any>viewerElement).msRequestFullscreen || (<any>viewerElement).mozRequestFullScreen;
                                     requestFullScreen.call(viewerElement);
                                 } else {
                                     let exitFullscreen = document.exitFullscreen || document.webkitExitFullscreen
@@ -107,7 +115,6 @@ export class DefaultViewer extends AbstractViewer {
                     break;
             }
         });
-
     }
 
     protected prepareContainerElement() {
@@ -117,7 +124,9 @@ export class DefaultViewer extends AbstractViewer {
 
     public loadModel(model: any = this.configuration.model): Promise<Scene> {
         this.showLoadingScreen();
-        return super.loadModel(model, true).catch(() => {
+        return super.loadModel(model, true).catch((error) => {
+            console.log(error);
+            this.hideLoadingScreen();
             this.showOverlayScreen('error');
             return this.scene;
         });
@@ -148,6 +157,7 @@ export class DefaultViewer extends AbstractViewer {
 
     private setModelMetaData() {
         let navbar = this.templateManager.getTemplate('navBar');
+        if (!navbar) return;
 
         let metadataContainer = navbar.parent.querySelector('#model-metadata');
 
@@ -225,7 +235,10 @@ export class DefaultViewer extends AbstractViewer {
     }
 
     public showOverlayScreen(subScreen: string) {
-        return this.templateManager.getTemplate('overlay').show((template => {
+        let template = this.templateManager.getTemplate('overlay');
+        if (!template) return Promise.reject('Overlay template not found');
+
+        return template.show((template => {
 
             var canvasRect = this.containerElement.getBoundingClientRect();
             var canvasPositioning = window.getComputedStyle(this.containerElement).position;
@@ -235,7 +248,11 @@ export class DefaultViewer extends AbstractViewer {
             template.parent.style.height = canvasRect.height + "px";
             template.parent.style.opacity = "1";
 
-            return this.templateManager.getTemplate(subScreen).show((template => {
+            let subTemplate = this.templateManager.getTemplate(subScreen);
+            if (!subTemplate) {
+                return Promise.reject(subScreen + ' template not found');
+            }
+            return subTemplate.show((template => {
                 template.parent.style.display = 'flex';
                 return Promise.resolve(template);
             }));
@@ -243,7 +260,10 @@ export class DefaultViewer extends AbstractViewer {
     }
 
     public hideOverlayScreen() {
-        return this.templateManager.getTemplate('overlay').hide((template => {
+        let template = this.templateManager.getTemplate('overlay');
+        if (!template) return Promise.reject('Overlay template not found');
+
+        return template.hide((template => {
             template.parent.style.opacity = "0";
             let onTransitionEnd = () => {
                 template.parent.removeEventListener("transitionend", onTransitionEnd);
@@ -268,7 +288,10 @@ export class DefaultViewer extends AbstractViewer {
     }
 
     public showLoadingScreen() {
-        return this.templateManager.getTemplate('loadingScreen').show((template => {
+        let template = this.templateManager.getTemplate('loadingScreen');
+        if (!template) return Promise.reject('oading Screen template not found');
+
+        return template.show((template => {
 
             var canvasRect = this.containerElement.getBoundingClientRect();
             var canvasPositioning = window.getComputedStyle(this.containerElement).position;
@@ -284,7 +307,10 @@ export class DefaultViewer extends AbstractViewer {
     }
 
     public hideLoadingScreen() {
-        return this.templateManager.getTemplate('loadingScreen').hide((template => {
+        let template = this.templateManager.getTemplate('loadingScreen');
+        if (!template) return Promise.reject('oading Screen template not found');
+
+        return template.hide((template => {
             template.parent.style.opacity = "0";
             let onTransitionEnd = () => {
                 template.parent.removeEventListener("transitionend", onTransitionEnd);
@@ -299,14 +325,15 @@ export class DefaultViewer extends AbstractViewer {
 
         let sceneConfig = this.configuration.scene || { defaultLight: true };
 
-        if (!sceneConfig.defaultLight && (this.configuration.lights && this.configuration.lights.length)) {
+        if (!sceneConfig.defaultLight && (this.configuration.lights && Object.keys(this.configuration.lights).length)) {
             // remove old lights
             this.scene.lights.forEach(l => {
                 l.dispose();
             });
 
-            this.configuration.lights.forEach((lightConfig, idx) => {
-                lightConfig.name = lightConfig.name || 'light-' + idx;
+            Object.keys(this.configuration.lights).forEach((name, idx) => {
+                let lightConfig = this.configuration.lights && this.configuration.lights[name] || { name: name, type: 0 };
+                lightConfig.name = name;
                 let constructor = Light.GetConstructorFromName(lightConfig.type, lightConfig.name, this.scene);
                 let light = constructor();
 
@@ -354,9 +381,9 @@ export class DefaultViewer extends AbstractViewer {
         this.camera.maxZ = cameraConfig.maxZ || this.camera.maxZ;
 
         if (cameraConfig.behaviors) {
-            cameraConfig.behaviors.forEach((behaviorConfig) => {
-                this.setCameraBehavior(behaviorConfig, focusMeshes);
-            });
+            for (let name in cameraConfig.behaviors) {
+                this.setCameraBehavior(cameraConfig.behaviors[name], focusMeshes);
+            }
         };
 
         if (sceneConfig.autoRotate) {
@@ -415,6 +442,7 @@ export class DefaultViewer extends AbstractViewer {
     }
 
     private extendClassWithConfig(object: any, config: any) {
+        if (!config) return;
         Object.keys(config).forEach(key => {
             if (key in object && typeof object[key] !== 'function') {
                 if (typeof object[key] === 'function') return;

+ 1 - 1
Viewer/src/viewer/viewer.ts

@@ -13,7 +13,7 @@ export abstract class AbstractViewer {
 
     protected configuration: ViewerConfiguration;
 
-    constructor(public containerElement: HTMLElement, initialConfiguration: ViewerConfiguration = { defaultViewer: true }) {
+    constructor(public containerElement: HTMLElement, initialConfiguration: ViewerConfiguration = {}) {
         // if exists, use the container id. otherwise, generate a random string.
         if (containerElement.id) {
             this.baseId = containerElement.id;

+ 1 - 1
Viewer/webpack.config.js

@@ -11,7 +11,7 @@ module.exports = {
         path: path.resolve(__dirname, 'dist'),
         filename: '[name].js',
         libraryTarget: 'umd',
-        library: 'Viewer3D',
+        library: 'BabylonViewer',
         umdNamedDefine: true,
         devtoolModuleFilenameTemplate: '[absolute-resource-path]'
     },

+ 8 - 8
tests/validation/validation.js

@@ -80,7 +80,7 @@ function evaluate(test, resultCanvas, result, renderImage, index, waitRing) {
     var renderData = getRenderData(canvas, engine);
     if (!test.onlyVisual) {
 
-        if (compare(renderData, resultCanvas)) { 
+        if (compare(renderData, resultCanvas)) {
             result.classList.add("failed");
             result.innerHTML = "×";
             console.log("failed");
@@ -104,7 +104,7 @@ function processCurrentScene(test, resultCanvas, result, renderImage, index, wai
     currentScene.executeWhenReady(function () {
         var renderCount = test.renderCount || 1;
 
-        engine.runRenderLoop(function() {
+        engine.runRenderLoop(function () {
             currentScene.render();
             renderCount--;
 
@@ -117,7 +117,7 @@ function processCurrentScene(test, resultCanvas, result, renderImage, index, wai
     });
 }
 
-function 
+function
 
 
 runTest(index) {
@@ -179,7 +179,7 @@ runTest(index) {
         });
     }
     else if (test.playgroundId) {
-        var snippetUrl = "https://babylonjs-api2.azurewebsites.net/snippets";
+        var snippetUrl = "//babylonjs-api2.azurewebsites.net/snippets";
         var pgRoot = "/playground"
         var xmlHttp = new XMLHttpRequest();
         xmlHttp.onreadystatechange = function () {
@@ -211,7 +211,7 @@ runTest(index) {
 
         request.onreadystatechange = () => {
             if (request.readyState === 4) {
-                request.onreadystatechange = null; 
+                request.onreadystatechange = null;
 
                 var scriptToRun = request.responseText.replace(/..\/..\/assets\//g, config.root + "/Assets/");
                 scriptToRun = scriptToRun.replace(/..\/..\/Assets\//g, config.root + "/Assets/");
@@ -220,7 +220,7 @@ runTest(index) {
 
                 if (test.replace) {
                     var split = test.replace.split(",");
-                    for (var i = 0; i < split.length; i+= 2) {
+                    for (var i = 0; i < split.length; i += 2) {
                         var source = split[i].trim();
                         var destination = split[i + 1].trim();
                         scriptToRun = scriptToRun.replace(source, destination);
@@ -242,7 +242,7 @@ runTest(index) {
         };
 
         request.send(null);
-        
+
     }
 }
 
@@ -261,7 +261,7 @@ var xhr = new XMLHttpRequest();
 
 xhr.open("GET", "config.json", true);
 
-xhr.addEventListener("load",function() {
+xhr.addEventListener("load", function () {
     if (xhr.status === 200) {
 
         config = JSON.parse(xhr.responseText);