https://github.com/jwplayer/ihop
https://github.com/jwplayer/ihop
Last synced: over 1 year ago
JSON representation
- Host: GitHub
- URL: https://github.com/jwplayer/ihop
- Owner: jwplayer
- License: isc
- Created: 2022-03-04T02:41:53.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2022-04-04T16:34:06.000Z (over 4 years ago)
- Last Synced: 2025-02-15T20:49:00.647Z (over 1 year ago)
- Language: JavaScript
- Size: 321 KB
- Stars: 0
- Watchers: 4
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# IHop: *The Hopping Library*
IHop is a utility to allow for objects and functions from one iframe context to be usable from many connected contexts - even cross-origin - as though those objects were local.
The term "iframe hopping" refers to the fact that each participant in the "network" (tree, really) of window objects only communicates only with it's immediate parent and children. Sending messages to window contexts beyond the immediate family must be done by routing through each intermediate node(s) (ie. hopping).
At its core, IHop is three things:
1. A proxy engine that can generate proxies for complex objects (even DOM elements!)
2. A network-agnostic routing fabric designed for hierarchical topologies
3. A globally-coherent state built on top of the routing fabric
### Table of Contents
* [Get It](#get-it)
* [Quick Usage](#usage)
* [API](#api)
* [Advanced](#advanced)
* [Architecture Diagram](#architecture)
* [Caveats](#caveats)
## Get It
1. Clone the repository
2. `npm install`
3. `npm run build`
You will then have `ihop.js` and `ihop.min.js` in the `./dist` directory. They are both UMD modules and therefore should work with the most popular module systems or as a script tag.
**NOTE**: The non-minified file does very noisy `console.debug` logging of every message that goes through the `Router` and should only be used for development.
## Quick Usage
Step1 - Include IHop on your page
```html
```
Step 2 - Initialize ihop
```js
const ihop = new IHop('pick_a_namespace');
```
Step 3a - Either export an object...
```js
ihop.export('test', { foo: 'bar' });
```
Step 3b - ...or a function
```js
ihop.export('func', () => 'Hello world!');
```
Step 4 - Use what you exported from another iframe or the parent context
```html
// This is assuming that we are in another iframe:
Promise.all([
ihop.import('pick_a_namespace.test'),
ihop.import('pick_a_namespace.func')
]).then(async ([test, func]) => {
console.log(await test.foo); //=> bar
console.log(await func()); //=> Hello world!
});
```
Congratulations! You’ve just successfully used IHop to export an object and a function across those pesky iframe barriers!
## API
* [new IHop(nameSpace, options)](#IHop.constructor)
* _methods_
* [.export(name, object)](#IHop.export) ⇒ void
* [.import(path)](#IHop.import) ⇒ Promise
* [.registerWorker(worker)](#IHop.registerWorker) ⇒ void
* _properties_
* [.tree](#IHop.tree)
### IHop(nameSpace, options) ⇒ instance
Construct the local IHop instance.
**Kind**: constructor of [IHop](#IHop)
| Param | Type | Description |
| --- | --- | --- |
| nameSpace | string | A globally available name-space to hold all of this context's exported objects. |
| options | object | Options to alter default behavior. |
| [options.model] | object | Container for Model-related options. |
| [options.model.forceRoot] | boolean | Set to true to stop this node from attempting to contact it's parent. |
| [options.network] | object | Container for Network-related options. |
| [options.network.allowedOrigins] | array<string> | A list of allowed origins for child nodes. Any messages received from origins not listed are immediately dropped. Leave empty to allow all origins. |
| [options.network.parentOrigin] | string | The allowed origin to use when communicating with the context's parent. |
| [options.network.parentWindow] | window | Override the default parent context. Mainly useful when initializing IHop in an Worker context. |
**Example**
```js
> const ihop = new IHop('myNameSpace');
```
### ihop.export(name, object) ⇒ void
Makes `object` available to every connected iframe via `name` within this iframe's namespace.
**Kind**: instance method of [IHop](#IHop)
| Param | Type | Description |
| --- | --- | --- |
| name | string | The name to export the object under in the current namespace. |
| object | object|function | The object or function to expose. |
**Example**
```js
> const ihop = new IHop('foo');
> const bar = {baz: 'hello!'};
// Make the 'bar' object available in other contexts under 'foo.bar'
> ihop.export('bar', baz);
```
### ihop.import(path) ⇒ Promise
Waits for a specific path to becomes available and then resolves the promise with the object or namespace at that path.
**Kind**: instance method of [IHop](#IHop)
| Param | Type | Description |
| --- | --- | --- |
| path | string | A path is one or more namespaces separated by a period (.) and optionally a final exported name. |
**Example**
```js
// An iframe with the namespace `A` is the root namespace and contains another iframe with the namespace `B`
// The iframe `B` exports an object named `foo`
> ihop.import('A.B.foo').then((foo) => {
// Do something with 'foo'
});
// Wait for more than one export
> Promise.all([
ihop.import('A.B.foo'),
ihop.import('A.bar')
]).then(([foo, bar]) => {
// Do something with 'foo' and 'bar'
});
```
### ihop.registerWorker(worker) ⇒ void
Register a web worker context. Workers are not able to automatically register themselves like iframes and must be explicitly linked in their parent context.
**Kind**: instance method of [IHop](#IHop)
| Param | Type | Description |
| --- | --- | --- |
| worker | Worker | An instance of a Web Worker. |
**Example**
```js
> const worker = new Worker('worker.js');
> ihop.registerWorker(worker);
```
### ihop.tree ⇒ object
Contains the exported namespace hierarchy.
**Kind**: instance property of [IHop](#IHop)
**Example**
```js
// An iframe with the namespace `A` contains another iframe with the namespace `B`
// The iframe `B` exports an object named `foo`
> ihop.import('A.B.foo').then(() => {
// We can also access foo via:
const foo = ihop.tree.A.B.foo;
});
```
## Advanced
IHop has support for some pretty advanced proxying. Not only can you export DOM nodes and manipulate them as though they were local, but you can also *treat functions as local* too!
This means that you can pass functions across the proxy as arguments and even return functions from other functions. The proxy engine handles all the fun stuff behind the scenes for you.
For example let's say that you export a function that returns a function from iframe A:
```html
const ihop = new IHop('A');
const compose = (fnA, fnB) => async (...args) => await fnA(await fnB(...args));
ihop.export('compose', compose);
```
NOTE: When we execute the fnA and fnB functions, we need to `await` - any function passed between contexts has it’s return value encapsulated in a promise.
Now in B, you want to use that function:
```html
const ihop = new IHop('B');
ihop.import('A.compose').then(async (compose) => {
const add = (a, b) => a + b;
const double = (n) => n * 2;
const sumAndDouble = await compose(double, add);
console.log(await sumAndDouble(3, 4));
});
```
And it just works!
## Architecture

Blue labels represent event types.
## Caveats
There are a few things to be aware of when using this library.
### Performance
Don't expect performing magic to be fast. This library should not be used for performance intensive operations. Even events that trigger more than a few times a second are not a good fit for cross-frame access.
### Synchronization
Exported objects are running in different threads and the library doesn't provide any synchronization primitives. Operations can happen out of order and nothing is atomic. It's best to avoid making changes to an object from more than one context.
### Proxying
There are obviously going to be places where the proxying breaks down but every attempt has been made to make it as transparent as possible.
Currently unsupported operations on proxies:
1. Both `getPrototypeOf`/`setPrototypeOf`
2. The `delete` operator
3. The `in` operator