The Developer documentation Developer Hub

Welcome to the Developer documentation developer hub. You'll find comprehensive guides and documentation to help you start working with Developer documentation as quickly as possible, as well as support if you get stuck. Let's jump right in!

Get Started    

Application API

Introduction

The FrameApp API lets you seamlessly integrate desktop applications into your own custom web-based workflows. Using JavaScript, the API gives you control of the Frame Terminal, an HTML5 client for remotely accessing applications hosted on your Frame Platform account in the public cloud. The API lets you start, stop or resume application sessions, query available system capacity, and much more.

Importing

In order to start using the FrameApp API, you need to import just a single script on your web page:

<script src="app.fra.me/embed/frame_app.js"></script>

After the script has been loaded, you will have the frameApi namespace on your window scope. That namespace contains all classes which are offered via the API and which you need in order to integrate a Frame-hosted application into your web page.

The most important class in our namespace is FrameApp. You can find it in the root namespace scope, directly under frameApi. You'll use that class to control the Frame Terminal, to start or stop an application session, and more.

Initialization of the FrameApp instance

FrameApp is a JavaScript class and takes a configuration object as the only parameter on its constructor. The only required field of the configuration object is the hash of the app which you want to use.

You can find the hash in your Frame account's Dashboard by going to the "APPS" section. Select the "Production" tab, click on the down arrow next your application's icon and select "Embed app" as shown below.

On the "Share Application (Production)" modal, you'll see a URL for the preview page of your application on the left side. This URL contains the application hash that you need for FrameApp. In the example above, the URL is:

 https://live.fra.me/a/wjyeYBjZ?poolId=12090

The application hash, in this case is: wjyeYBjZ

📘

The Frame embedded player: a simple alternative to the FrameApp API

Note that the preview page mentioned above shows how the Frame embedded player works. This is one of the simplest and fastest ways to integrate an app into a web page, but it is not as flexible as the FrameApp API covered here. If you'd like to learn more about the embedded player see this article.

Once you have your application hash, you have what you need to initialize FrameApp. The rest of the fields shown below are optional.

/**
 * Class constructor.
 * @param {Object} config
 *  @property {String}  hash                App hash from app share page.
 *  @property {String}  fileName            Optional. URL to a file to open on launch.
 *  @property {String}  userData            Optional. A string containing user data. It is URI encoded automatically.
 *  @property {Boolean} showErrors          Optional. Flag used to prevent player from showing errors
 *  @property {Boolean} hidePlayerFooter    Optional. Flag used to hide footer of the player
 *  @property {Number}  headerHeight        Optional. Inserts header above player for custom elements.
 *                                          You can place elements in the header by using id=’mf2-header’.
 *  @property {Number}  poolId              Optional. ID of a pool.
 *  @property {Boolean} autostart           Optional. Flag used to start a session when page is loaded.
 *  @property {String}  token               Optional. A Frame authentication token
 */
FrameApp = function(config) { ... }
var frameApp = new frameApi.FrameApp({
    hash: 'application hash from app share page',                       // required
    // fileName: URL to a file to open on launch                        // optional
    // userData: A string containing user data,                         //  optional
    // showErrors: Flag used to prevent player from showing errors      // optional
    // hidePlayerFooter: Flag used to hide footer of the player         // optional
    // headerHeight: Inserts header above player for custom elements.   // optional
    // You can place elements in the header by using id=’mf2-header’ 
    // poolId: ID of a desired pool                                     // optional
    // autostart: Flag used to start a session when page is loaded      // optional
    // token: A Frame authentication token                              // optional
});

📘

Pool IDs

Note that the optional Pool ID mentioned above is also available from the URL used to get the application hash. In the URL example above, the Pool ID is: 12090. The Pool ID specifies from which pool of production instances a session should be started. You can change the pool being used from the Frame Dashboard: go to the Settings page and click on the Production tab. There you'll see a drop down for selecting the default production pool (see below).

Events

During the lifetime of a FrameApp instance, various events will be triggered so that the parent page is informed about FrameApp changes. If you want to register a handler for a specific event, you should use the bind function:

/**
 * Binds a handler function to a particular event.
 * @param  {String} eventName
 * Name of the event to which we want to register the handler.
 * @param  {Function} handler
 * Handler function which will be invoked once the event is triggered.
 *  @param {Object} event   Optional. 
 */
FrameApp.prototype.bind = function(eventName, handler) { ... }
frameApp.bind("someEventName", function(event) {
    console.log("Event has been thrown: " + event.code);
});

Names of all events which can be triggered by a FrameApp instance are available as constants with the EVENT prefix on the FrameApp class. Here is list of all of them:

Event Name Constant

Event Explanation

FrameApp.EVENT_READY

Triggered after construction, once a component has been initialized. You must not invoke any of the methods on FrameApp until this event is triggered, otherwise an error will be thrown.

FrameApp.EVENT_ERROR

Triggered when an error occurs during FrameApp execution. You should register a callback with a single parameter. This parameter will contain additional information.

FrameApp.EVENT_LOADING_DONE

Triggered once the player has been successfully loaded and a session successfully started.

FrameApp.EVENT_TERMINAL_SHOWN

Triggered when the first frame is decoded inside the player.

FrameApp.EVENT_CLOSED

Triggered once the player is closed, either by user action or some method call.

FrameApp.EVENT_MINIMIZED

This event is triggered whenever an application is closed and the session is still alive, or when the .disconnect() method or disconnect option is selected while a session is still alive.

FrameApp.EVENT_BROADCAST_SESSION_ID

Triggered once the player obtains the session ID. Use this event to get the session ID and, for example, to store it in a cookie.

FrameApp.EVENT_BROADCAST_SHARE_URL

Triggered once the sharing of a current session is possible. Use this URL to access the same session from another computer.

FrameApp.EVENT_OPEN_URL

Triggered when a user clicks on the http/https link inside the hosted application.

Simple example on how to register a handler function for an error event:

frameApp.bind(FrameApp.EVENT_ERROR, function(errorEvent) {
  console.log("Something went wrong: " + errorEvent.toString();
});

Note: Please make sure that you do not invoke any methods on the FrameApp instance until FrameApp.EVENT_READY is triggered (as mentioned above). You can check the example page at the end of this document for more info.

Public methods on FrameApp instance

startSession(config)
The method starts a user session and brings up the Frame Terminal. For example you could invoke it once a user clicks a button to try out your product.

/**
 * Starts a terminal session.
 * @param  {Object} config                Optional.
 *
 *  @property  {Boolean} connectOnStart         Optional.
 *  If true or undefined, a session will start and the terminal will be shown.
 *  If false, a session will start, but the terminal will not be shown
 *  (minimized).
 *
 *  @property  {Boolean} waitForInstance        Optional.
 *  If true and there is no available instance to accept the session, it will
 *  wait for the new instance to start and connect to it. If false or undefined,  
 *  and there is no active instance, the result promise will be rejected. If 
 *  there is an available instance, the session will start normally. If no instance
 *  is available within 360 seconds, a timeout will occur. If the timeout occurs, 
 *  a new startSession() request is required.
 *
 * @return {Promise}
 * Resolved once the session has been started, or rejected if an error occurred.
 */
FrameApp.prototype.startSession = function(config) { ... }
frameApp.startSession()
.then(function() {
  console.log("Session was successfully started");
})
.catch(function(error) {
  console.error("Something went wrong: " + error.toString());
});

closeSession():
Closes the currently active session and releases a server instance, making it available for another user.

/**
 * Closes currently active session.
 */
FrameApp.prototype.closeSession = function() { ... }
try {
  frameApp.closeSession();
  console.log("Session closed");
} catch (err) {
  console.log("Something went wrong: " + err.toString());
}

resumeSession(sessionId, config):
Gives you the functionality to resume a session from another page or after a page refresh using the sessionId you previously stored (from example in a Cookie).

/**
 * Resumes an existing session.
 * @param  {String} sessionId   ID of a session to be resumed.
 * @param  {Object} options         Optional.
 *  @property  {Boolean} connectOnStart         Optional.
 *  If true or undefined, session will be resumed and terminal will be shown.
 *  If false, session will be resumed, but terminal will not be shown 
 *  (minimized).
 *
 * @return {Promise}
 * Resolved once session has been started, or rejected if an error occured.
 */
FrameApp.prototype.resumeSession = function(sessionId, config) { ... }
frameApp.resumeSession("abcdefg")
.then(function() {
  console.log("The session is resumed");
})
.catch(function(err) {
  console.log("Something went wrong: " + err.toString());
});

disconnect():
Disconnects a user from the current session and hides the terminal, however the terminal and the session will remain active in the background.

/**
 * Disconnects user from session (session remains live).
 */
FrameApp.prototype.disconnect = function() { ... }
try {
  frameApp.disconnect();
  console.log("Disconnected from session");
} catch (err) {
  console.log("Something went wrong: " + err.toString());
}

connect():
Connects a user to a terminal which is running in the background with an active session. You can invoke it after disconnect or after startSession / resumeSession which were called with connectOnStart equal to false.

/**
 * If there is an active session, the terminal will connect to it, 
 * otherwise it will start a new session.
 */
FrameApp.prototype.connect = function() { ... }
try {
  frameApp.connect();
  console.log("Connected to session");
} catch (err) {
  console.log("Something went wrong: " + err.toString());
}

connectNewApplication(config):
Works similar like connect method, just when connection is established it starts an application from the provided path and with optional arguments.

/**
 * Connects and starts an application.
 * @param {Object} config
 *  @property {String} path       Path to the application.
 *  @property {String[]} args     Array of optional command line arguments.
 */
FrameApp.prototype.connectNewApplication = function(config) { ... }
try {
  frameApp.connectNewApplication({
    path: "C:\\Windows\\system32\\notepad.exe",
    args: ["C:\\someTextFile.txt"],
  });
  console.log("Connected to session");
} catch (err) {
  console.log("Something went wrong: " + err.toString());
}

Public service methods on FrameApp instance

There are a few service methods available on the FrameApp instance, which you can use to interact with the FrameApp API.

getCapacityInfoAsync():
Returns capacity status for the current app.

/**
 * Gets capacitiy info from server.
 * @return {Promise<Object>}
 * Resolved once capacity info object has been received, or rejected if there was
 * a communication problem.
 *  @property {Number} available_instances  Number of available instances. 
 *  @property {Number} running_instances        Number of busy instances.
 *  @property {Number} coming_soon                  Num of instances which are starting.
 */
FrameApp.prototype.getCapacityInfoAsync = function() { ... }
frameApp.getCapacityInfoAsync()
.then(function(infoObject){
  console.log("# available instances: " + infoObject.available_instances);
  console.log("# running instances: " + infoObject.running_instances);
  console.log("# coming soon: " + infoObject.coming_soon);
})
.catch(function(err) {
  console.log("Communication problem: " + err.toString());
});

flagCurrentSessionAsProblematicAsync(description):
Using this method, you can flag the current session as problematic, so that our engineers can investigate it upon submitting a support ticket (the flag makes the session easier to find, but please also submit the precise start time of the session in question with your support ticket).

/**
 * Flags the current session as problematic.
 * @param  {String} description Optional. Description about the problem. 
 * @return {Promise}
 * Promise which will be resolved once session is marked as problematic, or  
 * rejected if there was a problem in communication with our servers.
 */
FrameApp.prototype.flagCurrentSessionAsProblematicAsync = function(description) { ... }
frameApp.flagCurrentSessionAsProblematicAsync("Slow response")
.then(function() {
  console.log("Frame is informed about problematic session");
})
.catch(function(err) {
  console.log("Communication problem: " + err.toString());
});

flagSessionAsProblematicAsync(sessionId, description):
This method is similar to flagCurrentSessionAsProblematicAsync, but you can specify any session id (for example one stored in a cookie).

/**
 * Flags given session as problematic.
 * @param  {String} sessionId
 * @param  {String} description Optional. Description about the problem. 
 * @return {Promise}
 * Promise which will be resolved once session is marked as problematic, or  
 * rejected if there was a problem in communication with our servers.
 */
 FrameApp.prototype.flagSessionAsProblematicAsync 
  = function(sessionId, description) { ... }
frameApp.flagSessionAsProblematicAsync("abcdefg", "Slow response")
.then(function() {
  console.log("Frame is informed about problematic session");
})
.catch(function(err) {
  console.log("Communication problem: " + err.toString());
});

Public properties on the FrameApp instance

You have two properties available on the instance which you can use to read data relevant to the current session. Note, values of these properties change during the lifetime of a FrameApp instance.

/**
 * ID of the current session.
 * @type {String}
 */
frameApp.sessionId

/**
 * Object containing various data about session
 * @type {Object}
 */
frameApp.sessionInfo

Error codes

During the lifetime of FrameApp errors can occur. Those errors will be returned to you either via triggering error events or by rejecting promises which are results of method calls. Most of them will have an error code, title and message. Based on the returned error code you can decide how to handle that particular situation.

We have provided a list of potential error codes via a nested errors namespace. There are two classes of error code constants: PlayerErrorCodes and PlatformErrorCodes.

frameApi.errors.PlayerErrorCodes:
These codes represent problems between the Frame Terminal and the backend virtual desktop. Here is a list of codes with explanations:

Code Constant

Explanation

PlayerErrorCodes.FIREWALL

A connection to the application could not be made.

PlayerErrorCodes.DISCONNECTED

Lost connection with Frame session

PlayerErrorCodes.INTRUSION

Security problems

frameApi.errors.PlatformErrorCodes:
Codes which represent problems on our platform. For example you might want to start a session and there are no available instances to handle it. Here is a list of the available codes:

Code Constant

Explanation

PlatformErrorCodes.ALL_FULL

All available instances are in use. You may see this, for instance, if Max is set to 2 and you try to start a 3rd session.

PlatformErrorCodes.SANDBOX_STARTING_UP

You tried to start a sandbox session, but the sandbox is turned off. When this code is returned, the sandbox will start automatically. This may take several minutes.

PlatformErrorCodes.STARTING_NEW_SRV_ELAS

You tried to start a production session, but we had to start a new instance for it. After a few minutes the instance will be ready. THIS IS NOT AN ERROR AND YOU SHOULD NOT RETRY UNLESS YOU SEE SOME OTHER PROBLEM.

PlatformErrorCodes.ZERO_PRODUCTION_INSTANCES

You have set your production capacity to 0.

PlatformErrorCodes.TIME_BETWEEN_SESSIONS

You are trying to start a session on an instance on which a session has just been closed.

PlatformErrorCodes.MAX_SESSION_LIMIT

You have reached the maximum number of simultaneous sessions configured for this account. (See the "Max" setting on the "Production" tab under "Settings" in the Dashboard in your Frame account).

Here is a short example of how you can use error codes to handle a problematic situation. Let's say you want to start a session, but the maximum session limit has been reached, so you inform your user that they should try later.

frameApp.startSession()
.catch(function(error) {
  if (error.code === frameApi.errors.PlatformErrorCodes.MAX_SESSION_LIMIT) {
    alert("We have reached maximum capacity, please try again later.");
  }
});

Network testing

SpeedTest

Sometimes you are not sure if your users have optimal network conditions, and as a consequence of that, you do not know if they'll have a good user experience. In order to solve this problem, we are offering SpeedTest class from our network namespace. You should use it to test client's bandwidth and latency before even instantiating the FrameApp. In that way, if network is bad, you can show a popup and inform a user that he is not able to start a session, without any interaction with FrameApp.

Amazon and Azure regions each have their own codes.

frameApi.network.AWSRegionCodes
For both bandwidth and latency tests, you'll have to provide an AWS region code as an input parameter for a testing method. That code represents a region in which your app is stored and executed. In order to make this process straight forward and simple, we created a small class AWSRegionCodes which contains constants for all Amazon regions.

You should always check frameApi.network.AWSRegionCodes for any new additions, but at present it contains:

  • AUSTRALIA
  • BRAZIL
  • CALIFORNIA
  • ENGLAND
  • GERMANY
  • INDIA
  • IRELAND
  • JAPAN
  • OHIO
  • OREGON
  • QUEBEC
  • SEOUL
  • SINGAPORE
  • VIRGINIA

frameApi.network.AzureRegionCodes
Azure regions are stored in AzureRegionCodes but otherwise work the same. The currently supported Azure regions are:

  • CALIFORNIA
  • IOWA
  • NORTH_CENTRAL_US
  • NORTH_EUROPE
  • SOUTH_CENTRAL_US
  • SOUTH_EAST_ASIA
  • VIRGINIA
  • WASHINGTON
  • WEST_EUROPE

frameApi.network.SpeedTest
SpeedTest offers two static methods, one for measuring bandwidth and the other for latency. Both of those methods are asynchronous and both of them are returning a Promise object.

SpeedTest.getBandwidthAsync(awsRegionCode):

or

SpeedTest.getBandwidthAsync(azureRegionCode):

/**
 * Measures bandwidth towards specific region.
 * @param  {String} awsRegionCode
 * @return {Promise<Number>}  Promise which will be completed upon successful
 *                                                      measurment, or rejected if something went wrong.
 */
SpeedTest.getBandwidthAsync = function(awsRegionCode) { ... }
var SpeedTest = frameApi.network.SpeedTest;
var AWSRegionCodes = frameApi.network.AWSRegionCodes;

SpeedTest.getBandwidthAsync(AWSRegionCodes.CALIFORNIA)
.then(function(bandwidth) {
  console.log("Client bandwidth:" + bandwidth);
})
.catch(function(error) {
  console.log("Something went wrong");
});

SpeedTest.getLatencyAsync(awsRegionCode):

/**
 * Measures latency towards specific region.
 * @param  {String} awsRegionCode
 * @return {Promise<Number>}  Promise which will be completed upon successful
 *                                                      measurment, or rejected if something went wrong.
 */
SpeedTest.getLatencyAsync = function(awsRegionCode) { ... }
var SpeedTest = frameApi.network.SpeedTest;
var AWSRegionCodes = frameApi.network.AWSRegionCodes;

SpeedTest.getLatencyAsync(AWSRegionCodes.CALIFORNIA)
.then(function(latency) {
  console.log("Client latency:" + latency);
})
.catch(function(error) {
  console.log("Something went wrong");
});

RegionSelector

For the best user experience, it is a good idea to host your application close to the user. If your users are not all in the same city or region, you may want to also host your application in more than one region. But how will you determine which region is closest to which user? One way is to ask the user to select the nearest major city. This has the advantage of simplicity and quick response, but sometimes, asking the user is not an option. To help with this scenario, we have created a class which knows how to automatically select the AWS region that seems to perform best for a particular user's current location. We call this class RegionSelector.

frameApi.network.RegionSelector
RegionSelector is built on top of the SpeedTest class and it uses it 'under the hood' to select a region. It will perform several latency measurements toward a given set of AWS regions, and based on those results it will return the region with the best performance. The region returned by RegionSelector may not always be the geographically closest region due to differences in network connectivity and activity. As an example, a user in Mumbai, may see better results from Tokyo than from Singapore due the location and capacity of undersea cables and gateway servers between these locations (network topology).

RegionSelector.getBestRegionCodeAsync(awsRegionCodes)

/**
 * Finds an AWS region to which latency is the lowest.
 * @param  {String[]}         awsRegionCodes
 * @return {Promise<String>}  Promise which will be resolved once the best region 
 *                            is selected, or rejected if an error occurs.
 */
getBestRegionCodeAsync: function(awsRegionCodes) {
var AWSRegionCodes = frameApi.network.AWSRegionCodes;
var RegionSelector = frameApi.network.RegionSelector;

var testRegionCodes = [ AWSRegionCodes.CALIFORNIA, AWSRegionCodes.IRELAND ];
RegionSelector.getBestRegionCodeAsync(testRegionCodes)
.then(function(bestRegion) {
  console.info("Best region is: " + bestRegion);
})
.catch(function(err) {
  console.info("An error occured: " + err);
});

RegionSelector.getBestHashRegionPairAsync(hashRegionPairs)
In most cases, after finding the best region, the next thing you will want to know is which application hash to use to provider your user with an application running in that region. For that purpose, you would probably create some kind of mapping between an application hash and its region code. In order to simplify this frequent scenario, we have created the method getBestHashRegionPairAsync .

/**
 * Finds a hash-region pair to which latency is the lowest.
 * @param  {HashRegionPair[]}         hashRegionPairs
 * @return {Promise<HashRegionPair>}  Promise which will be resolved once the 
 *                                    best pair is selected, or rejected if an 
 *                                    error occurs.
 */
getBestHashRegionPairAsync: function(hashRegionPairs) {
var AWSRegionCodes = frameApi.network.AWSRegionCodes;
var HashRegionPair = frameApi.network.HashRegionPair;
var RegionSelector = frameApi.network.RegionSelector;

var caPair = new HashRegionPair("abc" /* hash */, AWSRegionCodes.CALIFORNIA);
var irPair = new HashRegionPair("xyz", AWSRegionCodes.IRELAND);
var testPairs = [ caPair, irPair ];

RegionSelector.getBestHashRegionPairAsync(testPairs)
.then(function(bestPair) {
  console.info("Best hash is: " + bestPair.hash);
  console.info("from region: " + bestPair.awsRegionCode);
})
.catch(function(err) {
  console.info("An error occured: " + err);
});

Important note: Other network activity, for example loading of images, will affect SpeedTest and so effect RegionSelector network measurements. This can lead to bad bandwidth / latency results, although the actual capabilities of the network may be good enough. The best approach to avoid this problem, is to wait for the window load event, before performing any tests. After that event has been fired, you can be sure that loading of all assets on your page is completed and that you can safely run SpeedTest or RegionSelector.

WebSocketSupport

Web sockets allow a browser and a server to keep a connection open and talk back and forth. Frame Terminal uses web sockets to provide a stable connection for video streaming. Some users may be on a network which does not support web socket connections. If a user tries to connect on a network that won't support web sockets, an instance will start, but the browser will not be able to connect to it. After awhile, the session will timeout. This is wasteful of resources and can be frustrating for a user. The WebSocketSupport class is designed to give you the ability to test a client's web socket connection without even starting a session. You can then provide a message explaining the issue to the user and saving them the frustration of a failed connection.

frameApi.network.WebSocketSupportStatus
This is a small class, which contains constants that can be returned as a result of web socket connection testing. For most purposes, UNABLE_TO_CHECK should be treated the same as UNAVAILABLE, but you may want to check them separately anyway for debugging.

WebSocketSupportStatus.AVAILABLE

WebSocketSupportStatus.UNAVAILABLE

WebSocketSupportStatus.UNABLE_TO_CHECK

frameApi.network.WebSocketSupport
The WebSocketSupport class provides all everything you need to determine if a client's browser / network supports web socket connection or not. It has just a single method.

WebSocketSupport.getStatusAsync()

/**
 * Checks if a client's browser / network supports web socket connection.
 * @return {Promise<Number>}  Promise which will be resolved with a constant
 *                                                      from WebSocketSupportStatus.
 */
WebSocketSupport.prototype.getStatusAsync = function() { ... }

Here is a simple example of how to use this method.

var WebSocketSupport = frameApi.network.WebSocketSupport;
var WebSocketSupportStatus = frameApi.network.WebSocketSupportStatus;

WebSocketSupport.getStatusAsync()
.then(function(status) {
  switch (status) {
    case WebSocketSupportStatus.AVAILABLE:
      console.log("Supported!");
      break;
    case WebSocketSupportStatus.UNAVAILABLE:
      console.log("Unsupported!");
      break;
    case WebSocketSupportStatus.UNABLE_TO_CHECK:
      console.log("Couldn't determine.");
      break;
  }
});

Note: In order for the example to work, it should be served from a web server not from a local file.

Example Page

This is an example of what's needed to start a session and handle messages and errors.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta property="og:description" content="Launch an App With FrameApp API.">
    <title>FrameApp Auto Launcher</title>
    <script src="//app.fra.me/embed/frame_app.js"></script>
    <script src="//code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha256-/SIrNqv8h6QGKDuNoLGA4iret+kyesCkHGzVUUV0shc=" crossorigin="anonymous"></script>
    <style>
    body { color: gray; background: black; font-size: 3em; text-align: center; margin: 20%;};
    </style>
    </head>
    <body>
    
    <p id="message">Loading...</p>

    <script>
    
function getParameters(search) {
    return search.slice(1).split('&').reduce(function(p,c,i,a) {p[c.split('=')[0]] = c.split('=')[1]; return p;}, {});
}

function userMessage(text) {
    console.log(text);
    $('#message').html(text);
}

var urlParameters = getParameters(location.search);
if (urlParameters['hash']) {
    var hash = urlParameters['hash'];
    var token = urlParameters['token'];
    var userData = urlParameters['userData'];
    var frameApp = new FrameApp({
        hash: hash,
        userData: userData,
        token: token,
    });

    frameApp.bind(FrameApp.EVENT_ERROR, function(error) {
        if (error.code == frameApi.errors.PlatformErrorCodes.STARTING_NEW_SRV_ELAS) {
            userMessage("Starting an instance. This may take a few minutes.");
        } else if (undefined === error.code) {
            userMessage("Application not found for: " + hash);
        } else {
            userMessage("FrameApp.EVENT_ERROR: code:" + error.code + " message: " + error.message);
        }
    });

    frameApp.bind(FrameApp.EVENT_READY, function() {
        userMessage("Calling start session. This may take up to 3 minutes if no instance is running.");
        frameApp.startSession({
            connectOnStart: true,
            waitForInstance: true,
        }).then(function(){
            userMessage("entered then()");
        }).catch(function(error) {
            if (error.code == 57) {
                userMessage("Starting an instance. This may take a few minutes.");
                $('#message').html("Starting an instance. This may take a few minutes.");
            } else {
                userMessage("entered catch() with error: code: " + error.code + " message: " + error.message);
            }
        });
        frameApp.bind(FrameApp.EVENT_LOADING_DONE, function() {
            userMessage("FrameApp.EVENT_LOADING_DONE");
        });

        frameApp.bind(FrameApp.EVENT_BROADCAST_SESSION_ID, function() {
            userMessage("FrameApp.EVENT_BROADCAST_SESSION_ID");
        });

        frameApp.bind(FrameApp.EVENT_TERMINAL_SHOWN, function() {
            userMessage("FrameApp.EVENT_TERMINAL_SHOWN");
        });

        frameApp.bind(FrameApp.EVENT_CLOSED, function() {
            userMessage("You may now close this tab.");
            close(); // This won't do anything in most cases, but just in case.
        });
        
    });
        
} else {
    userMessage("Application hash is required.<p>Try<p>index.html?hash=abc123def");
}
      </script>
    </body>
</html>

More Complex Example page

This is an example page which shows how all currently available features of FrameApp are used. Download it, edit the file to set your app hash as the value of the eahash field and play around.

<html lang="en">
  <head>
    <title>FRAME | Demo</title>
  </head>

    <body>

    App hash: <input type="text" id="eahash" value="PUT YOUR HASH HERE!">
    <input type="button" id="start_app" value="Start app">
    <input type="button" id="start_app_no_player" value="Start app no player">
    <input type="button" id="connect" value="Connect">
    <input type="button" id="resume" value="Resume">
    <input type="button" id="resume_no_app_player" value="Resume no app player">
    <input type="button" id="save_session_id" value="Save session id">

    <div id="capacity_info" style="float:right; position:absolute; padding: 10px; margin-top: 2em; background: rgba(150, 150, 150, 0.5)"></div>
      
    <!-- JQuery used only for test page, not required for our API -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <!-- Simple Cookie library used only for test page, not required for our API -->
    <script src="https://rawgit.com/ScottHamper/Cookies/1.2.2/dist/cookies.min.js"></script>
    <!-- Load Frame API -->
    <script src="https://app.fra.me/embed/frame_app.js"></script>

        <script type="text/javascript">

      var frameApp = new frameApi.FrameApp({
          hash: $('#eahash').val()
      });

      frameApp.bind(FrameApp.EVENT_ERROR, function(e) {
          alert(e.message);
      });

      frameApp.bind(FrameApp.EVENT_LOADING_STARTED, function() {
        console.info('Loading started');
      });

      frameApp.bind(FrameApp.EVENT_LOADING_DONE, function() {
        console.info('Loading done.');
      });

      frameApp.bind(FrameApp.EVENT_CLOSED, function() {
        console.info('Session closed.');
      });

      frameApp.bind(FrameApp.EVENT_BROADCAST_SESSION_ID, function(sessionId) {
        console.info('Broadcast session id: ' + sessionId);
      });

      frameApp.bind(FrameApp.EVENT_BROADCAST_SHARE_URL, function(url) {
        console.info('Broadcast share url: ' + url);
      });

      frameApp.bind(FrameApp.EVENT_TERMINAL_SHOWN, function() {
        console.info('Terminal shown!');
      });

      frameApp.bind(FrameApp.EVENT_OPEN_URL, function(url) {
        console.info("Open url: " + url);
      });

      $(document).ready(function() {

        frameApp.bind(FrameApp.EVENT_READY, function() {

          $('#eahash').on('blur', function() {
            initializeFrameApp();
          });

          $("#start_app").click(function() {
            frameApp.startSession();
          });

          $('#start_app_no_player').click(function() {
            frameApp.startSession({
              connectOnStart: false,
            });
          });

          $('#connect').click(function() {
              frameApp.connect();
          });

          $('#resume').click(function() {
            frameApp.resumeSession(Cookies.get("sessionId"));
          });

          $('#resume_no_app_player').click(function() {
            var config = {
              connectOnStart: false
            };
            frameApp.resumeSession(Cookies.get('sessionId'), config);
          });

          $('#save_session_id').click(function() {
            Cookies.set('sessionId', frameApp.sessionId);
          });

          // Called each 3 seconds to return basic session parameters
          setInterval(function() {
            console.log("Getting session id: " + frameApp.sessionId);

            frameApp.getCapacityInfoAsync().then(function(capacityInfo) {
              console.log("Capacity info received:");
              console.log(capacityInfo);

              var props = ['available_instances', 'running_instances', 'coming_soon'];
              var stats = [];
              for(var i = 0; i < props.length; i++) {
                var property = props[i];
                stats.push(property + ': ' + capacityInfo[property]);
              }

              $('#capacity_info').html(stats.join('<br>'));
            });
          }, 3000);
        });
      });

    </script>
  </body>
</html>

Updated 2 years ago

Application API


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.