npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

simple-xdm

v2.2.1

Published

A simple iframe XDM library that allows to register API on the host which can be invoked by scripts in the iframe. Tiny.

Downloads

1,066

Readme

Simple XDM

build-status

A simple library that allows to expose host API to an embedded iframe through the postMessage protocol.

Contributing

Please see CONTRIBUTING.md

Goals

  • No changes necessary in the iframe library when the host API changes.
  • No DOM modifications (plays well with React.js).
  • Works with different module loaders using UMD: AMD, Common.js and globals.
  • Supports modern browsers (ES5+, i.e. IE10 and upwards, Chrome and Firefox)

Usage

Build

npm install
gulp

This command generates the two minified libs for the host and the iframe:

  • dist/host.min.js
  • dist/iframe.min.js

Code Coverage

We use Istanbul for code coverage statistics.

To run tests and generate coverage results:

COVERAGE=true gulp karma-ci

Then point your browser at:

file:///<path to simple-xdm>/coverage/index.html

Define your API

Include /dist/host.min.js or import host from 'simple-xdm/host'; on the host side. See /example/product/product.html for an example. Then you can use host.defineModule to define your API (see /example/product/product.js):

host.defineModule('messages', {
    error: function(title, body, options) {
        // your code
    },
    info: function(title, body, options) {
        // your code
    },
    success: function(title, body, options) {
        // your code
    },
    warning: function(title, body, options) {
        // your code
    },
});

Code in the iframe can call this API using:

AP.require('messages', function(messages) {
    messages.error('Error', message)
});

You can also define globals:

host.defineGlobals({
   request: function(options, cb) {
       setTimeout(function() {
           cb({statusCode: 200, response: options});
       }, 10);
   }
});

Code in the iframe can call this API using:

AP.request({ ... }, function(result) {
    // do something
});

Use the API in the iframe

Include /dist/iframe.min.js or import AP from 'simple-xdm/plugin'; on the iframe side. See /example/addon/add-on.html for an example.

Use the API either through globals or modules:

AP.require('messages', function(messages) {
    messages.error('Error', message)
});

AP.request({ ... }, function(result) {
    // do something
});

Create an iframe

The library does not modify the DOM directly, iframe creation is delegated to the host product. Here's how you create an iframe that will be able to call the host API through the simple-xdm bridge:

function setup(extension) {

    var iframeParams = AP.create(extension, init);

    var iframe = document.createElement('iframe');
    iframe.setAttribute('id', iframeParams.id);
    iframe.setAttribute('name', iframeParams.name);
    iframe.setAttribute('src', iframeParams.src);
    iframe.setAttribute('frameBorder', 0);
    document.getElementById(extension.key).appendChild(iframe);
    return true;
}

function init(extensionId) {
    console.log('Bridge established:' + extensionId);
}

setup({
    addon_key: 'my-addon',
    key: 'my-panel',
    url: 'http://localhost:8080/addon/add-on.html'
});

Events

iframes can also handle events dispatched by the host. A callback can optionally be used to return a result.

Event registration in the iframe:

AP.register({
    'some-event': function (event, cb) {
        cb('Some response');
    },
    'some-other-event': function(event) {
        // do something
    }
});

The host side can send events to a specific add-on using

host.dispatch('some-event',
    {addon_key: extension.addon_key},
    {message: message},
    function (result) {
        // do something
    }
);

Or it can target a specific module:

host.dispatch('some-event',
    {addon_key: extension.addon_key, key: extension.key},
    {message: message},
    function (result) {
        // do something
    }
);

Events can also be broadcast to all add-ons on the page:

host.dispatch('some-other-event', {}, {message: message});

iframes can also dispatch events to other iframes from the same plugin. This can be achieved using the broadcast method:

AP.broadcast('some-event', {})   

All other iframes from this plugin will then receive an event identical to events dispatched from the host container, with the event name provided. Broadcast events are not durable and will only be delivered to active iframes.

Seamless Iframes (AKA: auto resizing)

If you wish to have iframes without scrollbars. You can enable auto resizing. This will add new dom elements inside the iframe to detect when the width / height of the iframe content has changed, publish an event to the host page and resize the iframe DOM element.

host.create({
    addon_key: 'my-addon',
    key: 'my-module',
    url: 'https://example.com/my-module',
    options: { autoresize: true }
});

Conventions

  • Callbacks must be the last argument in the function declaration
  • Callbacks must be the only function in the argument list
  • Callbacks are optional
  • All function parameters besides the callback must be serializable through the Structured clone algorithm

Try it

In the project directory:

http-server

Then point your browser to http://localhost:8080/example/product/product.html

To test the actual cross-domain case, you can serve the local project through ngrok:

ngrok -log=stdout 8080

Then point your browser to your ngrok URL: http://.ngrok.com/example/product/product.html

Prerequisites

Install the HTTP server:

npm install -g http-server

Install ngrok

Sub-Extensions

You may require extensions to host other extensions.

  1. Use the combined.js located in ./dist.
  2. Your sub-extensions should use subCreate instead of create

Sub-Extensions modules

By default, modules can only be declared once and will send their messages to the frame that declares them. Modules declared in the top window are available to the sub-extension and can be used as expected.

Extensions can declare additional modules that are available for sub-extensions. By default they cannot overwrite existing modules.

For most use cases this will just work. However, there many be times when the same module is needed for both.

You can redeclare an existing module (as to target the parent frame) using extension options.

/* possible values
* parent    - targets only the parent frame (default).
* top       - targets the top frame (default for window.top declarations).
* both      - targets the parent but allows re-declaring.
**/

var options = {
    targets: {
        moduleName: {
            methodName: 'both'
        }
    }
};
window.combined.subCreate({options:options, ...});