Giter Site home page Giter Site logo

ifrau's Introduction

ifrau

NPM version Dependency Status

Short for iframe-free-range-app-utilities, ifrau makes it easy to communicate from within an IFRAME cross-domain to a parent host. It wraps postMessage in an easy-to-use, promise-based API.

Installation

Install from NPM:

npm install ifrau

Host and Client

ifrau exposes three classes:

  • Host: Created once for each FRA by the AppLoader within Brightspace. It will build an IFRAME element, point it at the FRA endpoint, and wait for the FRA to load and connect. It can then respond to events and requests from the FRA.
  • Client: Created by the free-range app, it will establish communication with the host and can then be used to send/receive requests and events.
  • SlimClient: A lighter-weight client created by components within free-range apps that may need to send/receive requests and events with the host. This assumes the parent app has already created a full client to manage syncing options.

To create a Host:

import { Host } from 'ifrau/host.js';

function parentProvider() {
    return document.getElementById('myParentId');
}

var host = new Host(parentProvider, endpoint, options)
host.connect().then(function() {
	console.log('connected to client');
});

Parameters:

  • parentProvider: function which will return the parent HTML element into which to insert the IFRAME
  • endpoint: URL of the free-range app endpoint (the src of the IFRAME)
  • options
  • debug: whether to enable console debugging, false by default
  • resizeFrame: whether the IFRAME should automatically resize to fit its content, true by default
  • syncFont: whether to allow client to automatically sync its font size with the host, false by default
  • syncLang: whether to allow client to automatically sync its language, timezone, internationalization, and OSLO settings with the host, false by default
  • syncPageTitle: whether the page title (in the <head> element) should be kept in sync automatically with the title of the FRA, false by default
  • syncCssVariable: whether css variables (in the <head> element) should be kept in sync automatically with the css variables of the FRA, false by default
  • height: sets the iframe to a certain height, also disables automatic resizing
  • id: sets the id of the iframe
  • allowFullScreen: whether the frame can be placed into full screen mode, false by default
  • allowMicrophone: whether the frame will allow access to the microphone, false by default
  • allowCamera: whether the frame will allow access to the camera, false by default
  • allowScreenCapture: whether the frame will allow access to record the screen, false by default
  • allowEncryptedMedia: whether the frame will allow access to encrypted media, false by default
  • allowAutoplay: whether the frame will allow access to autoplay, false by default
  • allowClipboard: whether the frame will allow access to the clipboard (copy/paste), false by default

Creating a Client is even simpler:

import { Client } from 'ifrau/client.js';

var client = new Client(options);
client
	.connect()
	.then(function() {
		console.log('connected to host!');
	});

Parameters:

  • options
  • debug: whether to enable console debugging, false by default
  • resizeFrame: whether this Client should participate in automatic resizing. true by default
  • syncFont: whether the font size should be automatically set to match the host page, false by default
  • syncLang: whether the page's language tag should be automatically set to match the host page, true by default
  • syncTitle: whether the host page's title and IFRAME element title should be kept in sync with the FRA's title, true by default
  • resizerOptions: pass iframe-resizer client options through to the iframe resizer client

Creating a SlimClient can be done in the same way:

import { SlimClient } from 'ifrau/client/slim.js';

var slimClient = new SlimClient(options);
slimClient
	.connect()
	.then(function() {
		console.log('connected to host!');
	});

Parameters:

  • options
  • debug: whether to enable console debugging, false by default

Events

Events are the simplest way to communicate between the host and client. They're an asynchronous "fire and forget" mechanism.

Let's say the host wanted a way to notify all clients that the user's session had expired. This example adds a handler for the sessionExpired event in the client:

var client = new Client();
client.onEvent('sessionExpired', function(who, when) {
	console.log('session expired', who, when);
}).connect().then(function() {
	console.log('connected to host');
});

The handler should be added before the call to connect(), otherwise it could miss events.

From the host's perspective, events must be triggered after a connection is established:

var host = new Host(...);
host.connect().then(function() {
	host.sendEvent('sessionExpired', 'user123', new Date());
});

Although this example sends an event from the host to the client, both parties can register for and send events.

Requests

Requests are similar to events, but instead of "fire and forget", they use promises to pass along a response to the requester.

Just like events, request handlers should be set up before connecting:

var host = new Host(...);
host.onRequest('sayMyName', 'Heisenberg')
	.connect().then(function() {
		console.log('connected to client!');
	});

Request handlers can either be a static value like in the "sayMyName" example above, or a function which can take optional arguments and returns a value:

host.onRequest('addThesePlease', function(p1, p2) {
		return p1 + p2;
	});

Finally, if the result isn't available immediately, the function can return a promise:

host.onRequest('addSlower', function(a, b) {
	return new Promise(function(resolve, reject) {
		setTimeout(function() {
			resolve(a + b);
		}, 1000);
	});
});

On the other side, making request()s uses a promise-based API:

var client = new Client();
client.connect()
    .then(function() {
	    return client.request('addThesePlease', 2, 3);
    }).then(function(val) {
		console.log(val); // 5
	});

Services

Building on the concept of requests, services can be registered by both the host and client. Services provide a way to wrap a set of methods in an API which can be versioned.

Again, services must be registered before connecting:

var host = new Host(...);
host.registerService('calculator', '1.0', {
    add: function(a, b) {
        return a + b;
    },
    subtract: function(a, b) {
        return a - b;
    }
});

To support breaking changes to your APIs while maintaining backwards compatibility, multiple versions of a service may be registered by passing in different values for the version.

Note: service methods become static, so any reference to this inside your methods will refer to the method itself.

Calling service APIs after connecting is simple and promise-based:

var client = new Client();
client.connect()
    .then(function() {
        return client.getService('calculator', '1.0');
    }).then(function(calculator) {
        return calculator.add(1, 5);
    }).then(function(result) {
        console.log(result); // 6
    });

Plugins

ifrau hosts and clients can be extended with plugins:

var myPlugin require('ifrau-someplugin');
var client = new Client()
	.use(myPlugin);

Please prefix plugin names with ifrau-* so that it can be easily found.

Chaining

When setting up your event, request handlers and services on the host or client, they can be chained:

var client = new Client();
client.onEvent('jump', function() {
	// handle "jump" event
}).onEvent('skip', function() {
	// handle "skip" event
}).onRequest('time', new Date())
.onRequest('sayMyName', function() {
	return 'Heisenberg';
}).registerService('myService', '1.2', {...});

Contributing

Contributions are welcome, please submit a pull request!

Code Style

This repository is configured with EditorConfig rules and contributions should make use of them.

Versioning and Releasing

This repo is configured to use semantic-release. Commits prefixed with fix: and feat: will trigger patch and minor releases when merged to main.

To learn how to create major releases and release from maintenance branches, refer to the semantic-release GitHub Action documentation.

ifrau's People

Contributors

ciss1995 avatar codebaboon avatar dbatiste avatar dependabot[bot] avatar devpow112 avatar dlockhart avatar edwardg8 avatar evanlemieux avatar grant-cleary avatar ianmclean2011 avatar j3parker avatar joshuaklassen avatar jstefaniuk-d2l avatar kiefer-co-d2l avatar maboelsoudd2l avatar maggiesazio avatar margaree avatar mdgbayly avatar mikepaulsond2l avatar mpharoah-d2l avatar njostonehouse avatar omsmith avatar prajapac avatar semantic-release-bot avatar stevenao avatar svanherk avatar tylergee avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ifrau's Issues

client connect() shouldn't resolve until plugins are "ready"

Certain plugins need to request data from the host and perform some actions before they're deemed "good to go". For example, the font-sync plugin needs to request the user's font family/size from the host and apply that to the body's style. Only then should applications attempt to bootstrap themselves, else they might end up with a FOUC situation.

Possible timing issues around connecting

If Close() is fired before Connect() completes it may be possible to end up with a dangling open connection. Probably need isConnecting and/or isOpening states to handle situations such as this, along with deferred execution (ie if Close() is called while isConnecting then defer the close action until connect() has completed).

connect should resolve with host/client

When calls to connect() resolve, they should be resolved with either the host or client. That would enable:

new Client()
  .connect().then(function(client) {
    // interact with client
  });

iframe resizer height calculation method 'max' causing jittery behaviour

We are seeing jittery behaviour across multiple browsers with the iframe resizer height calculations.
It seems to be caused by the use of the 'max' height calculation method. If we switch back to the default 'bodyOffset' the problem goes away.

The method was changed to 'max' a few months ago in response to another issue, although I don't recall the full details of why (maybe for IE10 issues which we don't even support now?)

Our problem seems to be caused by crazy resizing cycles that happen in response to some css transitions that are causing the iframe resizer to temporarily switch the height calc method, send a 'reset' event and then switch back to the set height calc method. I don't fully understand why its happening, but 'max' seems sub-optimal.

I'd like to submit PRs so we can at least configure the method it uses from the FRA control for the particular app in the LMS.

I was thinking of adding a new attribute to the IFramePageView class in the app loader similar to the existing 'height' property to allow the height calculation method to be passed down the iframe resizer host plugin.

Thoughts?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.