diff --git a/README.md b/README.md
index 94196d3..419ea61 100644
--- a/README.md
+++ b/README.md
@@ -6,34 +6,35 @@ Ruto is a lightweight(4KB), fast and easy-to-use JS library that streamlines the
It uses client-server design pattern to communicate between parent and child window. Any window can become the client or the server depending on who wants to send. It abstracts out the complications of postMessage API and provides a simple API to send and receive messages.
-
## Scenarios where it can be used
1. Parent window wants to send a message to child window and wants to wait for the response from the child window.
2. Parent window wants to send a message to child window and expects a reply within x seconds.
## Demo
-- [Parent to Iframe](https://rajnandan1.github.io/ruto/index.html)
-- [Parent to Popup](https://rajnandan1.github.io/ruto/index2.html)
+
+- [Parent to Iframe](https://rajnandan1.github.io/ruto/index.html)
+- [Parent to Popup](https://rajnandan1.github.io/ruto/index2.html)
## Table of Contents
-- [Installation](#installation)
-- [API](#api)
- - [send](#send)
- - [receive](#receive)
-- [Usage](#usage)
- - [Parent to Iframe Example](#parent-to-iframe-example)
- - [Parent Window](#parent-window)
- - [Child Window](#child-window)
- - [Parent to Popup Example](#parent-to-popup-example)
- - [Parent Window](#parent-window-1)
- - [Popup Window](#popup-window)
- - [Iframe to Parent Example](#iframe-to-parent-example)
- - [Iframe Window](#iframe-window)
- - [Parent Window](#parent-window-2)
- - [Popup to Parent Example](#popup-to-parent-example)
- - [Popup Window](#popup-window-1)
- - [Parent Window](#parent-window-3)
+
+- [Installation](#installation)
+- [API](#api)
+ - [send](#send)
+ - [receive](#receive)
+- [Usage](#usage)
+ - [Parent to Iframe Example](#parent-to-iframe-example)
+ - [Parent Window](#parent-window)
+ - [Child Window](#child-window)
+ - [Parent to Popup Example](#parent-to-popup-example)
+ - [Parent Window](#parent-window-1)
+ - [Popup Window](#popup-window)
+ - [Iframe to Parent Example](#iframe-to-parent-example)
+ - [Iframe Window](#iframe-window)
+ - [Parent Window](#parent-window-2)
+ - [Popup to Parent Example](#popup-to-parent-example)
+ - [Popup Window](#popup-window-1)
+ - [Parent Window](#parent-window-3)
## Installation
@@ -42,13 +43,13 @@ It uses client-server design pattern to communicate between parent and child win
```html
```
+
### ES6
```bash
npm i @rajnandan1/ruto
```
-
## API
The library exposes two methods `send` and `receive`.
@@ -57,33 +58,39 @@ The library exposes two methods `send` and `receive`.
The send method is used to send a message from parent window to child window or vice versa. It returns a promise which resolves with the response from the child window.
-
```javascript
send(route: string, node: Window | HTMLIFrameElement, message: string, options: WindowMQOptions): Promise
```
+
### route
+
The route is a unique identifier for the message. It has three parts
-- origin: The origin of the child window. Example if iframe or popup is located at http://example.com/iframe.html, then the origin will be http://example.com
-- type: The type of the window communication
- - parent-to-iframe: When you want to send a message from parent to iframe
- - parent-to-window: When you want to send a message from parent to popup
- - iframe-to-parent: When you want to send a message from iframe to parent
- - window-to-parent: When you want to send a message from popup to parent
-- topic: The topic of the message. Can be any string
+
+- origin: The origin of the child window. Example if iframe or popup is located at http://example.com/iframe.html, then the origin will be http://example.com
+- type: The type of the window communication
+ - parent-to-iframe: When you want to send a message from parent to iframe
+ - parent-to-window: When you want to send a message from parent to popup
+ - iframe-to-parent: When you want to send a message from iframe to parent
+ - window-to-parent: When you want to send a message from popup to parent
+- topic: The topic of the message. Can be any string
Example: http://example.com/parent-to-iframe/sometopic
### node
+
The node is the child window to which the message is to be sent. It can be either a window object or an iframe element.
Example: `document.getElementById('iframe') or window.open('popup.html', 'popup', 'width=600,height=400)`
### message
+
The message is the data that is to be sent to the child window. It has to be a `string`. If you want to send JSON, you have to stringify it before sending.
### options
+
The options is an object that can have the following properties. It is optional
-- timeout: The time in milliseconds to wait for the response from the child window. If the response is not received within the timeout, the promise will be rejected with a timeout error.
-Example: `{timeout: 5000}`
+
+- timeout: The time in milliseconds to wait for the response from the child window. If the response is not received within the timeout, the promise will be rejected with a timeout error.
+ Example: `{timeout: 5000}`
### receive
@@ -94,13 +101,15 @@ receive(subpath: string, node: Window | HTMLIFrameElement, callback: (res: Respo
```
### subpath
+
The subpath is a unique identifier for the message. It has two parts
-- type: The type of the window communication
- - parent-to-iframe: When you want to send a message from parent to iframe
- - parent-to-window: When you want to send a message from parent to popup
- - iframe-to-parent: When you want to send a message from iframe to parent
- - window-to-parent: When you want to send a message from popup to parent
-- topic: The topic of the message. Can be any string
+
+- type: The type of the window communication
+ - parent-to-iframe: When you want to send a message from parent to iframe
+ - parent-to-window: When you want to send a message from parent to popup
+ - iframe-to-parent: When you want to send a message from iframe to parent
+ - window-to-parent: When you want to send a message from popup to parent
+- topic: The topic of the message. Can be any string
> [!IMPORTANT]
> The subpath should be the same as the route of the sender. If the subpath is different, the message will not be received. It does not need the origin part of the route.
@@ -109,58 +118,58 @@ Example: parent-to-iframe/sometopic
### node
-The node is the window from which the message is to be received. It can be
-- iframe element
-- popup window reference
-- window.parent
-- window.opener
+The node is the window from which the message is to be received. It can be
+
+- iframe element
+- popup window reference
+- window.parent
+- window.opener
Example: `document.getElementById('iframe') or window.parent`
### callback
The callback is a function that is called when a message is received. It has two parameters
-- response: The response object that is used to send a response back to the sender using `response.send`
-- message: The message that is sent by the sender. It is a `string`.
-Example:
+- response: The response object that is used to send a response back to the sender using `response.send`
+- message: The message that is sent by the sender. It is a `string`.
+
+Example:
+
```javascript
ruto.receive('http://localhost:3000/parent-to-iframe/sometopic', window.parent, (response, message) => {
- const newMessage = message + " edited by child";
- return response.send(newMessage);
+ const newMessage = message + ' edited by child';
+ return response.send(newMessage);
});
```
-
## Usage
### ⭐ Parent to Iframe Example
-
-
#### Parent Window
-
-
```javascript
//
const options = {
- timeout: 5000
+ timeout: 5000,
};
-ruto.send('http://localhost:3000/parent-to-iframe/sometopic', document.getElementById('iframe'), "Your message", options).then((response) => {
- console.log(response); //Your message edited by iframe
-}).catch((error) => {
- console.log(error);
-});
+ruto.send('http://localhost:3000/parent-to-iframe/sometopic', document.getElementById('iframe'), 'Your message', options)
+ .then((response) => {
+ console.log(response); //Your message edited by iframe
+ })
+ .catch((error) => {
+ console.log(error);
+ });
```
#### Child Window
```javascript
ruto.receive('/parent-to-iframe/sometopic', window.parent, (response, message) => {
- const newMessage = message + " edited by iframe";
- return response.send(newMessage);
+ const newMessage = message + ' edited by iframe';
+ return response.send(newMessage);
});
```
@@ -172,21 +181,23 @@ ruto.receive('/parent-to-iframe/sometopic', window.parent, (response, message) =
//const popup = window.open('popup.html', 'popup', 'width=600,height=400');
const options = {
- timeout: 5000
+ timeout: 5000,
};
-ruto.send('http://localhost:3000/parent-to-window/sometopic', popup, "Your message", options).then((response) => {
- console.log(response); //Your message edited by popup
-}).catch((error) => {
- console.log(error);
-});
+ruto.send('http://localhost:3000/parent-to-window/sometopic', popup, 'Your message', options)
+ .then((response) => {
+ console.log(response); //Your message edited by popup
+ })
+ .catch((error) => {
+ console.log(error);
+ });
```
#### Popup Window
```javascript
ruto.receive('/parent-to-window/sometopic', window.opener, (response, message) => {
- const newMessage = message + " edited by popup";
- return response.send(newMessage);
+ const newMessage = message + ' edited by popup';
+ return response.send(newMessage);
});
```
@@ -196,45 +207,48 @@ ruto.receive('/parent-to-window/sometopic', window.opener, (response, message) =
```javascript
const options = {
- timeout: 5000
+ timeout: 5000,
};
-ruto.send('http://localhost:3000/iframe-to-parent/sometopic', window.parent, "Your message", options).then((response) => {
- console.log(response); //Your message edited by parent
-}).catch((error) => {
- console.log(error);
-});
+ruto.send('http://localhost:3000/iframe-to-parent/sometopic', window.parent, 'Your message', options)
+ .then((response) => {
+ console.log(response); //Your message edited by parent
+ })
+ .catch((error) => {
+ console.log(error);
+ });
```
#### Parent Window
```javascript
ruto.receive('/iframe-to-parent/sometopic', window.parent, (response, message) => {
- const newMessage = message + " edited by parent";
- return response.send(newMessage);
+ const newMessage = message + ' edited by parent';
+ return response.send(newMessage);
});
```
-
### ⭐ Popup to Parent Example
#### Popup Window
```javascript
const options = {
- timeout: 5000
+ timeout: 5000,
};
-ruto.send('http://localhost:3000/window-to-parent/sometopic', window.opener, "Your message", options).then((response) => {
- console.log(response); //Your message edited by parent
-}).catch((error) => {
- console.log(error);
-});
+ruto.send('http://localhost:3000/window-to-parent/sometopic', window.opener, 'Your message', options)
+ .then((response) => {
+ console.log(response); //Your message edited by parent
+ })
+ .catch((error) => {
+ console.log(error);
+ });
```
#### Parent Window
```javascript
ruto.receive('/window-to-parent/sometopic', window.opener, (response, message) => {
- const newMessage = message + " edited by parent";
- return response.send(newMessage);
+ const newMessage = message + ' edited by parent';
+ return response.send(newMessage);
});
```
diff --git a/dist/ruto.cjs.js b/dist/ruto.cjs.js
index e35c964..254b889 100644
--- a/dist/ruto.cjs.js
+++ b/dist/ruto.cjs.js
@@ -19,11 +19,9 @@ function GetFromType(url) {
const toDestination = splittedUrl[1];
if (toDestination.startsWith('parent')) {
return 'parent';
- }
- else if (toDestination.startsWith('iframe')) {
+ } else if (toDestination.startsWith('iframe')) {
return 'iframe';
- }
- else if (toDestination.startsWith('window')) {
+ } else if (toDestination.startsWith('window')) {
return 'window';
}
return 'parent';
@@ -35,18 +33,17 @@ function GetToType(url) {
const toDestination = splittedUrl[1];
if (toDestination.endsWith('parent')) {
return 'parent';
- }
- else if (toDestination.endsWith('iframe')) {
+ } else if (toDestination.endsWith('iframe')) {
return 'iframe';
- }
- else if (toDestination.endsWith('window')) {
+ } else if (toDestination.endsWith('window')) {
return 'window';
}
return 'iframe';
}
function GetUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
- const r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8;
+ const r = (Math.random() * 16) | 0,
+ v = c == 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
@@ -76,6 +73,21 @@ function ValidatePath(url) {
}
return true;
}
+function WaitForIframeLoad(iframe, timeout) {
+ return new Promise((resolve, reject) => {
+ const timeoutId = setTimeout(() => {
+ clearInterval(interval);
+ reject(new Error('Timeout exceeded'));
+ }, timeout);
+ const interval = setInterval(() => {
+ if (iframe.contentWindow) {
+ clearInterval(interval);
+ clearTimeout(timeoutId);
+ resolve();
+ }
+ }, 100);
+ });
+}
class SenderImpl {
constructor(route, node, options) {
@@ -105,13 +117,20 @@ class SenderImpl {
};
if (this.client.nodeTypeTo == 'iframe') {
const iframe = this.client.node;
- (_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage(payload, this.client.toOrigin);
- }
- else if (this.client.nodeTypeTo == 'parent') {
+ //wait for iframe to load
+ if (this.client.nodeReady) {
+ (_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage(payload, this.client.toOrigin);
+ } else {
+ WaitForIframeLoad(iframe, this.timeout).then(() => {
+ var _a;
+ this.client.nodeReady = true;
+ (_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage(payload, this.client.toOrigin);
+ });
+ }
+ } else if (this.client.nodeTypeTo == 'parent') {
const win = this.client.node;
win.postMessage(payload, this.client.toOrigin);
- }
- else if (this.client.nodeTypeTo == 'window') {
+ } else if (this.client.nodeTypeTo == 'window') {
const win = this.client.node;
win.postMessage(payload, this.client.toOrigin);
}
@@ -135,8 +154,7 @@ class SenderImpl {
clearTimeout(timer);
};
window.addEventListener('message', messageListener);
- }
- catch (error) {
+ } catch (error) {
this.rejecter(error);
}
});
@@ -149,10 +167,11 @@ class SenderImpl {
toOrigin: toOrigin,
fromOrigin: GetOrigin(window.location.href),
subpath: route.replace(toOrigin, ''),
+ nodeReady: false,
};
this.timeout = (options === null || options === void 0 ? void 0 : options.timeout) || 3000;
- this.resolver = (value) => { };
- this.rejecter = (reason) => { };
+ this.resolver = (value) => {};
+ this.rejecter = (reason) => {};
}
}
@@ -175,16 +194,13 @@ class ResponseImpl {
if (fromType == 'iframe' && toType == 'parent') {
let win = this.client.node;
win.postMessage(payload, this.client.toOrigin);
- }
- else if (fromType == 'parent' && toType == 'iframe') {
+ } else if (fromType == 'parent' && toType == 'iframe') {
let iframe = this.client.node;
(_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage(payload, this.client.toOrigin);
- }
- else if (fromType == 'window' && toType == 'parent') {
+ } else if (fromType == 'window' && toType == 'parent') {
let win = this.client.node;
win.postMessage(payload, this.client.toOrigin);
- }
- else if (fromType == 'parent' && toType == 'window') {
+ } else if (fromType == 'parent' && toType == 'window') {
let win = this.client.node;
win.postMessage(payload, this.client.toOrigin);
}
@@ -218,6 +234,7 @@ class ReceiverImpl {
toOrigin: event.data.fromOrigin,
fromOrigin: event.data.toOrigin,
subpath: subpath,
+ nodeReady: false,
};
const resp = new ResponseImpl(event.data.id, client);
callback(resp, event.data.message);
diff --git a/dist/ruto.cjs.js.map b/dist/ruto.cjs.js.map
index c871e5d..281008d 100644
--- a/dist/ruto.cjs.js.map
+++ b/dist/ruto.cjs.js.map
@@ -1 +1 @@
-{"version":3,"file":"ruto.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
\ No newline at end of file
+{"version":3,"file":"ruto.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
\ No newline at end of file
diff --git a/dist/ruto.esm.js b/dist/ruto.esm.js
index 9779fd2..68378c2 100644
--- a/dist/ruto.esm.js
+++ b/dist/ruto.esm.js
@@ -17,11 +17,9 @@ function GetFromType(url) {
const toDestination = splittedUrl[1];
if (toDestination.startsWith('parent')) {
return 'parent';
- }
- else if (toDestination.startsWith('iframe')) {
+ } else if (toDestination.startsWith('iframe')) {
return 'iframe';
- }
- else if (toDestination.startsWith('window')) {
+ } else if (toDestination.startsWith('window')) {
return 'window';
}
return 'parent';
@@ -33,18 +31,17 @@ function GetToType(url) {
const toDestination = splittedUrl[1];
if (toDestination.endsWith('parent')) {
return 'parent';
- }
- else if (toDestination.endsWith('iframe')) {
+ } else if (toDestination.endsWith('iframe')) {
return 'iframe';
- }
- else if (toDestination.endsWith('window')) {
+ } else if (toDestination.endsWith('window')) {
return 'window';
}
return 'iframe';
}
function GetUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
- const r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8;
+ const r = (Math.random() * 16) | 0,
+ v = c == 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
@@ -74,6 +71,21 @@ function ValidatePath(url) {
}
return true;
}
+function WaitForIframeLoad(iframe, timeout) {
+ return new Promise((resolve, reject) => {
+ const timeoutId = setTimeout(() => {
+ clearInterval(interval);
+ reject(new Error('Timeout exceeded'));
+ }, timeout);
+ const interval = setInterval(() => {
+ if (iframe.contentWindow) {
+ clearInterval(interval);
+ clearTimeout(timeoutId);
+ resolve();
+ }
+ }, 100);
+ });
+}
class SenderImpl {
constructor(route, node, options) {
@@ -103,13 +115,20 @@ class SenderImpl {
};
if (this.client.nodeTypeTo == 'iframe') {
const iframe = this.client.node;
- (_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage(payload, this.client.toOrigin);
- }
- else if (this.client.nodeTypeTo == 'parent') {
+ //wait for iframe to load
+ if (this.client.nodeReady) {
+ (_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage(payload, this.client.toOrigin);
+ } else {
+ WaitForIframeLoad(iframe, this.timeout).then(() => {
+ var _a;
+ this.client.nodeReady = true;
+ (_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage(payload, this.client.toOrigin);
+ });
+ }
+ } else if (this.client.nodeTypeTo == 'parent') {
const win = this.client.node;
win.postMessage(payload, this.client.toOrigin);
- }
- else if (this.client.nodeTypeTo == 'window') {
+ } else if (this.client.nodeTypeTo == 'window') {
const win = this.client.node;
win.postMessage(payload, this.client.toOrigin);
}
@@ -133,8 +152,7 @@ class SenderImpl {
clearTimeout(timer);
};
window.addEventListener('message', messageListener);
- }
- catch (error) {
+ } catch (error) {
this.rejecter(error);
}
});
@@ -147,10 +165,11 @@ class SenderImpl {
toOrigin: toOrigin,
fromOrigin: GetOrigin(window.location.href),
subpath: route.replace(toOrigin, ''),
+ nodeReady: false,
};
this.timeout = (options === null || options === void 0 ? void 0 : options.timeout) || 3000;
- this.resolver = (value) => { };
- this.rejecter = (reason) => { };
+ this.resolver = (value) => {};
+ this.rejecter = (reason) => {};
}
}
@@ -173,16 +192,13 @@ class ResponseImpl {
if (fromType == 'iframe' && toType == 'parent') {
let win = this.client.node;
win.postMessage(payload, this.client.toOrigin);
- }
- else if (fromType == 'parent' && toType == 'iframe') {
+ } else if (fromType == 'parent' && toType == 'iframe') {
let iframe = this.client.node;
(_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage(payload, this.client.toOrigin);
- }
- else if (fromType == 'window' && toType == 'parent') {
+ } else if (fromType == 'window' && toType == 'parent') {
let win = this.client.node;
win.postMessage(payload, this.client.toOrigin);
- }
- else if (fromType == 'parent' && toType == 'window') {
+ } else if (fromType == 'parent' && toType == 'window') {
let win = this.client.node;
win.postMessage(payload, this.client.toOrigin);
}
@@ -216,6 +232,7 @@ class ReceiverImpl {
toOrigin: event.data.fromOrigin,
fromOrigin: event.data.toOrigin,
subpath: subpath,
+ nodeReady: false,
};
const resp = new ResponseImpl(event.data.id, client);
callback(resp, event.data.message);
diff --git a/dist/ruto.esm.js.map b/dist/ruto.esm.js.map
index 0c1affe..9e2a58f 100644
--- a/dist/ruto.esm.js.map
+++ b/dist/ruto.esm.js.map
@@ -1 +1 @@
-{"version":3,"file":"ruto.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
\ No newline at end of file
+{"version":3,"file":"ruto.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
\ No newline at end of file
diff --git a/dist/ruto.min.js b/dist/ruto.min.js
index 8d084c6..6bb0ac4 100644
--- a/dist/ruto.min.js
+++ b/dist/ruto.min.js
@@ -1,2 +1,128 @@
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).ruto={})}(this,(function(e){"use strict";function t(e){if(e.startsWith("*"))return"*";if(e.startsWith("/"))return"";const t=document.createElement("a");return t.href=e,t.origin}function i(e){const i=t(e),n=e.replace(i,"").split("/")[1];return n.startsWith("parent")?"parent":n.startsWith("iframe")?"iframe":n.startsWith("window")?"window":"parent"}function n(e){const i=t(e),n=e.replace(i,"").split("/")[1];return n.endsWith("parent")?"parent":n.endsWith("iframe")?"iframe":n.endsWith("window")?"window":"iframe"}function o(e){const i=t(e),n=e.replace(i,"");if(!n.startsWith("/"))return!1;const o=n.split("/");if(3!=o.length)return!1;const s=o[1];return!!(s.startsWith("parent")||s.startsWith("iframe")||s.startsWith("window"))&&(!(s.startsWith("parent")&&!s.endsWith("iframe")&&!s.endsWith("window"))&&!((s.startsWith("iframe")||s.startsWith("window"))&&!s.endsWith("parent")))}class s{constructor(e,o,s){this.send=e=>new Promise(((t,i)=>{var n;if(this.resolver=t,this.rejecter=i,null==this.client.node||null==this.client.node)return i("Client Unhealthy");if("undefined"==typeof window||"undefined"==typeof document)return t("");try{const t="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)})),i={message:e,subpath:this.client.subpath,id:t,fromOrigin:this.client.fromOrigin,toOrigin:this.client.toOrigin,nodeTypeTo:this.client.nodeTypeTo,nodeTypeFrom:this.client.nodeTypeFrom};if("iframe"==this.client.nodeTypeTo){null===(n=this.client.node.contentWindow)||void 0===n||n.postMessage(i,this.client.toOrigin)}else if("parent"==this.client.nodeTypeTo){this.client.node.postMessage(i,this.client.toOrigin)}else if("window"==this.client.nodeTypeTo){this.client.node.postMessage(i,this.client.toOrigin)}const o=setTimeout((()=>{this.rejecter("Timeout"),window.removeEventListener("message",s)}),this.timeout),s=e=>{var i;"*"!=e.data.fromOrigin&&e.origin!==e.data.fromOrigin||(null===(i=e.data)||void 0===i?void 0:i.subpath)===this.client.subpath&&e.data.id===t&&(this.resolver(e.data.message),window.removeEventListener("message",s),clearTimeout(o))};window.addEventListener("message",s)}catch(e){this.rejecter(e)}}));const r=t(e);this.client={node:o,nodeTypeTo:n(e),nodeTypeFrom:i(e),toOrigin:r,fromOrigin:t(window.location.href),subpath:e.replace(r,"")},this.timeout=(null==s?void 0:s.timeout)||3e3,this.resolver=e=>{},this.rejecter=e=>{}}}class r{constructor(e,t){this.send=e=>{var t;const i={id:this.messageId,message:e,fromOrigin:this.client.fromOrigin,toOrigin:this.client.toOrigin,nodeTypeTo:this.client.nodeTypeTo,nodeTypeFrom:this.client.nodeTypeFrom,subpath:this.client.subpath};let n=this.client.nodeTypeFrom,o=this.client.nodeTypeTo;if("iframe"==n&&"parent"==o){this.client.node.postMessage(i,this.client.toOrigin)}else if("parent"==n&&"iframe"==o){null===(t=this.client.node.contentWindow)||void 0===t||t.postMessage(i,this.client.toOrigin)}else if("window"==n&&"parent"==o){this.client.node.postMessage(i,this.client.toOrigin)}else if("parent"==n&&"window"==o){this.client.node.postMessage(i,this.client.toOrigin)}},this.messageId=e,this.client=t}}class a{constructor(){this.receive=(e,t,i)=>{"undefined"!=typeof window&&"undefined"!=typeof document&&window.addEventListener("message",(n=>{var o;if(n.origin!==n.data.fromOrigin)return;if((null===(o=n.data)||void 0===o?void 0:o.subpath)!==e)return;if("parent"==n.data.nodeTypeFrom&&"window"==n.data.nodeTypeTo&&n.source!=t)return;const s={node:t,nodeTypeTo:n.data.nodeTypeFrom,nodeTypeFrom:n.data.nodeTypeTo,toOrigin:n.data.fromOrigin,fromOrigin:n.data.toOrigin,subpath:e},a=new r(n.data.id,s);i(a,n.data.message)}))}}}e.receive=function(e,t,i){if(!o(e))throw new Error("Invalid Path");(new a).receive(e,t,i)},e.send=function(e,t,i,n){if(!o(e))throw new Error("Invalid Path");return new s(e,t,n).send(i)}}));
+!(function (e, t) {
+ 'object' == typeof exports && 'undefined' != typeof module ? t(exports) : 'function' == typeof define && define.amd ? define(['exports'], t) : t(((e = 'undefined' != typeof globalThis ? globalThis : e || self).ruto = {}));
+})(this, function (e) {
+ 'use strict';
+ function t(e) {
+ if (e.startsWith('*')) return '*';
+ if (e.startsWith('/')) return '';
+ const t = document.createElement('a');
+ return (t.href = e), t.origin;
+ }
+ function i(e) {
+ const i = t(e),
+ n = e.replace(i, '').split('/')[1];
+ return n.startsWith('parent') ? 'parent' : n.startsWith('iframe') ? 'iframe' : n.startsWith('window') ? 'window' : 'parent';
+ }
+ function n(e) {
+ const i = t(e),
+ n = e.replace(i, '').split('/')[1];
+ return n.endsWith('parent') ? 'parent' : n.endsWith('iframe') ? 'iframe' : n.endsWith('window') ? 'window' : 'iframe';
+ }
+ function o(e) {
+ const i = t(e),
+ n = e.replace(i, '');
+ if (!n.startsWith('/')) return !1;
+ const o = n.split('/');
+ if (3 != o.length) return !1;
+ const s = o[1];
+ return !!(s.startsWith('parent') || s.startsWith('iframe') || s.startsWith('window')) && !(s.startsWith('parent') && !s.endsWith('iframe') && !s.endsWith('window')) && !((s.startsWith('iframe') || s.startsWith('window')) && !s.endsWith('parent'));
+ }
+ class s {
+ constructor(e, o, s) {
+ this.send = (e) =>
+ new Promise((t, i) => {
+ var n;
+ if (((this.resolver = t), (this.rejecter = i), null == this.client.node || null == this.client.node)) return i('Client Unhealthy');
+ if ('undefined' == typeof window || 'undefined' == typeof document) return t('');
+ try {
+ const t = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (e) {
+ const t = (16 * Math.random()) | 0;
+ return ('x' == e ? t : (3 & t) | 8).toString(16);
+ }),
+ i = { message: e, subpath: this.client.subpath, id: t, fromOrigin: this.client.fromOrigin, toOrigin: this.client.toOrigin, nodeTypeTo: this.client.nodeTypeTo, nodeTypeFrom: this.client.nodeTypeFrom };
+ if ('iframe' == this.client.nodeTypeTo) {
+ const e = this.client.node;
+ this.client.nodeReady
+ ? null === (n = e.contentWindow) || void 0 === n || n.postMessage(i, this.client.toOrigin)
+ : (function (e, t) {
+ return new Promise((i, n) => {
+ const o = setTimeout(() => {
+ clearInterval(s), n(new Error('Timeout exceeded'));
+ }, t),
+ s = setInterval(() => {
+ e.contentWindow && (clearInterval(s), clearTimeout(o), i());
+ }, 100);
+ });
+ })(e, this.timeout).then(() => {
+ var t;
+ (this.client.nodeReady = !0), null === (t = e.contentWindow) || void 0 === t || t.postMessage(i, this.client.toOrigin);
+ });
+ } else if ('parent' == this.client.nodeTypeTo) {
+ this.client.node.postMessage(i, this.client.toOrigin);
+ } else if ('window' == this.client.nodeTypeTo) {
+ this.client.node.postMessage(i, this.client.toOrigin);
+ }
+ const o = setTimeout(() => {
+ this.rejecter('Timeout'), window.removeEventListener('message', s);
+ }, this.timeout),
+ s = (e) => {
+ var i;
+ ('*' != e.data.fromOrigin && e.origin !== e.data.fromOrigin) || ((null === (i = e.data) || void 0 === i ? void 0 : i.subpath) === this.client.subpath && e.data.id === t && (this.resolver(e.data.message), window.removeEventListener('message', s), clearTimeout(o)));
+ };
+ window.addEventListener('message', s);
+ } catch (e) {
+ this.rejecter(e);
+ }
+ });
+ const r = t(e);
+ (this.client = { node: o, nodeTypeTo: n(e), nodeTypeFrom: i(e), toOrigin: r, fromOrigin: t(window.location.href), subpath: e.replace(r, ''), nodeReady: !1 }), (this.timeout = (null == s ? void 0 : s.timeout) || 3e3), (this.resolver = (e) => {}), (this.rejecter = (e) => {});
+ }
+ }
+ class r {
+ constructor(e, t) {
+ (this.send = (e) => {
+ var t;
+ const i = { id: this.messageId, message: e, fromOrigin: this.client.fromOrigin, toOrigin: this.client.toOrigin, nodeTypeTo: this.client.nodeTypeTo, nodeTypeFrom: this.client.nodeTypeFrom, subpath: this.client.subpath };
+ let n = this.client.nodeTypeFrom,
+ o = this.client.nodeTypeTo;
+ if ('iframe' == n && 'parent' == o) {
+ this.client.node.postMessage(i, this.client.toOrigin);
+ } else if ('parent' == n && 'iframe' == o) {
+ null === (t = this.client.node.contentWindow) || void 0 === t || t.postMessage(i, this.client.toOrigin);
+ } else if ('window' == n && 'parent' == o) {
+ this.client.node.postMessage(i, this.client.toOrigin);
+ } else if ('parent' == n && 'window' == o) {
+ this.client.node.postMessage(i, this.client.toOrigin);
+ }
+ }),
+ (this.messageId = e),
+ (this.client = t);
+ }
+ }
+ class a {
+ constructor() {
+ this.receive = (e, t, i) => {
+ 'undefined' != typeof window &&
+ 'undefined' != typeof document &&
+ window.addEventListener('message', (n) => {
+ var o;
+ if (n.origin !== n.data.fromOrigin) return;
+ if ((null === (o = n.data) || void 0 === o ? void 0 : o.subpath) !== e) return;
+ if ('parent' == n.data.nodeTypeFrom && 'window' == n.data.nodeTypeTo && n.source != t) return;
+ const s = { node: t, nodeTypeTo: n.data.nodeTypeFrom, nodeTypeFrom: n.data.nodeTypeTo, toOrigin: n.data.fromOrigin, fromOrigin: n.data.toOrigin, subpath: e, nodeReady: !1 },
+ a = new r(n.data.id, s);
+ i(a, n.data.message);
+ });
+ };
+ }
+ }
+ (e.receive = function (e, t, i) {
+ if (!o(e)) throw new Error('Invalid Path');
+ new a().receive(e, t, i);
+ }),
+ (e.send = function (e, t, i, n) {
+ if (!o(e)) throw new Error('Invalid Path');
+ return new s(e, t, n).send(i);
+ });
+});
//# sourceMappingURL=ruto.min.js.map
diff --git a/index.html b/index.html
index 833ac7c..56f2444 100644
--- a/index.html
+++ b/index.html
@@ -84,7 +84,7 @@ Parent Window
return v.toString(16);
});
}
- //get current origin
+ //get current origin
function sendMessage() {
const id = GetUUID();
$('#mymessage').animate({ scrollTop: $('#mymessage').prop('scrollHeight') }, 1000);
diff --git a/index2.html b/index2.html
index 7585970..b3d2fac 100644
--- a/index2.html
+++ b/index2.html
@@ -32,7 +32,7 @@
Window Demo
-
+
Github
diff --git a/package.json b/package.json
index 327c07b..edd0c44 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@rajnandan1/ruto",
- "version": "1.0.0",
+ "version": "1.0.1",
"description": "Ruto is a lightweight(4KB), fast and easy-to-use JS library that streamlines the communication between parent and child window(iframe/popup).",
"type": "module",
"main": "dist/ruto.min.js",
@@ -35,4 +35,4 @@
"tslib": "^2.6.2",
"typescript": "^5.4.5"
}
-}
\ No newline at end of file
+}
diff --git a/src/common.ts b/src/common.ts
index 228af5a..39677a5 100644
--- a/src/common.ts
+++ b/src/common.ts
@@ -1,5 +1,3 @@
-
-
export interface WindowMQOptions {
timeout: number;
}
@@ -10,6 +8,7 @@ export interface WindowClient {
toOrigin: string;
fromOrigin: string;
subpath: string;
+ nodeReady: boolean;
}
export interface Payload {
diff --git a/src/receive.ts b/src/receive.ts
index 9aa975a..2648d9e 100644
--- a/src/receive.ts
+++ b/src/receive.ts
@@ -50,7 +50,7 @@ export interface Receiver {
export class ReceiverImpl implements Receiver {
receive: (subpath: string, node: Window | HTMLIFrameElement, callback: (res: Response, message: string) => void) => void = (subpath: string, node: Window | HTMLIFrameElement, callback: (res: Response, message: string) => void) => {
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
- window.addEventListener('message', (event) => {
+ window.addEventListener('message', (event) => {
if (event.origin !== event.data.fromOrigin) {
return;
}
@@ -72,11 +72,12 @@ export class ReceiverImpl implements Receiver {
toOrigin: event.data.fromOrigin,
fromOrigin: event.data.toOrigin,
subpath: subpath,
+ nodeReady: false,
};
const resp = new ResponseImpl(event.data.id, client);
callback(resp, event.data.message);
});
- }
+ }
};
}
diff --git a/src/send.ts b/src/send.ts
index 2419978..0f2e78e 100644
--- a/src/send.ts
+++ b/src/send.ts
@@ -1,5 +1,5 @@
import { WindowMQOptions, WindowClient, Payload } from './common';
-import { GetOrigin, GetFromType, GetToType, GetUUID } from './utils';
+import { GetOrigin, GetFromType, GetToType, GetUUID, WaitForIframeLoad } from './utils';
export interface Sender {
send: (message: string) => Promise;
}
@@ -7,12 +7,12 @@ export interface Sender {
export class SenderImpl implements Sender {
client: WindowClient;
timeout: number;
-
resolver: (value: string | PromiseLike) => void;
rejecter: (reason?: any) => void;
constructor(route: string, node: Window | HTMLIFrameElement, options: WindowMQOptions) {
const toOrigin = GetOrigin(route);
+
this.client = {
node: node,
nodeTypeTo: GetToType(route),
@@ -20,6 +20,7 @@ export class SenderImpl implements Sender {
toOrigin: toOrigin,
fromOrigin: GetOrigin(window.location.href),
subpath: route.replace(toOrigin, ''),
+ nodeReady: false,
};
this.timeout = options?.timeout || 3000;
this.resolver = (value: string | PromiseLike) => {};
@@ -33,7 +34,7 @@ export class SenderImpl implements Sender {
if (this.client.node == null || this.client.node == undefined) {
return reject('Client Unhealthy');
}
- if (typeof window === 'undefined' || typeof document === 'undefined') {
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
// Resolve to null when imported server side. This makes the module
// safe to import in an isomorphic code base.
return resolve('');
@@ -51,7 +52,20 @@ export class SenderImpl implements Sender {
};
if (this.client.nodeTypeTo == 'iframe') {
const iframe = this.client.node as HTMLIFrameElement;
- iframe.contentWindow?.postMessage(payload, this.client.toOrigin);
+ //wait for iframe to load
+ if (this.client.nodeReady) {
+ iframe.contentWindow?.postMessage(payload, this.client.toOrigin);
+ } else {
+ WaitForIframeLoad(iframe, this.timeout).then(
+ () => {
+ this.client.nodeReady = true;
+ iframe.contentWindow?.postMessage(payload, this.client.toOrigin);
+ },
+ function (err) {
+ return reject('Client Timeout');
+ },
+ );
+ }
} else if (this.client.nodeTypeTo == 'parent') {
const win = this.client.node as Window;
win.postMessage(payload, this.client.toOrigin);
diff --git a/src/utils.ts b/src/utils.ts
index 3c7481b..e7f6447 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -80,3 +80,20 @@ export function ValidatePath(url: string): boolean {
return true;
}
+
+export function WaitForIframeLoad(iframe: HTMLIFrameElement, timeout: number): Promise {
+ return new Promise((resolve, reject) => {
+ const timeoutId = setTimeout(() => {
+ clearInterval(interval);
+ reject(new Error('Timeout exceeded'));
+ }, timeout);
+
+ const interval = setInterval(() => {
+ if (iframe.contentWindow) {
+ clearInterval(interval);
+ clearTimeout(timeoutId);
+ resolve();
+ }
+ }, 100);
+ });
+}