casement
Version:
An iFrame comms library for giving your app access to the outside world.
2 lines • 7.62 kB
JavaScript
/*! For license information please see casement.min.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.casement=t():e.casement=t()}(self,(()=>(()=>{"use strict";var e={d:(t,s)=>{for(var a in s)e.o(s,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:s[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{Inside:()=>a,Outside:()=>n});class s{constructor(){this.allowSend=!1,this.name="default",this.debug=!1,this.allowedDomain=""}loggy(...e){this.debug&&console.info(...e)}warny(...e){this.debug&&console.warn(...e)}capitalizeFirstLetter(e){return 0===e.length?e:e.charAt(0).toUpperCase()+e.slice(1)}sendAgnostic(e,t,s){const a=e?"inside":"outside",n=e?window.parent:this.iFrame.contentWindow;this.allowSend?(s||(s="message",this.loggy('Casement: No actionName was provided. Using default actionName "message".')),n.postMessage({type:"casement-message",from:a,name:this.name,message:t,actionName:s},this.allowedDomain),this.loggy("Casement: Sent message to inside window.")):this.warny(`Casement: Cannot send message from ${a} window. The other side has not yet loaded, or has not yet confirmed that it is ready.`)}requestAgnostic(e,t,s){const a=e?"inside":"outside",n=e?"outside":"inside",i=e?window.parent:this.iFrame.contentWindow;if(this.allowSend)return new Promise((e=>{const o=Math.random().toString(36).substring(2,15)+Math.random().toString(36).substring(2,15);i.postMessage({type:"casement-request",from:a,name:this.name,transmissionID:o,message:t,actionName:s},this.allowedDomain),this.loggy(`Casement: Sent request to ${n} window, ID ${o}.`);const d=s=>{!1!==this.domainCheck(s.origin)?(this.loggy(`Casement: Received response from ${n} window, ID ${o}.`),"casement-response"===s.data.type&&s.data.transmissionID===t.transmissionID&&s.data.name===this.name&&s.data.from===n?(window.removeEventListener("message",d),e(s.data.message),this.loggy(`Casement: Accepted response from ${n} window, ID ${o}.`)):this.warny(`Casement: Received response from ${n} window, but something about the response did not match the request. This is likely a bug.`,s)):this.warny(`Casement: Received response from ${n} window, but the origin (${s.origin}) did not match the allowed domain of ${this.allowedDomain}.`)};window.addEventListener("message",d),this.loggy(`Casement: Listening for response from ${n} window, ID ${o}.`)}));this.warny(`Casement: Cannot send request from ${a} window. The other side has not yet loaded, or has not yet confirmed that it is ready.`)}on(e,t){this.onMessage||(this.onMessage=[]),this.onMessage.push({name:e,callback:t}),this.loggy(`Casement: Registered handler for message "${e}".`)}handleIncomingAgnostic(e,t){const s=t?"inside":"outside",a=t?"outside":"inside",n=t?window.parent:this.iFrame.contentWindow;if(this.loggy("Casement: Incoming parser called.",e),!1!==this.domainCheck(e.origin))if(e.data.name===this.name)if(e.data.from===a)switch(e.data.type){case"casement-ready":this.handleReadinessAgnostic(e,t);break;case"casement-message":this.loggy(`Casement: Received categorized message from ${a} window.`);let i=!1;this.onMessage&&this.onMessage.forEach((t=>{t.name!==e.data.actionName&&"*"!==t.name||(this.loggy(`Casement: Accepted categorized message from ${a} window, actionName ${e.data.actionName}.`),t.callback(e.data.message),i=!0)})),i||this.warny(`Casement: Received a message from ${a} but no handler was set for action name ${e.data.actionName}. Make sure to use the ${this.capitalizeFirstLetter(s)}.on() method to set a handler for this request.`,e);break;case"casement-request":this.loggy(`Casement: Received request from ${a} window, ID ${e.data.transmissionID}.`),this.onMessage?n.postMessage({type:"casement-response",message:Promise.all(this.onMessage.map((t=>t.name===e.data.actionName?(this.loggy(`Casement: Accepted request from ${a} window, actionName ${e.data.actionName}.`),t.callback(e.data.message)):"*"===t.name?t.callback(e.data.message,e.data.actionName):void 0))),name:this.name,transmissionID:e.data.transmissionID,from:s},this.allowedDomain):this.warny(`Casement: Received a request from ${a} but no handler was set. Make sure to use the ${this.capitalizeFirstLetter(s)}.on() method to set a handler for this request.`)}else this.warny(`Casement: Received message appears to not come from ${a}. Not handling out of precaution.`,e);else this.warny(`Casement: Received message from ${a} window, but the name (${e.data.name}) did not match the expected name of ${this.name}.`,e);else this.warny(`Casement: Received message from ${a} window, but the origin (${e.origin}) did not match the allowed domain of ${this.allowedDomain}.`,e)}handleReadinessAgnostic(e,t){var s;const a=t?"inside":"outside",n=t?"outside":"inside",i=e.data.name;if(this.loggy(`Casement: ${a} readiness handler called.`),!1!==this.domainCheck(e.origin))if(i===this.name)switch(e.data.from){case"inside":this.loggy("Casement: Received ready message from inside window. Sending one back...",e),this.allowSend=!0,(null===(s=this.iFrame)||void 0===s?void 0:s.contentWindow)&&this.iFrame.contentWindow.postMessage({type:"casement-ready",from:"outside",name:this.name},this.allowedDomain),this.onReady&&this.onReady(this);break;case"outside":e.data.name===this.name&&this.loggy("Casement: Received ready message from outside window. Data channel is now open and both sides can send messages.",e),this.allowSend=!0,this.onReady&&this.onReady(this);break;default:this.loggy("Casement: Received a malformed ready message missing a 'from' property.",e)}else this.loggy(`Casement: Received ready message from ${a} window, but the name did not match the allowed name.`,e);else this.loggy(`Casement: Received ready message from ${n} window, but the origin did not match the allowed domain.`,e)}domainCheck(e){if("*"===this.allowedDomain)return!0;const t=new URL(e).origin;return t===new URL(this.allowedDomain).origin||t===window.location.origin}}class a extends s{loggy(...e){this.debug&&console.info(...e)}init(){window.addEventListener("message",(e=>{this.handleIncomingAgnostic(e,!0)})),this.loggy("Casement: Sending ready message to outside window and listening for responses."),window.parent.postMessage({type:"casement-ready",from:"inside",name:this.name},this.allowedDomain)}send(e,t){this.sendAgnostic(!0,e,t)}request(e,t){return this.requestAgnostic(!0,e,t)}constructor(e){super(),this.allowSend=!1,this.debug=!1,e.debug&&(this.debug=e.debug),this.loggy("Casement: Initializing inside window."),this.name=e.name,this.allowedDomain=e.allowedDomain,e.onReady&&(this.onReady=e.onReady),this.init()}}class n extends s{init(){window.addEventListener("message",(e=>{this.handleIncomingAgnostic(e,!1)})),this.loggy("Casement: Listening for ready message from inside window.")}send(e,t){this.sendAgnostic(!1,e,t)}request(e,t){return this.requestAgnostic(!1,e,t)}constructor(e){super(),this.allowSend=!1,this.debug=!1,e.debug&&(this.debug=e.debug),this.loggy("Casement: Outside instance created."),this.name=e.name,this.pageUrl=e.pageUrl||window.location.href,this.allowedDomain=new URL(this.pageUrl).origin,e.onReady&&(this.onReady=e.onReady),e.container?(this.container=e.container,this.iFrame=document.createElement("iframe"),this.iFrame.src=this.pageUrl,this.container.appendChild(this.iFrame)):e.iFrame&&(this.iFrame=e.iFrame),this.init()}}return t})()));