Flyer.js
flyer.js is a lightweight messaging library written for clojurescript & javascript.
It provides broadcast messaging between frames, iframes, and windows.
Including flyer in your clojurescript project
Leiningen
[flyer "1.1.2"]
Clojure CLI/deps.edn
flyer {:mvn/version "1.1.2"}
Gradle
compile 'flyer:flyer:1.1.2'
Maven
<dependency> <groupId>flyer</groupId> <artifactId>flyer</artifactId> <version>1.1.2</version> </dependency>
Features and Operation
- Simple API, from which you can build more elaborate messaging systems.
- Messages are broadcasted to all frames and registered windows, regardless of whether or not they want them (hence, the name)
- Frames can subscribe to specific channels, and can regex-match to broader topics being sent on that channel
Javascript Configuration
- Include flyer.js at the end of your page body
<script type="text/javascript" src="./js/flyer.js"></script>
- In situations where you would use window.open, use flyer.window.open instead. This is required in order to track window references.
Download
Javascript API
flyer.broadcast([Options])
Options
- channel
- the channel you wish to broadcast on. default is β*β, which stands for all channels
- topic
- the topic of your broadcast message. default is β*β, which stands for all topics
- data
- any JSON serializable object
- target
- determines what type of target domain is expected to receive this message. By default, the target is βallβ, but it can be set to βlocalβ, or a target domain of your choosing.
Examples
flyer.broadcast({ channel: "default", topic: "person.insert", data: {id: 1, name: "Ben"} }); flyer.broadcast({ channel: "default", topic: "person.delete", data: {id: 1} });
flyer.subscribe([Options])
Options
- channel
- the channel you wish to subscribe to. default is β*β, which stands for all channels
- topic
- the topic you wish to subscribe to. default is β*β, which stands for all topics. Note that this can also be a string representation of a regex expression.
- callback
- callback function of the form function(data, [topic], [channel] [origin]) that you wish to call when the subscription is made.
- origin
- refers to the origin, or domain from which the messages will be received. The default is all, but it can be set to local, or to another origin
Examples
flyer.subscribe({ channel: "default", topic: "person.*", callback: function(data, topic, channel) { if (topic == "person.insert") { console.log("Inserted Person! - " + data.name); } else if (topic == "person.delete") { console.log("Removed Person with id - " + data.id); } } });
flyer.window.open(url, name, [Options])
follows the same API as window.open
Clojurescript API (untested)
flyer.messaging/broadcast
(broadcast & options)
Options
- channel
- the channel you wish to broadcast on. default is β*β, which stands for all channels
- topic
- the topic of your broadcast message. default is β*β, which stands for all topics
- data
- any JSON serializable object
- target
- refers to the target origin, or domain in which to post the message. The default is :all, but it can also be :local, or a target origin of your choosing
Example
(broadcast :channel "default" :topic "person.insert" :data {:id 1 :name "Ben"} :origin :all)
flyer.messaging/subscribe
(subscribe & options)
Options
- channel
- the channel you wish to subscribe to. default is β*β, which stands for all channels
- topic
- the topic you wish to subscribe to. default is β*β, which stands for all topics. Note that this can also be a string representation of a regex expression.
- callback
- callback function of the form (fn [data] [topic] [channel] [origin]) that you wish to call when the subscription is made.
- origin
- the origin you wish to subscribe to. This is decides on the domain that messages can be received. The default is :all, but it can also be :local, or an origin of your choice
Example
(subscribe :channel "default" :origin :local :topic "person.*" :callback (fn [data topic channel] (condp = topic "person.insert" (.log js/console "Inserting person! - " (.-name data)) "person.delete" (.log js/console "Deleting person! - #" (.-id data)))))
flyer.window/open
(open url name & options)
url parameter
The window URL
name parameter
The unique name you wish to give the window
Options
key / value pairs of options equivalent to window.open options
Example
(open "frame_login.html" "login-page" :width 400 :height 600)
Example
Examples Outdated
Project Compilation
- Clone this Repository
- Install Leiningen
- cd into flyer.js directory
- type lein deps
- resulting flyer.js should now be present in ./resources/public/js/
Issues
- In order to communicate with frames and windows that are within an external window, you need to replace window.open with flyer.window.open
- The size of flyer.js is quite big, at a whopping 1mb. This is due to the nature of compilation. Use flyer.min.js to bring this down to 100kb
- Refreshing the parent window of an opened window will break any messages from being broadcasted throughout the application. This is due to the external window frame losing its parent (window.opener).
- external windows can be refreshed without losing communications, however, it requires that flyer.js be included within that html page
Technical Details
How it works
flyer.js works by hijacking window.postMessage, and the event generated, which can be attached to through the βmessageβ event
//attaching a listener to frame[0] window.frame[0].addEventListener("message", function(event) { console.log(event.data, event.origin) }); //posting a message to frame[0] window.frame[0].postMessage("some message", "*")
It also does a few other things:
- generates a list of frames, by performing a traversal through all of the frames and external windows.
- wraps the functionality into two sane functions.
The full set of steps when performing a broadcast:
- Generate list of frames and external window frames (flyer.traversal)
- Generate a message, by stringifying a defined message object {channel:β¦, topic:β¦, data:β¦} (flyer.messaging)
- For each frame, f, perform f.postMessage(msg, target)
The full steps when performing a subscribe:
- Grab the callback function, and add a βmessageβ event listener decorated with a set of rules
- Does it have the same channel?
- Does it have the same topic?
- If it isnβt the same topic, does the provided callback topic regex match the broadcasted topic?
- Does it have a lax origin, or is it accepting all origins?
- If it passes the conditions in step 1, the main callback is called with the results of the broadcast. Further conditions can be provided at the developers discretion.