File: source/room-init.js

                      /**
                       * Contains the current version of Skylink Web SDK.
                       * @attribute VERSION
                       * @type String
                       * @readOnly
                       * @for Skylink
                       * @since 0.1.0
                       */
                      Skylink.prototype.VERSION = '@@version';
                      
                      /**
                       * The list of <a href="#method_init"><code>init()</code> method</a> ready states.
                       * @attribute READY_STATE_CHANGE
                       * @param {Number} INIT      <small>Value <code>0</code></small>
                       *   The value of the state when <code>init()</code> has just started.
                       * @param {Number} LOADING   <small>Value <code>1</code></small>
                       *   The value of the state when <code>init()</code> is authenticating App Key provided
                       *   (and with credentials if provided as well) with the Auth server.
                       * @param {Number} COMPLETED <small>Value <code>2</code></small>
                       *   The value of the state when <code>init()</code> has successfully authenticated with the Auth server.
                       *   Room session token is generated for joining the <code>defaultRoom</code> provided in <code>init()</code>.
                       *   <small>Room session token has to be generated each time User switches to a different Room
                       *   in <a href="#method_joinRoom"><code>joinRoom()</code> method</a>.</small>
                       * @param {Number} ERROR     <small>Value <code>-1</code></small>
                       *   The value of the state when <code>init()</code> has failed authenticating with the Auth server.
                       *   [Rel: Skylink.READY_STATE_CHANGE_ERROR]
                       * @type JSON
                       * @readOnly
                       * @for Skylink
                       * @since 0.1.0
                       */
                      Skylink.prototype.READY_STATE_CHANGE = {
                        INIT: 0,
                        LOADING: 1,
                        COMPLETED: 2,
                        ERROR: -1
                      };
                      
                      /**
                       * The list of <a href="#method_init"><code>init()</code> method</a> ready state failure codes.
                       * @attribute READY_STATE_CHANGE_ERROR
                       * @param {Number} API_INVALID                 <small>Value <code>4001</code></small>
                       *   The value of the failure code when provided App Key in <code>init()</code> does not exists.
                       *   <small>To resolve this, check that the provided App Key exists in
                       *   <a href="https://console.temasys.io">the Temasys Console</a>.</small>
                       * @param {Number} API_DOMAIN_NOT_MATCH        <small>Value <code>4002</code></small>
                       *   The value of the failure code when <code>"domainName"</code> property in the App Key does not
                       *   match the accessing server IP address.
                       *   <small>To resolve this, contact our <a href="http://support.temasys.io">support portal</a>.</small>
                       * @param {Number} API_CORS_DOMAIN_NOT_MATCH   <small>Value <code>4003</code></small>
                       *   The value of the failure code when <code>"corsurl"</code> property in the App Key does not match accessing CORS.
                       *   <small>To resolve this, configure the App Key CORS in
                       *   <a href="https://console.temasys.io">the Temasys Console</a>.</small>
                       * @param {Number} API_CREDENTIALS_INVALID     <small>Value <code>4004</code></small>
                       *   The value of the failure code when there is no [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)
                       *   present in the HTTP headers during the request to the Auth server present nor
                       *   <code>options.credentials.credentials</code> configuration provided in the <code>init()</code>.
                       *   <small>To resolve this, ensure that CORS are present in the HTTP headers during the request to the Auth server.</small>
                       * @param {Number} API_CREDENTIALS_NOT_MATCH   <small>Value <code>4005</code></small>
                       *   The value of the failure code when the <code>options.credentials.credentials</code> configuration provided in the
                       *   <code>init()</code> does not match up with the <code>options.credentials.startDateTime</code>,
                       *   <code>options.credentials.duration</code> or that the <code>"secret"</code> used to generate
                       *   <code>options.credentials.credentials</code> does not match the App Key's <code>"secret</code> property provided.
                       *   <small>To resolve this, check that the <code>options.credentials.credentials</code> is generated correctly and
                       *   that the <code>"secret"</code> used to generate it is from the App Key provided in the <code>init()</code>.</small>
                       * @param {Number} API_INVALID_PARENT_KEY      <small>Value <code>4006</code></small>
                       *   The value of the failure code when the App Key provided does not belong to any existing App.
                       *   <small>To resolve this, check that the provided App Key exists in
                       *   <a href="https://console.temasys.io">the Developer Console</a>.</small>
                       * @param {Number} API_NO_MEETING_RECORD_FOUND <small>Value <code>4010</code></small>
                       *   The value of the failure code when provided <code>options.credentials</code>
                       *   does not match any scheduled meetings available for the "Persistent Room" enabled App Key provided.
                       *   <small>See the <a href="http://support.temasys.io/support/solutions/articles/
                       * 12000002811-using-the-persistent-room-feature-to-configure-meetings">Persistent Room article</a> to learn more.</small>
                       * @param {Number} API_OVER_SEAT_LIMIT         <small>Value <code>4020</code></small>
                       *   The value of the failure code when App Key has reached its current concurrent users limit.
                       *   <small>To resolve this, use another App Key. To create App Keys dynamically, see the
                       *   <a href="https://temasys.atlassian.net/wiki/display/TPD/SkylinkAPI+-+Application+Resources">Application REST API
                       *   docs</a> for more information.</small>
                       * @param {Number} API_RETRIEVAL_FAILED        <small>Value <code>4021</code></small>
                       *   The value of the failure code when App Key retrieval of authentication token fails.
                       *   <small>If this happens frequently, contact our <a href="http://support.temasys.io">support portal</a>.</small>
                       * @param {Number} API_WRONG_ACCESS_DOMAIN     <small>Value <code>5005</code></small>
                       *   The value of the failure code when App Key makes request to the incorrect Auth server.
                       *   <small>To resolve this, ensure that the <code>roomServer</code> is not configured. If this persists even without
                       *   <code>roomServer</code> configuration, contact our <a href="http://support.temasys.io">support portal</a>.</small>
                       * @param {Number} XML_HTTP_REQUEST_ERROR      <small>Value <code>-1</code></small>
                       *   The value of the failure code when requesting to Auth server has timed out.
                       * @param {Number} NO_SOCKET_IO                <small>Value <code>1</code></small>
                       *   The value of the failure code when dependency <a href="http://socket.io/download/">Socket.IO client</a> is not loaded.
                       *   <small>To resolve this, ensure that the Socket.IO client dependency is loaded before the Skylink SDK.
                       *   You may use the provided Socket.IO client <a href="http://socket.io/download/">CDN here</a>.</small>
                       * @param {Number} NO_XMLHTTPREQUEST_SUPPORT   <small>Value <code>2</code></small>
                       *   The value of the failure code when <a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest">
                       *   XMLHttpRequest API</a> required to make request to Auth server is not supported.
                       *   <small>To resolve this, display in the Web UI to ask clients to switch to the list of supported browser
                       *   as <a href="https://github.com/Temasys/SkylinkJS/tree/0.6.14#supported-browsers">listed in here</a>.</small>
                       * @param {Number} NO_WEBRTC_SUPPORT           <small>Value <code>3</code></small>
                       *   The value of the failure code when <a href="https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/">
                       *   RTCPeerConnection API</a> required for Peer connections is not supported.
                       *   <small>To resolve this, display in the Web UI to ask clients to switch to the list of supported browser
                       *   as <a href="https://github.com/Temasys/SkylinkJS/tree/0.6.14#supported-browsers">listed in here</a>.
                       *   For <a href="http://confluence.temasys.com.sg/display/TWPP">plugin supported browsers</a>, if the clients
                       *   does not have the plugin installed, there will be an installation toolbar that will prompt for installation
                       *   to support the RTCPeerConnection API.</small>
                       * @param {Number} NO_PATH                     <small>Value <code>4</code></small>
                       *   The value of the failure code when provided <code>init()</code> configuration has errors.
                       * @param {Number} ADAPTER_NO_LOADED           <small>Value <code>7</code></small>
                       *   The value of the failure code when dependency <a href="https://github.com/Temasys/AdapterJS/">AdapterJS</a>
                       *   is not loaded.
                       *   <small>To resolve this, ensure that the AdapterJS dependency is loaded before the Skylink dependency.
                       *   You may use the provided AdapterJS <a href="https://github.com/Temasys/AdapterJS/">CDN here</a>.</small>
                       * @param {Number} PARSE_CODECS                <small>Value <code>8</code></small>
                       *   The value of the failure code when codecs support cannot be parsed and retrieved.
                       * @type JSON
                       * @readOnly
                       * @for Skylink
                       * @since 0.4.0
                       */
                      Skylink.prototype.READY_STATE_CHANGE_ERROR = {
                        API_INVALID: 4001,
                        API_DOMAIN_NOT_MATCH: 4002,
                        API_CORS_DOMAIN_NOT_MATCH: 4003,
                        API_CREDENTIALS_INVALID: 4004,
                        API_CREDENTIALS_NOT_MATCH: 4005,
                        API_INVALID_PARENT_KEY: 4006,
                        API_NO_MEETING_RECORD_FOUND: 4010,
                        API_OVER_SEAT_LIMIT: 4020,
                        API_RETRIEVAL_FAILED: 4021,
                        API_WRONG_ACCESS_DOMAIN: 5005,
                        XML_HTTP_REQUEST_ERROR: -1,
                        NO_SOCKET_IO: 1,
                        NO_XMLHTTPREQUEST_SUPPORT: 2,
                        NO_WEBRTC_SUPPORT: 3,
                        NO_PATH: 4,
                        ADAPTER_NO_LOADED: 7,
                        PARSE_CODECS: 8
                      };
                      
                      /**
                       * Spoofs the REGIONAL_SERVER to prevent errors on deployed apps except the fact this no longer works.
                       * Automatic regional selection has already been implemented hence REGIONAL_SERVER is no longer useful.
                       * @attribute REGIONAL_SERVER
                       * @type JSON
                       * @readOnly
                       * @private
                       * @for Skylink
                       * @since 0.6.16
                       */
                      Skylink.prototype.REGIONAL_SERVER = {
                        APAC1: '',
                        US1: ''
                      };
                      
                      /**
                       * The list of User's priority weight schemes for <a href="#method_joinRoom">
                       * <code>joinRoom()</code> method</a> connections.
                       * @attribute PRIORITY_WEIGHT_SCHEME
                       * @param {String} ENFORCE_OFFERER  <small>Value <code>"enforceOfferer"</code></small>
                       *   The value of the priority weight scheme to enforce User as the offerer.
                       * @param {String} ENFORCE_ANSWERER <small>Value <code>"enforceAnswerer"</code></small>
                       *   The value of the priority weight scheme to enforce User as the answerer.
                       * @param {String} AUTO             <small>Value <code>"auto"</code></small>
                       *   The value of the priority weight scheme to let User be offerer or answerer based on Signaling server selection.
                       * @type JSON
                       * @readOnly
                       * @for Skylink
                       * @since 0.6.18
                       */
                      Skylink.prototype.PRIORITY_WEIGHT_SCHEME = {
                        ENFORCE_OFFERER: 'enforceOfferer',
                        ENFORCE_ANSWERER: 'enforceAnswerer',
                        AUTO: 'auto'
                      };
                      
                      /**
                       * Function that generates an <a href="https://en.wikipedia.org/wiki/Universally_unique_identifier">UUID</a> (Unique ID).
                       * @method generateUUID
                       * @return {String} Returns a generated UUID (Unique ID).
                       * @for Skylink
                       * @since 0.5.9
                       */
                      /* jshint ignore:start */
                      Skylink.prototype.generateUUID = function() {
                        var d = new Date().getTime();
                        var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                          var r = (d + Math.random() * 16) % 16 | 0;
                          d = Math.floor(d / 16);
                          return (c === 'x' ? r : (r && 0x7 | 0x8)).toString(16);
                        });
                        return uuid;
                      };
                      /* jshint ignore:end */
                      
                      /**
                       * Function that authenticates and initialises App Key used for Room connections.
                       * @method init
                       * @param {JSON|String} options The configuration options.
                       * - When provided as a string, it's configured as <code>options.appKey</code>.
                       * @param {String} options.appKey The App Key.
                       *   <small>By default, <code>init()</code> uses [HTTP CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)
                       *   authentication. For credentials based authentication, see the <code>options.credentials</code> configuration
                       *   below. You can know more about the <a href="http://support.temasys.io/support/solutions/articles/
                       * 12000002712-authenticating-your-application-key-to-start-a-connection">in the authentication methods article here</a>
                       *   for more details on the various authentication methods.</small>
                       *   <small>If you are using the Persistent Room feature for scheduled meetings, you will require to
                       *   use the credential based authentication. See the <a href="http://support.temasys.io/support
                       * /solutions/articles/12000002811-using-the-persistent-room-feature-to-configure-meetings">Persistent Room article here
                       *   </a> for more information.</small>
                       * @param {String} [options.defaultRoom] The default Room to connect to when no <code>room</code> parameter
                       *    is provided in  <a href="#method_joinRoom"><code>joinRoom()</code> method</a>.
                       * - When not provided or is provided as an empty string, its value is <code>options.appKey</code>.
                       *   <small>Note that switching Rooms is not available when using <code>options.credentials</code> based authentication.
                       *   The Room that User will be connected to is the <code>defaultRoom</code> provided.</small>
                       * @param {String} [options.roomServer] The Auth server for debugging purposes to use.
                       *   <small>Note that this is a debugging feature and is only used when instructed for debugging purposes.</small>
                       * @param {Boolean} [options.enableIceTrickle=true] The flag if Peer connections should
                       *   trickle ICE for faster connectivity.
                       * @param {Boolean} [options.enableDataChannel=true] <blockquote class="info">
                       *   Note that for Edge browsers, this value is overriden as <code>false</code> due to its supports.
                       *   </blockquote> The flag if Datachannel connections should be enabled.
                       *   <small>This is required to be enabled for <a href="#method_sendBlobData"><code>sendBlobData()</code> method</a>,
                       *   <a href="#method_sendURLData"><code>sendURLData()</code> method</a> and
                       *   <a href="#method_sendP2PMessage"><code>sendP2PMessage()</code> method</a>.</small>
                       * @param {Boolean} [options.enableTURNServer=true] The flag if TURN ICE servers should
                       *   be used when constructing Peer connections to allow TURN connections when required and enabled for the App Key.
                       * @param {Boolean} [options.enableSTUNServer=true] The flag if STUN ICE servers should
                       *   be used when constructing Peer connections to allow TURN connections when required.
                       * @param {Boolean} [options.forceTURN=false] The flag if Peer connections should enforce
                       *   connections over the TURN server.
                       *   <small>This overrides <code>options.enableTURNServer</code> value to <code>true</code> and
                       *   <code>options.enableSTUNServer</code> value to <code>false</code>, <code>options.filterCandidatesType.host</code>
                       *   value to <code>true</code>, <code>options.filterCandidatesType.srflx</code> value to <code>true</code> and
                       *   <code>options.filterCandidatesType.relay</code> value to <code>false</code>.</small>
                       *   <small>Note that currently for MCU enabled Peer connections, the <code>options.filterCandidatesType</code>
                       *   configuration is not honoured as Peers connected with MCU is similar as a forced TURN connection. The flags
                       *   will act as if the value is <code>false</code> and ICE candidates will never be filtered regardless of the
                       *   <code>options.filterCandidatesType</code> configuration.</small>
                       * @param {Boolean} [options.usePublicSTUN=true] The flag if publicly available STUN ICE servers should
                       *   be used if <code>options.enableSTUNServer</code> is enabled.
                       * @param {Boolean} [options.TURNServerTransport] <blockquote class="info">
                       *   Note that configuring the protocol may not necessarily result in the desired network transports protocol
                       *   used in the actual TURN network traffic as it depends which protocol the browser selects and connects with.
                       *   This simply configures the TURN ICE server urls <code?transport=(protocol)</code> query option when constructing
                       *   the Peer connection. When all protocols are selected, the ICE servers urls are duplicated with all protocols.<br>
                       *   Note that for Edge browsers, this value is overriden as <code>UDP</code> due to its supports.
                       *   </blockquote> The option to configure the <code>?transport=</code>
                       *   query parameter in TURN ICE servers when constructing a Peer connections.
                       * - When not provided, its value is <code>ANY</code>.
                       *   [Rel: Skylink.TURN_TRANSPORT]
                       * @param {Boolean} [options.disableVideoFecCodecs=false] <blockquote class="info">
                       *   Note that this is an experimental flag and may cause disruptions in connections or connectivity issues when toggled,
                       *   and to prevent connectivity issues, these codecs will not be removed for MCU enabled Peer connections.
                       *   </blockquote> The flag if video FEC (Forward Error Correction)
                       *   codecs like ulpfec and red should be removed in sending session descriptions.
                       *   <small>This can be useful for debugging purposes to prevent redundancy and overheads in RTP encoding.</small>
                       * @param {Boolean} [options.disableComfortNoiseCodec=false] <blockquote class="info">
                       *   Note that this is an experimental flag and may cause disruptions in connections or connectivity issues when toggled.
                       *   </blockquote> The flag if audio
                       *   <a href="https://en.wikipedia.org/wiki/Comfort_noise">Comfort Noise (CN)</a> codec should be removed
                       *   in sending session descriptions.
                       *   <small>This can be useful for debugging purposes to test preferred audio quality and feedback.</small>
                       * @param {Boolean} [options.disableREMB=false] <blockquote class="info">
                       *   Note that this is mainly used for debugging purposes and that it is an experimental flag, so
                       *   it may cause disruptions in connections or connectivity issues when toggled. </blockquote>
                       *   The flag if video REMB feedback packets should be disabled in sending session descriptions.
                       * @param {JSON} [options.credentials] The credentials used for authenticating App Key with
                       *   credentials to retrieve the Room session token used for connection in <a href="#method_joinRoom">
                       *   <code>joinRoom()</code> method</a>.
                       *   <small>Note that switching of Rooms is not allowed when using credentials based authentication, unless
                       *   <code>init()</code> is invoked again with a different set of credentials followed by invoking
                       *   the <a href="#method_joinRoom"><code>joinRoom()</code> method</a>.</small>
                       * @param {String} options.credentials.startDateTime The credentials User session in Room starting DateTime
                       *   in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601 format</a>.
                       * @param {Number} options.credentials.duration The credentials User session in Room duration in hours.
                       * @param {String} options.credentials.credentials The generated credentials used to authenticate
                       *   the provided App Key with its <code>"secret"</code> property.
                       *   <blockquote class="details"><h5>To generate the credentials:</h5><ol>
                       *   <li>Concatenate a string that consists of the Room name you provide in the <code>options.defaultRoom</code>,
                       *   the <code>options.credentials.duration</code> and the <code>options.credentials.startDateTime</code>.
                       *   <small>Example: <code>var concatStr = defaultRoom + "_" + duration + "_" + startDateTime;</code></small></li>
                       *   <li>Hash the concatenated string with the App Key <code>"secret"</code> property using
                       *   <a href="https://en.wikipedia.org/wiki/SHA-1">SHA-1</a>.
                       *   <small>Example: <code>var hash = CryptoJS.HmacSHA1(concatStr, appKeySecret);</code></small>
                       *   <small>See the <a href="https://code.google.com/p/crypto-js/#HMAC"><code>CryptoJS.HmacSHA1</code> library</a>.</small></li>
                       *   <li>Encode the hashed string using <a href="https://en.wikipedia.org/wiki/Base64">base64</a>
                       *   <small>Example: <code>var b64Str = hash.toString(CryptoJS.enc.Base64);</code></small>
                       *   <small>See the <a href="https://code.google.com/p/crypto-js/#The_Cipher_Output">CryptoJS.enc.Base64</a> library</a>.</small></li>
                       *   <li>Encode the base64 encoded string to replace special characters using UTF-8 encoding.
                       *   <small>Example: <code>var credentials = encodeURIComponent(base64String);</code></small>
                       *   <small>See <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
                       * Global_Objects/encodeURIComponent">encodeURIComponent() API</a>.</small></li></ol></blockquote>
                       * @param {Boolean} [options.audioFallback=false] The flag if <a href="#method_getUserMedia">
                       *   <code>getUserMedia()</code> method</a> should fallback to retrieve only audio Stream when
                       *   retrieving audio and video Stream fails.
                       * @param {Boolean} [options.forceSSL=false] The flag if HTTPS connections should be enforced
                       *   during request to Auth server and socket connections to Signaling server
                       *   when accessing <code>window.location.protocol</code> value is <code>"http:"</code>.
                       *   <small>By default, <code>"https:"</code> protocol connections uses HTTPS connections.</small>
                       * @param {String|JSON} [options.audioCodec] <blockquote class="info">
                       *   Note that if the audio codec is not supported, the SDK will not configure the local <code>"offer"</code> or
                       *   <code>"answer"</code> session description to prefer the codec.<br>
                       *   Note that for Edge browsers, this value is set as <code>OPUS</code> due to its supports.</blockquote>
                       *   The option to configure the preferred audio codec to use to encode sending audio data when available for Peer connection.
                       * - When not provided, its value is <code>AUTO</code>.
                       *   [Rel: Skylink.AUDIO_CODEC]
                       * @param {String} options.audioCodec.codec The audio codec to prefer to encode sending audio data when available.
                       *   <small>The value must not be <code>AUTO</code>.</small>
                       *   [Rel: Skylink.AUDIO_CODEC]
                       * @param {Number} [options.audioCodec.samplingRate] The audio codec sampling to prefer to encode sending audio data when available.
                       * @param {Number} [options.audioCodec.channels] The audio codec channels to prefer to encode sending audio data when available.
                       * @param {String|JSON} [options.videoCodec] <blockquote class="info">
                       *   Note that if the video codec is not supported, the SDK will not configure the local <code>"offer"</code> or
                       *   <code>"answer"</code> session description to prefer the codec.<br>
                       *   Note that for Edge browsers, this value is set as <code>H264</code> due to its supports.</blockquote>
                       *   The option to configure the preferred video codec to use to encode sending video data when available for Peer connection.
                       * - When not provided, its value is <code>AUTO</code>.
                       *   [Rel: Skylink.VIDEO_CODEC]
                       * @param {String} options.videoCodec.codec The video codec to prefer to encode sending audio data when available.
                       *   <small>The value must not be <code>AUTO</code>.</small>
                       *   [Rel: Skylink.VIDEO_CODEC]
                       * @param {Number} [options.videoCodec.samplingRate] The video codec sampling to prefer to encode sending video data when available.
                       * @param {Number} [options.socketTimeout=20000] The timeout for each attempts for socket connection
                       *   with the Signaling server to indicate that connection has timed out and has failed to establish.
                       *   <small>Note that the mininum timeout value is <code>5000</code>. If less, this value will be <code>5000</code>.</small>
                       *   <small>Note that it is recommended to use <code>12000</code> as the lowest timeout value if Peers are connecting
                       *   using Polling transports to prevent connection errors.</small>
                       * @param {Boolean} [options.forceTURNSSL=false] <blockquote class="info">
                       *   Note that currently Firefox does not support the TURNS protocol, and that if TURNS is required,
                       *   TURN ICE servers using port <code>443</code> will be used instead.<br>
                       *   Note that for Edge browsers, this value is overriden as <code>false</code> due to its supports and
                       *   only port <code>3478</code> is used.</blockquote>
                       *   The flag if TURNS protocol should be used when <code>options.enableTURNServer</code> is enabled.
                       * @param {JSON} [options.filterCandidatesType] <blockquote class="info">
                       *   Note that this a debugging feature and there might be connectivity issues when toggling these flags.
                       *   </blockquote> The configuration options to filter the type of ICE candidates sent and received.
                       * @param {Boolean} [options.filterCandidatesType.host=false] The flag if local network ICE candidates should be filtered out.
                       * @param {Boolean} [options.filterCandidatesType.srflx=false] The flag if STUN ICE candidates should be filtered out.
                       * @param {Boolean} [options.filterCandidatesType.relay=false] The flag if TURN ICE candidates should be filtered out.
                       * @param {JSON} [options.throttleIntervals] The configuration options to configure the throttling method timeouts.
                       * @param {Number} [options.throttleIntervals.shareScreen=10000] The interval timeout for
                       *   <a href="#method_shareScreen"><code>shareScreen()</code> method</a> throttling in milliseconds.
                       * @param {Number} [options.throttleIntervals.getUserMedia=0] The interval timeout for
                       *   <a href="#method_getUserMedia"><code>getUserMedia()</code> method</a> throttling in milliseconds.
                       * @param {Number} [options.throttleIntervals.refreshConnection=5000] <blockquote class="info">
                       *   Note that this throttling is only done for MCU enabled Peer connections with the
                       *   <code>options.mcuUseRenegoRestart</code> being set to <code>false</code>.
                       *   </blockquote> The interval timeout for <a href="#method_refreshConnection">
                       *   <code>refreshConnection()</code> method</a> throttling in milliseconds.
                       *   <small>Note that there will be no throttling when <a href="#method_refreshConnection">
                       *   <code>refreshConnection()</code> method</a> is called internally.</small>
                       * @param {Boolean} [options.throttleShouldThrowError=false] The flag if throttled methods should throw errors when
                       *   method is invoked less than the interval timeout value configured in <code>options.throttleIntervals</code>.
                       * @param {Boolean} [options.mcuUseRenegoRestart=false] <blockquote class="info">
                       *   Note that this feature is currently is beta and for any enquiries on enabling and its support, please
                       *   contact <a href="http://support.temasys.io">our support portal</a>.</blockquote>
                       *   The flag if <a href="#method_refreshConnection"><code>
                       *   refreshConnection()</code> method</a> should renegotiate like non-MCU enabled Peer connection for MCU
                       *   enabled Peer connections instead of invoking <a href="#method_joinRoom"><code>joinRoom()</code> method</a> again.
                       * @param {String|Array} [options.iceServer] The ICE servers for debugging purposes to use.
                       *   - When defined as string, the value is considered as <code>[options.iceServer]</code>.
                       *   <small>Note that this is a debugging feature and is only used when instructed for debugging purposes.</small>
                       * @param {String} [options.iceServer.#index] The ICE server url for debugging purposes to use.
                       * @param {String|JSON} [options.socketServer] The Signaling server for debugging purposes to use.
                       *   - When defined as string, the value is considered as <code>{ url: options.socketServer }</code>.
                       *   <small>Note that this is a debugging feature and is only used when instructed for debugging purposes.</small>
                       * @param {String} options.socketServer.url The Signaling server URL for debugging purposes to use.
                       * @param {Array} [options.socketServer.ports] The list of Signaling server ports for debugging purposes to use.
                       *   <small>If not defined, it will use the default list of ports specified.</small>
                       * @param {Number} options.socketServer.ports.#index The Signaling server port to fallback and use for debugging purposes.
                       * @param {String} [options.socketServer.protocol] The Signaling server protocol for debugging purposes to use.
                       *   <small>If not defined, it will use the default protocol specified.</small>
                       * @param {JSON} [options.codecParams] <blockquote class="info">
                       *   Note that some of these parameters are mainly used for experimental or debugging purposes. Toggling any of
                       *   these feature may result in disruptions in connectivity.</blockquote>
                       *   The audio and video codecs parameters to configure.
                       * @param {JSON} [options.codecParams.video] The video codecs parameters to configure.
                       * @param {JSON} [options.codecParams.video.h264] The H264 video codec parameters to configure.
                       * @param {String} [options.codecParams.video.h264.profileLevelId] <blockquote class="info">
                       *   Note that this parameter should only be used for debugging purposes only.</blockquote>
                       *   The H264 video codec base16 encoded string which indicates the H264 baseline, main, or the extended profiles.
                       *   <small>When not provided, the default browser configuration is used.</small>
                       * @param {Boolean} [options.codecParams.video.h264.levelAsymmetryAllowed] <blockquote class="info">
                       *   Note that this is an experimental parameter which may result in connectivity issues when toggled.</blockquote>
                       *   The flag if streaming H264 sending video data should be encoded at a different level
                       *   from receiving video data from Peer encoding to User when Peer is the offerer.
                       *   <small>If Peer is the offerer instead of the User, the Peer's <code>peerInfo.config.priorityWeight</code> will be
                       *   higher than User's <code>peerInfo.config.priorityWeight</code>.</small>
                       *   <small>When not provided, the default browser configuration is used.</small>
                       * @param {Boolean} [options.codecParams.video.h264.packetizationMode] <blockquote class="info">
                       *   Note that this is an experimental parameter which may result in connectivity issues when enabled. It is
                       *   advisable to turn off this feature off when receiving H264 decoders do not support the packetization mode,
                       *   which may result in a blank receiving video stream.</blockquote>
                       *   The flag to enable H264 video codec packetization mode, which splits video frames that are larger
                       *   for a RTP packet into RTP packet chunks.
                       *   <small>When not provided, the default browser configuration is used.</small>
                       * @param {JSON} [options.codecParams.video.vp8] The VP8 video codec parameters to configure.
                       * @param {Number} [options.codecParams.video.vp8.maxFr] <blockquote class="info">
                       *   Note that this parameter should only be used for debugging purposes only. Do not toggle this otherwise.</blockquote>
                       *   The maximum number of fps (frames per second) that the VP8 video codec decoder is capable of
                       *   decoding when receiving encoded video data packets.
                       *   <small>When not provided, the default browser configuration is used.</small>
                       * @param {Number} [options.codecParams.video.vp8.maxFs] <blockquote class="info">
                       *   Note that this parameter should only be used for debugging purposes only. Do not toggle this otherwise.</blockquote>
                       *   The maximum number of frame size macroblocks that the VP8 video codec decoder is capable of
                       *   decoding when receiving encoded video data packets.
                       *   <small>The value has to have the width and height of the frame in macroblocks less than the value of
                       *   <code>parseInt(Math.sqrt(maxFs * 8))</code>. E.g. If the value is <code>1200</code>, it is capable of
                       *   support <code>640x480</code> frame width and height, which heights up to <code>1552px</code>
                       *   (<code>97</code> macroblocks value.</small>
                       *   <small>When not provided, the default browser configuration is used.</small>
                       * @param {JSON} [options.codecParams.video.vp9] The VP9 video codec parameters to configure.
                       * @param {Number} [options.codecParams.video.vp9.maxFr] <blockquote class="info">
                       *   Note that this parameter should only be used for debugging purposes only. Do not toggle this otherwise.</blockquote>
                       *   The maximum number of fps (frames per second) that the VP9 video codec decoder is capable of
                       *   decoding when receiving encoded video data packets.
                       *   <small>When not provided, the default browser configuration is used.</small>
                       * @param {Number} [options.codecParams.video.vp9.maxFs] <blockquote class="info">
                       *   Note that this parameter should only be used for debugging purposes only. Do not toggle this otherwise.</blockquote>
                       *   The maximum number of frame size macroblocks that the VP9 video codec decoder is capable of
                       *   decoding when receiving encoded video data packets.
                       *   <small>The value has to have the width and height of the frame in macroblocks less than the value of
                       *   <code>parseInt(Math.sqrt(maxFs * 8))</code>. E.g. If the value is <code>1200</code>, it is capable of
                       *   support <code>640x480</code> frame width and height, which heights up to <code>1552px</code>
                       *   (<code>97</code> macroblocks value.</small>
                       *   <small>When not provided, the default browser configuration is used.</small>
                       * @param {JSON} [options.codecParams.audio] The audio codecs parameters to configure.
                       * @param {JSON} [options.codecParams.audio.opus] <blockquote class="info">
                       *   Note that this is only applicable to OPUS audio codecs with a sampling rate of <code>48000</code> Hz (hertz).
                       *   </blockquote> The OPUS audio codec parameters to configure.
                       * @param {Boolean} [options.codecParams.audio.opus.stereo] The flag if OPUS audio codec stereo band
                       *   should be configured for sending encoded audio data.
                       *   <small>When not provided, the default browser configuration is used.</small>
                       * @param {Boolean} [options.codecParams.audio.opus.usedtx] <blockquote class="info">
                       *   Note that this feature might not work depending on the browser support and implementation.</blockquote>
                       *   The flag if OPUS audio codec should enable DTX (Discontinuous Transmission) for sending encoded audio data.
                       *   <small>This might help to reduce bandwidth as it reduces the bitrate during silence or background noise, and
                       *   goes hand-in-hand with the <code>options.voiceActivityDetection</code> flag in <a href="#method_joinRoom">
                       *   <code>joinRoom()</code> method</a>.</small>
                       *   <small>When not provided, the default browser configuration is used.</small>
                       * @param {Boolean} [options.codecParams.audio.opus.useinbandfec] <blockquote class="info">
                       *   Note that this parameter should only be used for debugging purposes only.</blockquote>
                       *   The flag if OPUS audio codec has the capability to take advantage of the in-band FEC
                       *   (Forward Error Correction) when sending encoded audio data.
                       *   <small>This helps to reduce the harm of packet loss by encoding information about the previous packet loss.</small>
                       *   <small>When not provided, the default browser configuration is used.</small>
                       * @param {Number} [options.codecParams.audio.opus.maxplaybackrate] <blockquote class="info">
                       *   Note that this parameter should only be used for debugging purposes only.</blockquote>
                       *   The OPUS audio codec maximum output sampling rate in Hz (hertz) that is is capable of receiving
                       *   decoded audio data, to adjust to the hardware limitations and ensure that any sending audio data
                       *   would not encode at a higher sampling rate specified by this.
                       *   <small>This value must be between <code>8000</code> to <code>48000</code>.</small>
                       *   <small>When not provided, the default browser configuration is used.</small>
                       * @param {Number} [options.codecParams.minptime] <blockquote class="info">
                       *   Note that this parameter should only be used for debugging purposes only.</blockquote>
                       *   The OPUS audio codec receiving audio data decoder minimum length of time in milleseconds should be
                       *   encapsulated in a single received encoded audio data packet.
                       *   <small>This value must be between <code>3</code> to <code>120</code></small>
                       *   <small>When not provided, the default browser configuration is used.</small>
                       * @param {String} [options.priorityWeightScheme] <blockquote class="info">
                       *   Note that this parameter should only be used for debugging purposes only and may not work when
                       *   internals change.</blockquote> The User's priority weight to enforce User as offerer or answerer.
                       * - When not provided, its value is <code>AUTO</code>.
                       *   [Rel: Skylink.PRIORITY_WEIGHT_SCHEME]
                       * @param {Function} [callback] The callback function fired when request has completed.
                       *   <small>Function parameters signature is <code>function (error, success)</code></small>
                       *   <small>Function request completion is determined by the <a href="#event_readyStateChange">
                       *   <code>readyStateChange</code> event</a> <code>state</code> parameter payload value
                       *   as <code>COMPLETED</code> for request success.</small>
                       *   [Rel: Skylink.READY_STATE_CHANGE]
                       * @param {JSON|String} callback.error The error result in request.
                       * - When defined as string, it's the error when required App Key is not provided.
                       *   <small>Defined as <code>null</code> when there are no errors in request</small>
                       * @param {Number} callback.error.errorCode The <a href="#event_readyStateChange"><code>readyStateChange</code>
                       *   event</a> <code>error.errorCode</code> parameter payload value.
                       *   [Rel: Skylink.READY_STATE_CHANGE_ERROR]
                       * @param {Error|String} callback.error.error The <a href="#event_readyStateChange"><code>readyStateChange</code>
                       *   event</a> <code>error.content</code> parameter payload value.
                       * @param {Number} callback.error.status The <a href="#event_readyStateChange"><code>readyStateChange</code>
                       *   event</a> <code>error.status</code> parameter payload value.
                       * @param {JSON} callback.success The success result in request.
                       *   <small>Defined as <code>null</code> when there are errors in request</small>
                       * @param {String} callback.success.serverUrl The constructed REST URL requested to Auth server.
                       * @param {Number} callback.success.readyState The current ready state.
                       *   [Rel: Skylink.READY_STATE_CHANGE]
                       * @param {String} callback.success.selectedRoom The Room based on the current Room session token retrieved for.
                       * @param {String} callback.success.appKey The configured value of the <code>options.appKey</code>.
                       * @param {String} callback.success.defaultRoom The configured value of the <code>options.defaultRoom</code>.
                       * @param {String} callback.success.roomServer The configured value of the <code>options.roomServer</code>.
                       * @param {Boolean} callback.success.enableIceTrickle The configured value of the <code>options.enableIceTrickle</code>.
                       * @param {Boolean} callback.success.enableDataChannel The configured value of the <code>options.enableDataChannel</code>.
                       * @param {Boolean} callback.success.enableTURNServer The configured value of the <code>options.enableTURNServer</code>.
                       * @param {Boolean} callback.success.enableSTUNServer The configured value of the <code>options.enableSTUNServer</code>.
                       * @param {Boolean} callback.success.TURNTransport The configured value of the <code>options.TURNServerTransport</code>.
                       * @param {Boolean} callback.success.audioFallback The configured value of the <code>options.audioFallback</code>.
                       * @param {Boolean} callback.success.forceSSL The configured value of the <code>options.forceSSL</code>.
                       * @param {String|JSON} callback.success.audioCodec The configured value of the <code>options.audioCodec</code>.
                       * @param {String|JSON} callback.success.videoCodec The configured value of the <code>options.videoCodec</code>.
                       * @param {Number} callback.success.socketTimeout The configured value of the <code>options.socketTimeout</code>.
                       * @param {Boolean} callback.success.forceTURNSSL The configured value of the <code>options.forceTURNSSL</code>.
                       * @param {Boolean} callback.success.forceTURN The configured value of the <code>options.forceTURN</code>.
                       * @param {Boolean} callback.success.usePublicSTUN The configured value of the <code>options.usePublicSTUN</code>.
                       * @param {Boolean} callback.success.disableVideoFecCodecs The configured value of the <code>options.disableVideoFecCodecs</code>.
                       * @param {Boolean} callback.success.disableComfortNoiseCodec The configured value of the <code>options.disableComfortNoiseCodec</code>.
                       * @param {Boolean} callback.success.disableREMB The configured value of the <code>options.disableREMB</code>.
                       * @param {JSON} callback.success.filterCandidatesType The configured value of the <code>options.filterCandidatesType</code>.
                       * @param {JSON} callback.success.throttleIntervals The configured value of the <code>options.throttleIntervals</code>.
                       * @param {Boolean} callback.success.throttleShouldThrowError The configured value of the <code>options.throttleShouldThrowError</code>.
                       * @param {JSON} callback.success.mcuUseRenegoRestart The configured value of the <code>options.mcuUseRenegoRestart</code>.
                       * @param {JSON} callback.success.iceServer The configured value of the <code>options.iceServer</code>.
                       *   <small>See the <code>.urls</code> property in this object for configured value if defined.</small>
                       * @param {JSON|String} callback.success.socketServer The configured value of the <code>options.socketServer</code>.
                       * @example
                       *   // Example 1: Using CORS authentication and connection to default Room
                       *   skylinkDemo(appKey, function (error, success) {
                       *     if (error) return;
                       *     skylinkDemo.joinRoom(); // Goes to default Room
                       *   });
                       *
                       *   // Example 2: Using CORS authentication and connection to a different Room
                       *   skylinkDemo(appKey, function (error, success) {
                       *     skylinkDemo.joinRoom("testxx"); // Goes to "testxx" Room
                       *   });
                       *
                       *   // Example 3: Using credentials authentication and connection to only default Room
                       *   var defaultRoom   = "test",
                       *       startDateTime = (new Date()).toISOString(),
                       *       duration      = 1, // Allows only User session to stay for 1 hour
                       *       appKeySecret  = "xxxxxxx",
                       *       hash          = CryptoJS.HmacSHA1(defaultRoom + "_" + duration + "_" + startDateTime, appKeySecret);
                       *       credentials   = encodeURIComponent(hash.toString(CryptoJS.enc.Base64));
                       *
                       *   skylinkDemo({
                       *     defaultRoom: defaultRoom,
                       *     appKey: appKey,
                       *     credentials: {
                       *       duration: duration,
                       *       startDateTime: startDateTime,
                       *       credentials: credentials
                       *     }
                       *   }, function (error, success) {
                       *     if (error) return;
                       *     skylinkDemo.joinRoom(); // Goes to default Room (switching to different Room is not allowed for credentials authentication)
                       *   });
                       * @trigger <ol class="desc-seq">
                       *   <li>If parameter <code>options</code> is not provided: <ol><li><b>ABORT</b> and return error.</li></ol></li>
                       *   <li>Checks if dependecies and browser APIs are available. <ol><li>If AdapterJS is not loaded: <ol>
                       *   <li><a href="#event_readyStateChange"><code>readyStateChange</code> event</a> triggers
                       *   parameter payload <code>state</code> as <code>ERROR</code> and <code>error.errorCode</code> as
                       *   <code>ADAPTER_NO_LOADED</code>.</li><li><b>ABORT</b> and return error.</li></ol></li>
                       *   <li>If socket.io-client is not loaded: <ol><li><a href="#event_readyStateChange">
                       *   <code>readyStateChange</code> event</a> triggers parameter payload <code>state</code>
                       *   as <code>ERROR</code> and <code>error.errorCode</code> as <code>NO_SOCKET_IO</code>.</li>
                       *   <li><b>ABORT</b> and return error. </li></ol></li>
                       *   <li>If XMLHttpRequest API is not available: <ol><li><a href="#event_readyStateChange">
                       *   <code>readyStateChange</code> event</a> triggers parameter payload <code>state</code>
                       *   as <code>ERROR</code> and <code>error.errorCode</code> as <code>NO_XMLHTTPREQUEST_SUPPORT</code>.</li>
                       *   <li><b>ABORT</b> and return error.</li></ol></li><li>If WebRTC is not supported by device: <ol>
                       *   <li><a href="#event_readyStateChange"><code>readyStateChange</code> event</a> triggers parameter
                       *   payload <code>state</code> as <code>ERROR</code> and <code>error.errorCode</code> as
                       *   <code>NO_WEBRTC_SUPPORT</code>.</li><li><b>ABORT</b> and return error.</li></ol></li></ol></li>
                       *   <li>Retrieves Room session token from Auth server. <ol>
                       *   <li><a href="#event_readyStateChange"><code>readyStateChange</code> event</a> triggers
                       *   parameter payload <code>state</code> as <code>LOADING</code>.</li>
                       *   <li>If retrieval was successful: <ol><li><a href="#event_readyStateChange"><code>readyStateChange</code> event</a>
                       *   triggers parameter payload <code>state</code> as <code>COMPLETED</code>.</li></ol></li><li>Else: <ol>
                       *   <li><a href="#event_readyStateChange"><code>readyStateChange</code> event</a> triggers parameter
                       *   payload <code>state</code> as <code>ERROR</code>.</li><li><b>ABORT</b> and return error.</li></ol></li></ol></li></ol>
                       * @for Skylink
                       * @since 0.5.5
                       */
                      Skylink.prototype.init = function(options, callback) {
                        var self = this;
                      
                        if (typeof options === 'function'){
                          callback = options;
                          options = undefined;
                        }
                      
                        if (!options) {
                          var error = 'No API key provided';
                          log.error(error);
                          if (typeof callback === 'function'){
                            callback(error,null);
                          }
                          return;
                        }
                      
                        var appKey, room, defaultRoom;
                        var startDateTime, duration, credentials;
                        var roomServer = self._roomServer;
                        // NOTE: Should we get all the default values from the variables
                        // rather than setting it?
                        var enableIceTrickle = true;
                        var enableDataChannel = true;
                        var enableSTUNServer = true;
                        var enableTURNServer = true;
                        var TURNTransport = self.TURN_TRANSPORT.ANY;
                        var audioFallback = false;
                        var forceSSL = false;
                        var socketTimeout = 20000;
                        var forceTURNSSL = false;
                        var audioCodec = self.AUDIO_CODEC.AUTO;
                        var videoCodec = self.VIDEO_CODEC.AUTO;
                        var forceTURN = false;
                        var usePublicSTUN = true;
                        var disableVideoFecCodecs = false;
                        var disableComfortNoiseCodec = false;
                        var disableREMB = false;
                        var filterCandidatesType = {
                          host: false,
                          srflx: false,
                          relay: false
                        };
                        var throttleIntervals = {
                          shareScreen: 10000,
                          refreshConnection: 5000,
                          getUserMedia: 0
                        };
                        var throttleShouldThrowError = false;
                        var mcuUseRenegoRestart = false;
                        var iceServer = null;
                        var socketServer = null;
                        var codecParams = {
                          audio: { opus: {} },
                          video: { h264: {}, vp8: {}, vp9: {} }
                        };
                        var priorityWeightScheme = self.PRIORITY_WEIGHT_SCHEME.AUTO;
                      
                        log.log('Provided init options:', options);
                      
                        if (typeof options === 'string') {
                          // set all the default api key, default room and room
                          appKey = options;
                          defaultRoom = appKey;
                          room = appKey;
                        } else {
                          // set the api key
                          appKey = options.appKey || options.apiKey;
                          // set the room server
                          roomServer = (typeof options.roomServer === 'string') ?
                            options.roomServer : roomServer;
                          // check room server if it ends with /. Remove the extra /
                          roomServer = (roomServer.lastIndexOf('/') ===
                            (roomServer.length - 1)) ? roomServer.substring(0,
                            roomServer.length - 1) : roomServer;
                          // set the default room
                          defaultRoom = (typeof options.defaultRoom === 'string' && options.defaultRoom) ?
                            options.defaultRoom : appKey;
                          // set the selected room
                          room = defaultRoom;
                          // set ice trickle option
                          enableIceTrickle = (typeof options.enableIceTrickle === 'boolean') ?
                            options.enableIceTrickle : enableIceTrickle;
                          // set data channel option
                          enableDataChannel = (typeof options.enableDataChannel === 'boolean') ?
                            options.enableDataChannel : enableDataChannel;
                          // set stun server option
                          enableSTUNServer = (typeof options.enableSTUNServer === 'boolean') ?
                            options.enableSTUNServer : enableSTUNServer;
                          // set turn server option
                          enableTURNServer = (typeof options.enableTURNServer === 'boolean') ?
                            options.enableTURNServer : enableTURNServer;
                          // set the force ssl always option
                          forceSSL = (typeof options.forceSSL === 'boolean') ?
                            options.forceSSL : forceSSL;
                          // set the socket timeout option
                          socketTimeout = (typeof options.socketTimeout === 'number') ?
                            options.socketTimeout : socketTimeout;
                          // set the socket timeout option to be above 5000
                          socketTimeout = (socketTimeout < 5000) ? 5000 : socketTimeout;
                          // set the force turn ssl always option
                          forceTURNSSL = (typeof options.forceTURNSSL === 'boolean') ?
                            options.forceTURNSSL : forceTURNSSL;
                          // set the force turn server option
                          forceTURN = (typeof options.forceTURN === 'boolean') ?
                            options.forceTURN : forceTURN;
                          // set the use public stun option
                          usePublicSTUN = (typeof options.usePublicSTUN === 'boolean') ?
                            options.usePublicSTUN : usePublicSTUN;
                          // set the use of disabling ulpfec and red codecs
                          disableVideoFecCodecs = (typeof options.disableVideoFecCodecs === 'boolean') ?
                            options.disableVideoFecCodecs : disableVideoFecCodecs;
                          // set the use of disabling CN codecs
                          disableComfortNoiseCodec = (typeof options.disableComfortNoiseCodec === 'boolean') ?
                            options.disableComfortNoiseCodec : disableComfortNoiseCodec;
                          // set the use of disabling REMB packets
                          disableREMB = (typeof options.disableREMB === 'boolean') ?
                            options.disableREMB : disableREMB;
                          // set the flag if throttling should throw error when called less than the interval timeout configured
                          throttleShouldThrowError = (typeof options.throttleShouldThrowError === 'boolean') ?
                            options.throttleShouldThrowError : throttleShouldThrowError;
                          // set the flag if MCU refreshConnection() should use renegotiation
                          mcuUseRenegoRestart = (typeof options.mcuUseRenegoRestart === 'boolean') ?
                            options.mcuUseRenegoRestart : mcuUseRenegoRestart;
                          // set the flag if MCU refreshConnection() should use renegotiation
                          mcuUseRenegoRestart = (typeof options.mcuUseRenegoRestart === 'boolean') ?
                            options.mcuUseRenegoRestart : mcuUseRenegoRestart;
                          // set the use of filtering ICE candidates
                          if (typeof options.filterCandidatesType === 'object' && options.filterCandidatesType) {
                            filterCandidatesType.host = (typeof options.filterCandidatesType.host === 'boolean') ?
                              options.filterCandidatesType.host : filterCandidatesType.host;
                            filterCandidatesType.srflx = (typeof options.filterCandidatesType.srflx === 'boolean') ?
                              options.filterCandidatesType.srflx : filterCandidatesType.srflx;
                            filterCandidatesType.relay = (typeof options.filterCandidatesType.relay === 'boolean') ?
                              options.filterCandidatesType.relay : filterCandidatesType.relay;
                          }
                          // set the use of throttling interval timeouts
                          if (typeof options.throttleIntervals === 'object' && options.throttleIntervals) {
                            throttleIntervals.shareScreen = (typeof options.throttleIntervals.shareScreen === 'number') ?
                              options.throttleIntervals.shareScreen : throttleIntervals.shareScreen;
                            throttleIntervals.refreshConnection = (typeof options.throttleIntervals.refreshConnection === 'number') ?
                              options.throttleIntervals.refreshConnection : throttleIntervals.refreshConnection;
                            throttleIntervals.getUserMedia = (typeof options.throttleIntervals.getUserMedia === 'number') ?
                              options.throttleIntervals.getUserMedia : throttleIntervals.getUserMedia;
                          }
                      
                          // set the Signaling server url for debugging purposes
                          if (options.socketServer) {
                            if (typeof options.socketServer === 'string') {
                              socketServer = options.socketServer;
                            } else if (typeof options.socketServer === 'object' && options.socketServer.url &&
                              typeof options.socketServer.url === 'string') {
                              socketServer = {
                                url: options.socketServer.url,
                                ports: Array.isArray(options.socketServer.ports) && options.socketServer.ports.length > 0 ?
                                  options.socketServer.ports : [],
                                protocol: options.socketServer.protocol && typeof options.socketServer.protocol === 'string' ?
                                  options.socketServer.protocol : null
                              };
                            }
                          }
                      
                          // set the Signaling server url for debugging purposes
                          if (options.iceServer) {
                            iceServer = typeof options.iceServer === 'string' ? { urls: [options.iceServer] } :
                              (Array.isArray(options.iceServer) && options.iceServer.length > 0 &&
                              options.iceServer[0] && typeof options.iceServer[0] === 'string' ? { urls: options.iceServer } : null);
                          }
                      
                          // set turn transport option
                          if (typeof options.TURNServerTransport === 'string') {
                            // loop out for every transport option
                            for (var ttType in self.TURN_TRANSPORT) {
                              // do a check if the transport option is valid
                              if (self.TURN_TRANSPORT.hasOwnProperty(ttType) && self.TURN_TRANSPORT[ttType] === options.TURNServerTransport) {
                                TURNTransport = options.TURNServerTransport;
                                break;
                              }
                            }
                          }
                      
                          // set the preferred audio codec
                          if (options.audioCodec && ((typeof options.audioCodec === 'string' &&
                            options.audioCodec !== self.AUDIO_CODEC.AUTO) || (typeof options.audioCodec === 'object' &&
                            options.audioCodec.codec && typeof options.audioCodec.codec === 'string' &&
                            options.audioCodec.codec !== self.AUDIO_CODEC.AUTO))) {
                            // loop out for every audio codec option
                            for (var acType in self.AUDIO_CODEC) {
                              // do a check if the audio codec option is valid
                              if (self.AUDIO_CODEC.hasOwnProperty(acType)) {
                                if (typeof options.audioCodec === 'string' && self.AUDIO_CODEC[acType] === options.audioCodec) {
                                  audioCodec = options.audioCodec;
                                  break;
                                } else if (typeof options.audioCodec === 'object' && self.AUDIO_CODEC[acType] === options.audioCodec.codec) {
                                  audioCodec = {
                                    codec: options.audioCodec.codec,
                                    samplingRate: typeof options.audioCodec.samplingRate === 'number' &&
                                      options.audioCodec.samplingRate > 0 ? options.audioCodec.samplingRate : null,
                                    channels: typeof options.audioCodec.channels === 'number' &&
                                      options.audioCodec.channels > 0 ? options.audioCodec.channels : null
                                  };
                                  break;
                                }
                              }
                            }
                          }
                      
                          // set the preferred video codec
                          if (options.videoCodec && ((typeof options.videoCodec === 'string' &&
                            options.videoCodec !== self.VIDEO_CODEC.AUTO) || (typeof options.videoCodec === 'object' &&
                            options.videoCodec.codec && typeof options.videoCodec.codec === 'string' &&
                            options.videoCodec.codec !== self.VIDEO_CODEC.AUTO))) {
                            // loop out for every video codec option
                            for (var vcType in self.VIDEO_CODEC) {
                              // do a check if the video codec option is valid
                              if (self.VIDEO_CODEC.hasOwnProperty(vcType)) {
                                if (typeof options.videoCodec === 'string' && self.VIDEO_CODEC[vcType] === options.videoCodec) {
                                  videoCodec = options.videoCodec;
                                  break;
                                } else if (typeof options.videoCodec === 'object' && self.VIDEO_CODEC[vcType] === options.videoCodec.codec) {
                                  videoCodec = {
                                    codec: options.videoCodec.codec,
                                    samplingRate: typeof options.videoCodec.samplingRate === 'number' &&
                                      options.videoCodec.samplingRate > 0 ? options.videoCodec.samplingRate : null
                                  };
                                  break;
                                }
                              }
                            }
                          }
                      
                          // set the priority weight scheme
                          if (typeof options.priorityWeightScheme === 'string') {
                            // loop out for every transport option
                            for (var pwsType in self.PRIORITY_WEIGHT_SCHEME) {
                              // do a check if the transport option is valid
                              if (self.PRIORITY_WEIGHT_SCHEME.hasOwnProperty(pwsType) &&
                                self.PRIORITY_WEIGHT_SCHEME[pwsType] === options.priorityWeightScheme) {
                                priorityWeightScheme = options.priorityWeightScheme;
                                break;
                              }
                            }
                          }
                      
                          // set the codec params
                          if (options.codecParams && typeof options.codecParams === 'object') {
                            // Set audio codecs params
                            if (options.codecParams.audio && typeof options.codecParams.audio === 'object') {
                              // Set the audio codec opus params
                              if (options.codecParams.audio.opus && typeof options.codecParams.audio.opus === 'object') {
                                codecParams.audio.opus = {
                                  stereo: typeof options.codecParams.audio.opus.stereo === 'boolean' ?
                                    options.codecParams.audio.opus.stereo : null,
                                  usedtx: typeof options.codecParams.audio.opus.usedtx === 'boolean' ?
                                    options.codecParams.audio.opus.usedtx : null,
                                  useinbandfec: typeof options.codecParams.audio.opus.useinbandfec === 'boolean' ?
                                    options.codecParams.audio.opus.useinbandfec : null,
                                  maxplaybackrate: typeof options.codecParams.audio.opus.maxplaybackrate === 'number' &&
                                    options.codecParams.audio.opus.maxplaybackrate >= 8000 &&
                                    options.codecParams.audio.opus.maxplaybackrate <= 48000 ?
                                    options.codecParams.audio.opus.maxplaybackrate : null,
                                  minptime: typeof options.codecParams.audio.opus.minptime === 'number' &&
                                    options.codecParams.audio.opus.minptime >= 3 ? options.codecParams.audio.opus.minptime : null
                                };
                              }
                            }
                            // Set video codecs params
                            if (options.codecParams.video && typeof options.codecParams.video === 'object') {
                              // Set the video codec H264 params
                              if (options.codecParams.video.h264 && typeof options.codecParams.video.h264 === 'object') {
                                codecParams.video.h264 = {
                                  // Only allowing profile-level-id change for experimental fixes or changes incase..
                                  // Strong NOT recommended, this is like an information
                                  profileLevelId: options.codecParams.video.h264.profileLevelId &&
                                    typeof options.codecParams.video.h264.profileLevelId === 'string' ?
                                    options.codecParams.video.h264.profileLevelId : null,
                                  levelAsymmetryAllowed: typeof options.codecParams.video.h264.levelAsymmetryAllowed === 'boolean' ?
                                    options.codecParams.video.h264.levelAsymmetryAllowed : null,
                                  packetizationMode: typeof options.codecParams.video.h264.packetizationMode === 'boolean' ?
                                    options.codecParams.video.h264.packetizationMode : null
                                };
                              }
                              // Set the video codec VP8 params
                              if (options.codecParams.video.vp8 && typeof options.codecParams.video.vp8 === 'object') {
                                // Only allowing max-fs, max-fr change for experimental fixes or changes incase..
                                // (NOT used for any other purposes)!!!!
                                // Strong NOT recommended, this is like an information
                                codecParams.video.vp8 = {
                                  maxFs: typeof options.codecParams.video.vp8.maxFs === 'number' ?
                                    options.codecParams.video.vp8.maxFs : null,
                                  maxFr: typeof options.codecParams.video.vp8.maxFr === 'number' ?
                                    options.codecParams.video.vp8.maxFr : null
                                };
                              }
                              // Set the video codec VP9 params
                              if (options.codecParams.video.vp9 && typeof options.codecParams.video.vp9 === 'object') {
                                // Only allowing max-fs, max-fr change for experimental fixes or changes incase..
                                // (NOT used for any other purposes)!!!!
                                // Strong NOT recommended, this is like an information
                                codecParams.video.vp9 = {
                                  maxFs: typeof options.codecParams.video.vp9.maxFs === 'number' ?
                                    options.codecParams.video.vp9.maxFs : null,
                                  maxFr: typeof options.codecParams.video.vp9.maxFr === 'number' ?
                                    options.codecParams.video.vp9.maxFr : null
                                };
                              }
                            }
                          }
                      
                          // set audio fallback option
                          audioFallback = options.audioFallback || audioFallback;
                          // Custom default meeting timing and duration
                          // Fallback to default if no duration or startDateTime provided
                          if (options.credentials &&
                            typeof options.credentials.credentials === 'string' &&
                            typeof options.credentials.duration === 'number' &&
                            typeof options.credentials.startDateTime === 'string') {
                            // set start data time
                            startDateTime = options.credentials.startDateTime;
                            // set the duration
                            duration = options.credentials.duration;
                            // set the credentials
                            credentials = options.credentials.credentials;
                          }
                      
                          // if force turn option is set to on
                          if (forceTURN === true) {
                            enableTURNServer = true;
                            enableSTUNServer = false;
                            filterCandidatesType.host = true;
                            filterCandidatesType.srflx = true;
                            filterCandidatesType.relay = false;
                          }
                        }
                      
                        if (window.webrtcDetectedBrowser === 'edge') {
                          forceTURNSSL = false;
                          TURNTransport = self.TURN_TRANSPORT.UDP;
                          enableDataChannel = false;
                        }
                      
                        // api key path options
                        self._appKey = appKey;
                        self._roomServer = roomServer;
                        self._defaultRoom = defaultRoom;
                        self._selectedRoom = room;
                        self._path = roomServer + '/api/' + appKey + '/' + room;
                        // set credentials if there is
                        if (credentials && startDateTime && duration) {
                          self._roomStart = startDateTime;
                          self._roomDuration = duration;
                          self._roomCredentials = credentials;
                          self._path += (credentials) ? ('/' + startDateTime + '/' +
                            duration + '?&cred=' + credentials) : '';
                        }
                      
                        self._path += ((credentials) ? '&' : '?') + 'rand=' + (new Date()).toISOString();
                      
                        // skylink functionality options
                        self._enableIceTrickle = enableIceTrickle;
                        self._enableDataChannel = enableDataChannel;
                        self._enableSTUN = enableSTUNServer;
                        self._enableTURN = enableTURNServer;
                        self._TURNTransport = TURNTransport;
                        self._audioFallback = audioFallback;
                        self._forceSSL = forceSSL;
                        self._socketTimeout = socketTimeout;
                        self._forceTURNSSL = forceTURNSSL;
                        self._selectedAudioCodec = audioCodec;
                        self._selectedVideoCodec = videoCodec;
                        self._forceTURN = forceTURN;
                        self._usePublicSTUN = usePublicSTUN;
                        self._disableVideoFecCodecs = disableVideoFecCodecs;
                        self._disableComfortNoiseCodec = disableComfortNoiseCodec;
                        self._filterCandidatesType = filterCandidatesType;
                        self._throttlingTimeouts = throttleIntervals;
                        self._throttlingShouldThrowError = throttleShouldThrowError;
                        self._disableREMB = disableREMB;
                        self._mcuUseRenegoRestart = mcuUseRenegoRestart;
                        self._iceServer = iceServer;
                        self._socketServer = socketServer;
                        self._codecParams = codecParams;
                        self._priorityWeightScheme = priorityWeightScheme;
                      
                        log.log('Init configuration:', {
                          serverUrl: self._path,
                          readyState: self._readyState,
                          appKey: self._appKey,
                          roomServer: self._roomServer,
                          defaultRoom: self._defaultRoom,
                          selectedRoom: self._selectedRoom,
                          enableDataChannel: self._enableDataChannel,
                          enableIceTrickle: self._enableIceTrickle,
                          enableTURNServer: self._enableTURN,
                          enableSTUNServer: self._enableSTUN,
                          TURNTransport: self._TURNTransport,
                          audioFallback: self._audioFallback,
                          forceSSL: self._forceSSL,
                          socketTimeout: self._socketTimeout,
                          forceTURNSSL: self._forceTURNSSL,
                          audioCodec: self._selectedAudioCodec,
                          videoCodec: self._selectedVideoCodec,
                          forceTURN: self._forceTURN,
                          usePublicSTUN: self._usePublicSTUN,
                          disableVideoFecCodecs: self._disableVideoFecCodecs,
                          disableComfortNoiseCodec: self._disableComfortNoiseCodec,
                          disableREMB: self._disableREMB,
                          filterCandidatesType: self._filterCandidatesType,
                          throttleIntervals: self._throttlingTimeouts,
                          throttleShouldThrowError: self._throttlingShouldThrowError,
                          mcuUseRenegoRestart: self._mcuUseRenegoRestart,
                          iceServer: self._iceServer,
                          socketServer: self._socketServer,
                          codecParams: self._codecParams,
                          priorityWeightScheme: self._priorityWeightScheme
                        });
                        // trigger the readystate
                        self._readyState = 0;
                        self._trigger('readyStateChange', self.READY_STATE_CHANGE.INIT, null, self._selectedRoom);
                      
                        if (typeof callback === 'function'){
                          var hasTriggered = false;
                      
                          var readyStateChangeFn = function (readyState, error) {
                            if (!hasTriggered) {
                              if (readyState === self.READY_STATE_CHANGE.COMPLETED) {
                                log.log([null, 'Socket', null, 'Firing callback. ' +
                                'Ready state change has met provided state ->'], readyState);
                                hasTriggered = true;
                                self.off('readyStateChange', readyStateChangeFn);
                                callback(null,{
                                  serverUrl: self._path,
                                  readyState: self._readyState,
                                  appKey: self._appKey,
                                  roomServer: self._roomServer,
                                  defaultRoom: self._defaultRoom,
                                  selectedRoom: self._selectedRoom,
                                  enableDataChannel: self._enableDataChannel,
                                  enableIceTrickle: self._enableIceTrickle,
                                  enableTURNServer: self._enableTURN,
                                  enableSTUNServer: self._enableSTUN,
                                  TURNTransport: self._TURNTransport,
                                  audioFallback: self._audioFallback,
                                  forceSSL: self._forceSSL,
                                  socketTimeout: self._socketTimeout,
                                  forceTURNSSL: self._forceTURNSSL,
                                  audioCodec: self._selectedAudioCodec,
                                  videoCodec: self._selectedVideoCodec,
                                  forceTURN: self._forceTURN,
                                  usePublicSTUN: self._usePublicSTUN,
                                  disableVideoFecCodecs: self._disableVideoFecCodecs,
                                  disableComfortNoiseCodec: self._disableComfortNoiseCodec,
                                  disableREMB: self._disableREMB,
                                  filterCandidatesType: self._filterCandidatesType,
                                  throttleIntervals: self._throttlingTimeouts,
                                  throttleShouldThrowError: self._throttlingShouldThrowError,
                                  mcuUseRenegoRestart: self._mcuUseRenegoRestart,
                                  iceServer: self._iceServer,
                                  socketServer: self._socketServer,
                                  codecParams: self._codecParams,
                                  priorityWeightScheme: self._priorityWeightScheme
                                });
                              } else if (readyState === self.READY_STATE_CHANGE.ERROR) {
                                log.log([null, 'Socket', null, 'Firing callback. ' +
                                  'Ready state change has met provided state ->'], readyState);
                                log.debug([null, 'Socket', null, 'Ready state met failure'], error);
                                hasTriggered = true;
                                self.off('readyStateChange', readyStateChangeFn);
                                callback({
                                  error: new Error(error),
                                  errorCode: error.errorCode,
                                  status: error.status
                                },null);
                              }
                            }
                          };
                      
                          self.on('readyStateChange', readyStateChangeFn);
                        }
                      
                        self._loadInfo();
                      };
                      
                      /**
                       * Starts retrieving Room credentials information from API server.
                       * @method _requestServerInfo
                       * @private
                       * @for Skylink
                       * @since 0.5.2
                       */
                      Skylink.prototype._requestServerInfo = function(method, url, callback, params) {
                        var self = this;
                        // XDomainRequest is supported in IE8 - 9
                        var useXDomainRequest = typeof window.XDomainRequest === 'function' ||
                          typeof window.XDomainRequest === 'object';
                      
                        self._socketUseXDR = useXDomainRequest;
                        var xhr;
                      
                        // set force SSL option
                        url = (self._forceSSL) ? 'https:' + url : url;
                      
                        if (useXDomainRequest) {
                          log.debug([null, 'XMLHttpRequest', method, 'Using XDomainRequest. ' +
                            'XMLHttpRequest is now XDomainRequest'], {
                            agent: window.webrtcDetectedBrowser,
                            version: window.webrtcDetectedVersion
                          });
                          xhr = new XDomainRequest();
                          xhr.setContentType = function (contentType) {
                            xhr.contentType = contentType;
                          };
                        } else {
                          log.debug([null, 'XMLHttpRequest', method, 'Using XMLHttpRequest'], {
                            agent: window.webrtcDetectedBrowser,
                            version: window.webrtcDetectedVersion
                          });
                          xhr = new window.XMLHttpRequest();
                          xhr.setContentType = function (contentType) {
                            xhr.setRequestHeader('Content-type', contentType);
                          };
                        }
                      
                        xhr.onload = function () {
                          var response = xhr.responseText || xhr.response;
                          var status = xhr.status || 200;
                          log.debug([null, 'XMLHttpRequest', method, 'Received sessions parameters'],
                            JSON.parse(response || '{}'));
                          callback(status, JSON.parse(response || '{}'));
                        };
                      
                        xhr.onerror = function (error) {
                          log.error([null, 'XMLHttpRequest', method, 'Failed retrieving information:'],
                            { status: xhr.status });
                          self._readyState = -1;
                          self._trigger('readyStateChange', self.READY_STATE_CHANGE.ERROR, {
                            status: xhr.status || null,
                            content: 'Network error occurred. (Status: ' + xhr.status + ')',
                            errorCode: self.READY_STATE_CHANGE_ERROR.XML_HTTP_REQUEST_ERROR
                          }, self._selectedRoom);
                        };
                      
                        xhr.onprogress = function () {
                          log.debug([null, 'XMLHttpRequest', method,
                            'Retrieving information and config from webserver. Url:'], url);
                          log.debug([null, 'XMLHttpRequest', method, 'Provided parameters:'], params);
                        };
                      
                        xhr.open(method, url, true);
                        if (params) {
                          xhr.setContentType('application/json;charset=UTF-8');
                          xhr.send(JSON.stringify(params));
                        } else {
                          xhr.send();
                        }
                      };
                      
                      /**
                       * Parses the Room credentials information retrieved from API server.
                       * @method _parseInfo
                       * @private
                       * @for Skylink
                       * @since 0.5.2
                       */
                      Skylink.prototype._parseInfo = function(info) {
                        log.log('Parsing parameter from server', info);
                        if (!info.pc_constraints && !info.offer_constraints) {
                          this._trigger('readyStateChange', this.READY_STATE_CHANGE.ERROR, {
                            status: 200,
                            content: info.info,
                            errorCode: info.error
                          }, self._selectedRoom);
                          return;
                        }
                      
                        log.debug('Peer connection constraints:', info.pc_constraints);
                        log.debug('Offer constraints:', info.offer_constraints);
                      
                        this._key = info.cid;
                        this._appKeyOwner = info.apiOwner;
                        this._signalingServer = info.ipSigserver;
                        this._isPrivileged = info.isPrivileged;
                        this._autoIntroduce = info.autoIntroduce;
                      
                        this._user = {
                          uid: info.username,
                          token: info.userCred,
                          timeStamp: info.timeStamp,
                          streams: [],
                          info: {}
                        };
                        this._room = {
                          id: info.room_key,
                          token: info.roomCred,
                          startDateTime: info.start,
                          duration: info.len,
                          connection: {
                            peerConstraints: JSON.parse(info.pc_constraints),
                            peerConfig: null,
                            offerConstraints: JSON.parse(info.offer_constraints),
                            sdpConstraints: {
                              mandatory: {
                                OfferToReceiveAudio: true,
                                OfferToReceiveVideo: true
                              }
                            },
                            mediaConstraints: JSON.parse(info.media_constraints)
                          }
                        };
                        //this._parseDefaultMediaStreamSettings(this._room.connection.mediaConstraints);
                      
                        // set the socket ports
                        this._socketPorts = {
                          'http:': info.httpPortList,
                          'https:': info.httpsPortList
                        };
                      
                        // use default bandwidth and media resolution provided by server
                        //this._streamSettings.bandwidth = info.bandwidth;
                        //this._streamSettings.video = info.video;
                        this._readyState = 2;
                        this._trigger('readyStateChange', this.READY_STATE_CHANGE.COMPLETED, null, this._selectedRoom);
                        log.info('Parsed parameters from webserver. ' +
                          'Ready for web-realtime communication');
                      
                      };
                      
                      /**
                       * Loads and checks the dependencies if they are loaded correctly.
                       * @method _loadInfo
                       * @private
                       * @for Skylink
                       * @since 0.5.2
                       */
                      Skylink.prototype._loadInfo = function() {
                        var self = this;
                      
                        // check if adapterjs has been loaded already first or not
                        var adapter = (function () {
                          try {
                            return window.AdapterJS || AdapterJS;
                          } catch (error) {
                            return false;
                          }
                        })();
                      
                        if (!(!!adapter ? typeof adapter.webRTCReady === 'function' : false)) {
                          var noAdapterErrorMsg = 'AdapterJS dependency is not loaded or incorrect AdapterJS dependency is used';
                          self._trigger('readyStateChange', self.READY_STATE_CHANGE.ERROR, {
                            status: null,
                            content: noAdapterErrorMsg,
                            errorCode: self.READY_STATE_CHANGE_ERROR.ADAPTER_NO_LOADED
                          }, self._selectedRoom);
                          return;
                        }
                        if (!window.io) {
                          log.error('Socket.io not loaded. Please load socket.io');
                          self._readyState = -1;
                          self._trigger('readyStateChange', self.READY_STATE_CHANGE.ERROR, {
                            status: null,
                            content: 'Socket.io not found',
                            errorCode: self.READY_STATE_CHANGE_ERROR.NO_SOCKET_IO
                          }, self._selectedRoom);
                          return;
                        }
                        if (!window.XMLHttpRequest) {
                          log.error('XMLHttpRequest not supported. Please upgrade your browser');
                          self._readyState = -1;
                          self._trigger('readyStateChange', self.READY_STATE_CHANGE.ERROR, {
                            status: null,
                            content: 'XMLHttpRequest not available',
                            errorCode: self.READY_STATE_CHANGE_ERROR.NO_XMLHTTPREQUEST_SUPPORT
                          }, self._selectedRoom);
                          return;
                        }
                        if (!self._path) {
                          log.error('Skylink is not initialised. Please call init() first');
                          self._readyState = -1;
                          self._trigger('readyStateChange', self.READY_STATE_CHANGE.ERROR, {
                            status: null,
                            content: 'No API Path is found',
                            errorCode: self.READY_STATE_CHANGE_ERROR.NO_PATH
                          }, self._selectedRoom);
                          return;
                        }
                        adapter.webRTCReady(function () {
                          self._isUsingPlugin = !!adapter.WebRTCPlugin.plugin && !!adapter.WebRTCPlugin.plugin.VERSION;
                      
                          // Prevent empty object returned when constructing the RTCPeerConnection object
                          if (!(function () {
                            try {
                              var p = new window.RTCPeerConnection(null);
                              // IE returns as typeof object
                              return ['object', 'function'].indexOf(typeof p.createOffer) > -1 && p.createOffer !== null;
                            } catch (e) {
                              return false;
                            }
                          })()) {
                            if (window.RTCPeerConnection && self._isUsingPlugin) {
                              log.error('Plugin is not available. Please check plugin status.');
                            } else {
                              log.error('WebRTC not supported. Please upgrade your browser');
                            }
                            self._readyState = -1;
                            self._trigger('readyStateChange', self.READY_STATE_CHANGE.ERROR, {
                              status: null,
                              content: self._isUsingPlugin && window.RTCPeerConnection ? 'Plugin is not available' : 'WebRTC not available',
                              errorCode: self.READY_STATE_CHANGE_ERROR.NO_WEBRTC_SUPPORT
                            }, self._selectedRoom);
                            return;
                          }
                      
                          var getCodecsSupportCalled = false;
                          self._getCodecsSupport(function (error) {
                            if (getCodecsSupportCalled) {
                              return;
                            }
                            getCodecsSupportCalled = true;
                            if (error) {
                              log.error(error);
                              self._readyState = -1;
                              self._trigger('readyStateChange', self.READY_STATE_CHANGE.ERROR, {
                                status: null,
                                content: error.message || error.toString(),
                                errorCode: self.READY_STATE_CHANGE_ERROR.PARSE_CODECS
                              }, self._selectedRoom);
                              return;
                            }
                      
                            if (Object.keys(self._currentCodecSupport.audio).length === 0 && Object.keys(self._currentCodecSupport.video).length === 0) {
                              log.error('No audio/video codecs available to start connection.');
                              self._readyState = -1;
                              self._trigger('readyStateChange', self.READY_STATE_CHANGE.ERROR, {
                                status: null,
                                content: 'No audio/video codecs available to start connection',
                                errorCode: self.READY_STATE_CHANGE_ERROR.PARSE_CODECS
                              }, self._selectedRoom);
                              return;
                            }
                      
                            self._readyState = 1;
                            self._trigger('readyStateChange', self.READY_STATE_CHANGE.LOADING, null, self._selectedRoom);
                            self._requestServerInfo('GET', self._path, function(status, response) {
                              if (status !== 200) {
                                // 403 - Room is locked
                                // 401 - API Not authorized
                                // 402 - run out of credits
                                var errorMessage = 'XMLHttpRequest status not OK\nStatus was: ' + status;
                                self._readyState = 0;
                                self._trigger('readyStateChange', self.READY_STATE_CHANGE.ERROR, {
                                  status: status,
                                  content: (response) ? (response.info || errorMessage) : errorMessage,
                                  errorCode: response.error ||
                                    self.READY_STATE_CHANGE_ERROR.INVALID_XMLHTTPREQUEST_STATUS
                                }, self._selectedRoom);
                                return;
                              }
                              self._parseInfo(response);
                            });
                          });
                        });
                      };
                      
                      /**
                       * Starts initialising for Room credentials for room name provided in <code>joinRoom()</code> method.
                       * @method _initSelectedRoom
                       * @private
                       * @for Skylink
                       * @since 0.5.5
                       */
                      Skylink.prototype._initSelectedRoom = function(room, callback) {
                        var self = this;
                        if (typeof room === 'function' || typeof room === 'undefined') {
                          log.error('Invalid room provided. Room:', room);
                          return;
                        }
                        var defaultRoom = self._defaultRoom;
                        var initOptions = {
                          appKey: self._appKey,
                          roomServer: self._roomServer,
                          defaultRoom: room,
                          enableDataChannel: self._enableDataChannel,
                          enableIceTrickle: self._enableIceTrickle,
                          enableTURNServer: self._enableTURN,
                          enableSTUNServer: self._enableSTUN,
                          TURNServerTransport: self._TURNTransport,
                          audioFallback: self._audioFallback,
                          forceSSL: self._forceSSL,
                          socketTimeout: self._socketTimeout,
                          forceTURNSSL: self._forceTURNSSL,
                          audioCodec: self._selectedAudioCodec,
                          videoCodec: self._selectedVideoCodec,
                          forceTURN: self._forceTURN,
                          usePublicSTUN: self._usePublicSTUN,
                          disableVideoFecCodecs: self._disableVideoFecCodecs,
                          disableComfortNoiseCodec: self._disableComfortNoiseCodec,
                          disableREMB: self._disableREMB,
                          filterCandidatesType: self._filterCandidatesType,
                          throttleIntervals: self._throttlingTimeouts,
                          throttleShouldThrowError: self._throttlingShouldThrowError,
                          mcuUseRenegoRestart: self._mcuUseRenegoRestart,
                          iceServer: self._iceServer ? self._iceServer.urls : null,
                          socketServer: self._socketServer ? self._socketServer : null,
                          codecParams: self._codecParams ? self._codecParams : null,
                          priorityWeightScheme: self._priorityWeightScheme ? self._priorityWeightScheme : null
                        };
                        if (self._roomCredentials) {
                          initOptions.credentials = {
                            credentials: self._roomCredentials,
                            duration: self._roomDuration,
                            startDateTime: self._roomStart
                          };
                        }
                        self.init(initOptions, function (error, success) {
                          self._defaultRoom = defaultRoom;
                          if (error) {
                            callback(error, null);
                          } else {
                            callback(null, success);
                          }
                        });
                      };