Using OS.js libraries in iframe application - how load them?


#1

I’m new to OS.js and I’m picking up some existing code, having a few issues, some are my lack of knoweldge some are intricacies of the existing code.

My objective is to create a new IFrame Application that uses the OS.js settings capability. For that I assume that I need to load the OS.js libraries. I see in the existing code a similar need. so in their index.html they have

... loads more before this
</div>

  <script src="osjs.js"></script>
  <script src="src/js/appcode.js"></script>
</body>
</html>

Now I see on osjs.js a line of code

window.OSjs = (function() {

which I was guessing implies that the DOM needs to be ready before osjs.js can run. And bingo, when I specify

node osjs watch --debug --package=mine/Applciation
… etc … /osjs.js:35
window.OSjs = (function() {
^

ReferenceError: window is not defined
at /home/davea/projects/Unity_Console/overlays/unity/packages/unity/UnityLogViewer/data/osjs.js:35:3
at Object. (/home/davea/projects/Unity_Console/overlays/unity/packages/unity/UnityLogViewer/data/osjs.js:

Despite this the existing application is running! Somehow they avoid this race (would love to know why it works, but that’s no my real question)

So, questions:

  • What’s the official way, if any, of loading those OS.js libraries? Far as I can see, we should be doing it once the DOM is ready.

  • Assuming I can load the libraries is the full API available to the IFrame App?


#2

Hi,

The IFrame applications were originally made to load external content, or re-package existing webapps to OS.js applications.

To use the OS.js functionality inside the iframe you can do messaging between your iframe and the window (which is OS.js). An iframe is basically a separate instance, so if you load the osjs.js file, you’ll basically create another OS.js instance, since that contains all the bootstrapping etc.

Seems like you’re on v2… I might have some examples for this lying around. I’ll look.


#3

Thank you for the speedy response.

Am I right in thinking that using a “normal” Application might be a better choice than iFrame if I want to be a fully-fledged OS.js citizen? I’m only using an iFrame for my new Application because that’s what the legacy code is using. I am probably stuck with the version that is currently being used.


#4

Yes. Going “native” is the best option.

That really depends on how your legacy codebase was written.

What are you trying to do here exactly ? You want to convert a GUI, or is there some other functionality in the OS.js core libs you want ?


#5

I’m adding new discrete function to an existing composite app. Existing applications are each independent in iFrames, but were custom written, don’t think they needed to be iFrame, but that is how it was done, they all seem to load their own OS.js instance, don’t know exactly what they use.

Meanwhile my app will be written from scratch, I have free rein. My initial requirement is to use settings APIs and ideally open new windows and perhaps have them inter-communicate. I’m assuming that full OS.js is what I need.


#6

Yeah, that would be best. And I recommend going with v3 if you’re going to make something new. I’m pretty much ending v2 support once I get v3 out.


Speaking of v3. I’ve already made it so you can use the libraries externally. The GUI stuff is fully working. Example:

So you can do that in an iframe. But to make use of any of the core APIs that relies on the OS.js instance (basically all the core.<method>() stuff) some form of communication library is required, which I will be looking into when I’m done with all the other stuff on my current goal list.

I’ve made some facilities for this in the client which is demonstrated in the @osjs/example-iframe-application. But it has some limitations, and requires a lot of boiler-plate and stuff… hopefully with this communication library it will all be super simple and minimal.

That said, I still recommend going native when making new applications. A lot less of things to worry about and potentially go wrong :slight_smile:


#7

I’ve been getting some questions from other channels relating to using libraries that might have to be isolated, and from some of the posts you’ve created here, I’ve been experimenting more with this communication library a bit more.

Might have something to demo this weekend :slight_smile:


#8

Because of some unforeseen issues relating to personal life, this got a bit delayed… however, I’ve started pushing out some stuff relating to this.

These are the sources as of now:

Making iframe windows is now a lot easier and has a standard for communication.

Your iframe window could look like kinda this (which can be improved with Webpack and Babel):

<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <div>
      <button id="create-dialog">Create Dialog</button>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/@osjs/iframe/dist/main.js"></script>
    <script>
const {IFrame} = osjsIframe;

const inst = new IFrame();

inst.on('osjs:init', ({type}) => {
  inst.send({type: 'ping'});
});

inst.on('message', msg => {
  console.warn('[iframe] message', msg);
  if (msg.type === 'pong') {
    console.info('Application said hello');
  }
});

document.getElementById('create-dialog').addEventListener('click', () => {
  inst.send({type: 'create-dialog'}, ({btn, value}) => {
    console.info('Dialog response was', {btn, value});
  });
});
    </script>
  </body>
</html>

And the OS.js application that wraps this together:

import {name as applicationName} from './metadata.json';

OSjs.make('osjs/packages').register(applicationName, (core, args, options, metadata) => {
  // Create a new Application instance
  const proc = core.make('osjs/application', {args, options, metadata});

  // Create a new IFrame Window
  const win = proc.createIframeWindow();

  // Register our iframe events
  win.on('iframe:init', context => {
    console.log('iframe inited');
  });

  win.on('iframe:message', (context, payload) => {
    const {type} = payload;

    switch (type) {
    case 'ping':
      console.log('IFrame says hello');
      context.send({type: 'pong'});
      break;

    case 'create-dialog':
      core.make('osjs/dialog', 'alert', {message: 'IFrame dialog'}, (btn, value) => {
        context.respond({btn, value});
      });
      break;

    default:
      console.warn('Unknown iframe signal', payload);
      break;
    }
  });

  // Make sure application is destroyed when window closes
  win.on('destroy', () => proc.destroy());

  // Opens the window iframe to a spesified location
  win.open(proc.resource('/data/index.html'));

  return proc;
});

I’m not sure if this makes any sense without a graphical demonstration, so I made a quick one (though very basic, might give you an idea of what’s going on… but in 4K, so don’t know how it turned out) :slight_smile: