text: /** @namespace x3dom.nodeTypes */
  /*
   * X3DOM JavaScript Library
   * http://www.x3dom.org
   *
   * (C)2009 Fraunhofer IGD, Darmstadt, Germany
   * Dual licensed under the MIT and GPL
   */
  
  // ### Inline ###
  x3dom.registerNodeType(
      "Inline",
      "Networking",
      defineClass( x3dom.nodeTypes.X3DGroupingNode,
  
          /**
           * Constructor for Inline
           * @constructs x3dom.nodeTypes.Inline
           * @x3d 3.3
           * @component Networking
           * @status full
           * @extends x3dom.nodeTypes.X3DGroupingNode
           * @param {Object} [ctx=null] - context object, containing initial settings like namespace
           * @classdesc Inline is a Grouping node that can load nodes from another X3D scene via url.
           */
          function ( ctx )
          {
              x3dom.nodeTypes.Inline.superClass.call( this, ctx );
  
              /**
               * Each specified URL shall refer to a valid X3D file that contains a list of children nodes, prototypes and routes at the top level. Hint: Strings can have multiple values, so separate each string by quote marks. Warning: strictly match directory and filename capitalization for http links!
               * @var {x3dom.fields.MFString} url
               * @memberof x3dom.nodeTypes.Inline
               * @initvalue []
               * @field x3d
               * @instance
               */
              this.addField_MFString( ctx, "url", [] );
  
              /**
               * Specifies whether the X3D file specified by the url field is loaded.
               * TRUE: load immediately (it's also possible to load the URL at a later time by sending a TRUE event to the load field);
               * FALSE: no action is taken (by sending a FALSE event to the load field of a previously loaded Inline,
               * the contents of the Inline will be unloaded from the scene graph)
               * @var {x3dom.fields.SFBool} load
               * @memberof x3dom.nodeTypes.Inline
               * @initvalue true
               * @field x3d
               * @instance
               */
              this.addField_SFBool( ctx, "load", true );
  
              /**
               * The description field specifies a textual description.
               * This may be used by browser-specific user interfaces that wish to present users with more detailed information.
               * @var {x3dom.fields.SFString} description
               * @memberof x3dom.nodeTypes.Anchor
               * @initvalue []
               * @field x3d
               * @instance
               */
              this.addField_SFString( ctx, "description", "" );
  
              /**
               * Specifies the namespace of the Inline node.
               * @var {x3dom.fields.MFString} nameSpaceName
               * @memberof x3dom.nodeTypes.Inline
               * @initvalue []
               * @field x3dom
               * @instance
               */
              this.addField_MFString( ctx, "nameSpaceName", [] );
  
              /**
               * Specifies the content type of the resource.
               * @var {x3dom.fields.SFString} contentType
               * @memberof x3dom.nodeTypes.Inline
               * @initvalue ""
               * @range ["", "model/x3d+xml", "model/x3d+json", "model/gltf+json", "model/gltf-binary"]
               * @field x3dom
               * @instance
               */
              this.addField_SFString( ctx, "contentType", "" );
  
              /**
               * Specifies whether the DEF value is used as id when no other id is set.
               * @var {x3dom.fields.SFBool} mapDEFToID
               * @memberof x3dom.nodeTypes.Inline
               * @initvalue false
               * @field x3dom
               * @instance
               */
              this.addField_SFBool( ctx, "mapDEFToID", false );
  
              this.initDone = false;
              this.count = 0;
              this.numRetries = x3dom.nodeTypes.Inline.MaximumRetries;
  
              this.ContentType = {
                  X3D  : "model/x3d+xml",
                  GLTF : "model/gltf+json",
                  GLB  : "model/gltf-binary",
                  X3DJ : "model/x3d+json"
              };
          },
          {
              fieldChanged : function ( fieldName )
              {
                  if ( fieldName == "url" || fieldName == "load" )
                  {
                      //Remove the childs of the x3domNode
                      for ( var i = 0; i < this._childNodes.length; i++ )
                      {
                          this.removeChild( this._childNodes[ i ] );
                      }
  
                      //if reflected to DOM remove the childs of the domNode
                      if ( this._vf.nameSpaceName.length != 0 )
                      {
                          var node = this._xmlNode;
                          if ( node && node.hasChildNodes() )
                          {
                              while ( node.childNodes.length >= 1 )
                              {
                                  node.removeChild( node.firstChild );
                              }
                          }
                      }
                      this.loadInline();
                  }
                  else if ( fieldName == "render" )
                  {
                      this.invalidateVolume();
                      //this.invalidateCache();
                  }
              },
  
              nodeChanged : function ()
              {
                  if ( !this.initDone )
                  {
                      this.initDone = true;
                      this.loadInline();
                  }
              },
  
              parentRemoved : function ()
              {
                  var global = x3dom.getGlobal();
  
                  if ( this._childNodes.length > 0 && this._childNodes[ 0 ] && this._childNodes[ 0 ]._nameSpace )
                  {this._nameSpace.removeSpace( this._childNodes[ 0 ]._nameSpace );}
  
                  for ( var i = 0, n = this._childNodes.length; i < n; i++ )
                  {
                      if ( this._childNodes[ i ] )
                      {
                          this._childNodes[ i ].parentRemoved( this );
                          global[ "_remover" ] = this.removeChild( this._childNodes[ i ] );
                      }
                  }
  
                  delete global[ "_remover" ];
              },
  
              fireEvents : function ( eventType )
              {
                  if ( this._xmlNode &&
                      ( this._xmlNode[ "on" + eventType ] ||
                          this._xmlNode.hasAttribute( "on" + eventType ) ||
                          this._listeners[ eventType ] ) )
                  {
                      var event = {
                          target          : this._xmlNode,
                          type            : eventType,
                          error           : ( eventType == "error" ) ? "XMLHttpRequest Error" : "",
                          cancelBubble    : false,
                          stopPropagation : function () { this.cancelBubble = true; }
                      };
  
                      try
                      {
                          var attrib = this._xmlNode[ "on" + eventType ];
  
                          if ( typeof( attrib ) === "function" )
                          {
                              attrib.call( this._xmlNode, event );
                          }
                          else
                          {
                              var funcStr = this._xmlNode.getAttribute( "on" + eventType );
                              var func = new Function( "event", funcStr );
                              func.call( this._xmlNode, event );
                          }
  
                          var list = this._listeners[ eventType ];
                          if ( list )
                          {
                              for ( var i = 0; i < list.length; i++ )
                              {
                                  list[ i ].call( this._xmlNode, event );
                              }
                          }
                      }
                      catch ( ex )
                      {
                          x3dom.debug.logException( ex );
                      }
                  }
              },
  
              isValidContentType : function ( contentType )
              {
                  for ( var type in this.ContentType )
                  {
                      if ( contentType === this.ContentType[ type ] )
                      {
                          return true;
                      }
                  }
  
                  return false;
              },
  
              getContentType : function ()
              {
                  //Return contentType if defined & valid
                  if ( this._vf.contentType != "" && this.isValidContentType( this._vf.contentType ) )
                  {
                      return this._vf.contentType;
                  }
  
                  //Try to detect the contentType from suffix
                  if ( this._vf.url.length && this._vf.url[ 0 ].length )
                  {
                      var lastPointIndex = this._vf.url[ 0 ].lastIndexOf( "." );
  
                      if ( lastPointIndex != -1 )
                      {
                          var suffix = this._vf.url[ 0 ].substr( lastPointIndex ).toLowerCase();
  
                          switch ( suffix )
                          {
                              case ".x3d"  :  return this.ContentType.X3D;
                              case ".gltf" :  return this.ContentType.GLTF;
                              case ".glb"  :  return this.ContentType.GLB;
                              case ".json" :  return this.ContentType.X3DJ;
                          }
                      }
                  }
              },
  
              loadX3D : function ( inlScene, nameSpace )
              {
                  if ( !this._vf.load )
                  {
                      x3dom.debug.logInfo( "Inline: load field prevented loading of " + this._vf.url[ 0 ] );
                      return;
                  }
  
                  var that = this;
  
                  var newScene = null;
  
                  if ( inlScene )
                  {
                      newScene = nameSpace.setupTree( inlScene );
  
                      if ( that._vf.nameSpaceName.length != 0 )
                      {
                          while ( inlScene.children.length )
                          {
                              var childDomNode = inlScene.children[ 0 ];
                              setNamespace( that._vf.nameSpaceName, childDomNode, that._vf.mapDEFToID );
                              that._xmlNode.appendChild( childDomNode );
                          }
                      }
                  }
                  else
                  {
                      x3dom.debug.logError( "No Scene in resource" );
                  }
  
                  // trick to free memory, assigning a property to global object, then deleting it
                  var global = x3dom.getGlobal();
  
                  if ( that._childNodes.length > 0 && that._childNodes[ 0 ] && that._childNodes[ 0 ]._nameSpace )
                  {that._nameSpace.removeSpace( that._childNodes[ 0 ]._nameSpace );}
  
                  while ( that._childNodes.length !== 0 )
                  {global[ "_remover" ] = that.removeChild( that._childNodes[ 0 ] );}
  
                  delete global[ "_remover" ];
  
                  if ( newScene )
                  {
                      that.addChild( newScene );
  
                      that.invalidateVolume();
  
                      // recalc changed scene bounding box twice
                      var theScene = that._nameSpace.doc._scene;
  
                      if ( theScene )
                      {
                          theScene.invalidateVolume();
                          //theScene.invalidateCache();
  
                          window.setTimeout( function ()
                          {
                              that.invalidateVolume();
                              //that.invalidateCache();
  
                              theScene.updateVolume();
                              that._nameSpace.doc.needRender = true;
                              that._nameSpace.doc.decrementDownloads();
                              that._nameSpace.doc.needRender = true;
                              x3dom.debug.logInfo( "Inline: added " + that._vf.url[ 0 ] + " to scene." );
                          }, 1000 );
                      }
  
                      that.fireEvents( "load" );
                  }
  
                  newScene = null;
                  nameSpace = null;
                  inlScene = null;
              },
  
              loadInline : function ()
              {
                  var that = this;
  
                  var contentType = this.getContentType();
  
                  var isBinary = false;
  
                  var xhr = new window.XMLHttpRequest();
  
                  xhr.onreadystatechange = function ()
                  {
                      if ( xhr.readyState == 4 )
                      {
                          //202 Still Transcoding
                          if ( xhr.status === x3dom.nodeTypes.Inline.AwaitTranscoding )
                          {
                              if ( that.count < that.numRetries )
                              {
                                  that.count++;
  
                                  var refreshTime = +xhr.getResponseHeader( "Refresh" ) || 5;
  
                                  x3dom.debug.logInfo( "XHR status: " + xhr.status + " - Await Transcoding (" + that.count + "/" + that.numRetries + "): " +
                                                      "Next request in " + refreshTime + " seconds" );
  
                                  window.setTimeout( function ()
                                  {
                                      that._nameSpace.doc.decrementDownloads();;
                                      that.loadInline();
                                  }, refreshTime * 1000 );
                              }
                              else
                              {
                                  x3dom.debug.logError( "XHR status: " + xhr.status + " - Await Transcoding (" + that.count + "/" + that.numRetries + "): " +
                                                       "No Retries left" );
  
                                  that._nameSpace.doc.decrementDownloads();;
  
                                  that.count = 0;
                              }
                          }
                          else if ( xhr.status == 200 || xhr.status == 0 )
                          {
                              x3dom.debug.logInfo( "Inline: downloading " + that._vf.url[ 0 ] + " done." );
  
                              if ( contentType == undefined )
                              {
                                  contentType = xhr.getResponseHeader( "Content-Type" );
                              }
  
                              that.count = 0;
  
                              var inlineScene;
  
                              var namespace = that.addNameSpace();
  
                              if ( contentType == that.ContentType.GLTF || contentType == that.ContentType.GLB )
                              {
                                  if ( xhr.response )
                                  {
                                      var loader = new x3dom.glTF2Loader( namespace );
  
                                      inlineScene = loader.load( xhr.response, isBinary );
  
                                      that.loadX3D( inlineScene, namespace );
                                  }
                                  else
                                  {
                                      x3dom.debug.logError( "Invalide XHR Response" );
  
                                      that.fireEvents( "error" );
  
                                      that._nameSpace.doc.decrementDownloads();;
                                      that.count = 0;
                                  }
                              }
                              else if ( contentType == that.ContentType.X3DJ )
                              {
                                  if ( xhr.response )
                                  {
                                      var json = x3dom.protoExpander.prototypeExpander( xhr.responseURL, xhr.response );
                                      var parser = new x3dom.JSONParser();
  
                                      var xml = parser.parseJavaScript( json );
  
                                      if ( xml !== undefined && xml !== null )
                                      {
                                          inlineScene = xml.getElementsByTagName( "Scene" )[ 0 ] ||
                                                        xml.getElementsByTagName( "scene" )[ 0 ];
  
                                          that.loadX3D( inlineScene, namespace );
                                      }
                                      else
                                      {
                                          that.fireEvents( "error" );
                                          that._nameSpace.doc.decrementDownloads();
                                          that.count = 0;
                                      }
                                  }
                                  else
                                  {
                                      x3dom.debug.logError( "Invalide XHR Response" );
  
                                      that.fireEvents( "error" );
  
                                      that._nameSpace.doc.decrementDownloads();
                                      that.count = 0;
                                  }
                              }
                              else if ( contentType == that.ContentType.X3D )
                              {
                                  var xml;
  
                                  if ( navigator.appName == "Microsoft Internet Explorer" )
                                  {
                                      xml = new DOMParser().parseFromString( xhr.responseText, "text/xml" );
                                  }
                                  else
                                  {
                                      xml = xhr.responseXML;
                                  }
  
                                  if ( xml !== undefined && xml !== null )
                                  {
                                      inlineScene = xml.getElementsByTagName( "Scene" )[ 0 ] ||
                                                    xml.getElementsByTagName( "scene" )[ 0 ];
  
                                      that.loadX3D( inlineScene, namespace );
                                  }
                                  else
                                  {
                                      that.fireEvents( "error" );
                                      that._nameSpace.doc.decrementDownloads();
                                      that.count = 0;
                                  }
                              }
                          }
                          else
                          {
                              x3dom.debug.logError( "XHR status: " + xhr.status + " - XMLHttpRequest requires web server running!" );
  
                              that.fireEvents( "error" );
  
                              that._nameSpace.doc.decrementDownloads();
                              that.count = 0;
                          }
                      }
                  };
  
                  if ( this._vf.url.length && this._vf.url[ 0 ].length )
                  {
                      var xhrURI = this._nameSpace.getURL( this._vf.url[ 0 ] );
  
                      xhr.open( "GET", xhrURI, true );
                      xhr.setRequestHeader( "Accept", "model/x3d+xml; q=1.0, model/gltf+json; q=0.5, model/gltf-binary; q=0.5" );
  
                      if ( xhr.overrideMimeType )
                      {
                          if ( contentType == this.ContentType.X3D )
                          {
                              xhr.overrideMimeType( "text/xml" );
                          }
                          if ( contentType == this.ContentType.X3DJ )
                          {
                              xhr.overrideMimeType( "application/json" );
                              xhr.responseType = "json";
                          }
                          else if ( contentType == this.ContentType.GLTF )
                          {
                              isBinary = false;
                              xhr.responseType = "json";
                          }
                          else if ( contentType == this.ContentType.GLB )
                          {
                              isBinary = true;
                              xhr.responseType = "arraybuffer";
                          }
                      }
  
                      try
                      {
                          this._nameSpace.doc.incrementDownloads();
                          x3dom.RequestManager.addRequest( xhr );
                      }
                      catch ( ex )
                      {
                          this.fireEvents( "error" );
                          x3dom.debug.logError( this._vf.url[ 0 ] + ": " + ex );
                          this._nameSpace.doc.decrementDownloads();
                      }
                  }
              },
  
              addNameSpace : function ()
              {
                  var nsName = ( this._vf.nameSpaceName.length != 0 ) ?
                      this._vf.nameSpaceName.toString().replace( " ", "" ) : "";
  
                  var nameSpace = new x3dom.NodeNameSpace( nsName, this._nameSpace.doc );
  
                  var url = this._vf.url.length ? this._vf.url[ 0 ] : "";
  
                  if ( ( url[ 0 ] === "/" ) || ( url.indexOf( ":" ) >= 0 ) )
                  {
                      nameSpace.setBaseURL( url );
                  }
                  else
                  {
                      nameSpace.setBaseURL( this._nameSpace.baseURL + url );
                  }
  
                  this._nameSpace.addSpace( nameSpace );
  
                  return nameSpace;
              }
          }
      )
  );
  
  x3dom.nodeTypes.Inline.AwaitTranscoding = 202;      // Parameterizable retry state for Transcoder
  x3dom.nodeTypes.Inline.MaximumRetries = 15;         // Parameterizable maximum number of retries
  
  function setNamespace ( prefix, childDomNode, mapDEFToID )
  {
      if ( childDomNode instanceof Element && childDomNode.__setAttribute !== undefined )
      {
          if ( childDomNode.hasAttribute( "id" ) )
          {
              childDomNode.__setAttribute( "id", prefix.toString().replace( " ", "" ) + "__" + childDomNode.getAttribute( "id" ) );
          }
          else if ( childDomNode.hasAttribute( "DEF" ) && mapDEFToID )
          {
              childDomNode.__setAttribute( "id", prefix.toString().replace( " ", "" ) + "__" + childDomNode.getAttribute( "DEF" ) );
              // workaround for Safari
              if ( !childDomNode.id )
              {childDomNode.id = childDomNode.__getAttribute( "id" );}
          }
      }
  
      if ( childDomNode.hasChildNodes() )
      {
          childDomNode.childNodes.forEach( function ( children )
          {
              setNamespace( prefix, children, mapDEFToID );
          } );
      }
  }
  