davidjbradshaw / iframe-resizer

Keep iFrames sized to their content.

Home Page:https://iframe-resizer.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Request: Support for complex messaging.

sethreidnz opened this issue · comments

Hi,

I was thinking of forking this and creating the functionality for passing full JSON objects back and forth rather than just the simple string objects. Am I missing something and this is already possible?

I was thinking that I could create this with another config value of "allow-complex" or something and that way it wouldn't require another plugin to do this with and would mean if you want legacy support then you simply use the old method..

What do you think? I am going to using this plugin pretty heavily in a project for a client currently and have basically written what I'm talking about in a seperate plugin I wrote before I came across what you have done.

Would be keen to discuss with you some of hte approaches we have been using to handle communications between windows using dispatching custom events based on the message data that is being sent. So that other scripts can selectively attach to these events and not have to worry about the internals workings on what'g going on. This allows the windows to comunicate in a safe way that also allows one window to call into another windows functions while being walled off from free reign cross frame execution.

Will what I have working as a plugin on top of your work tha I was thinking could partially be intergrated into a more "advanced mode" or something.

var WindowMessenger = (function () {
    /// <summary>
    /// Handles all of the window messaging functionality between Iframes on the page and the parent window
    /// </summary>    

    /*-----Private variables ----*/
    var trustedEventOrigin = '*';//set to wildcard to make this script work for example purpose.

    /*-----Public variables and methods----*/
    var me = {};

    me.MessageTypes = {
        IFrameSizer: '[iFrameSizer]'
    };

    me.AttachToMessageEvent = function (messageType, action) {
        /// <summary>
        /// Public interface to attach attach to the messsageType events
        /// </summary>
        /// <param name="messageType">The type of message that the calling script wants to attach to. E.G. 'IFrameSizer'</param>
        /// <param name="action">Function callback to attach to the event</param>

        switch (messageType) {
            case me.MessageTypes.IFrameSizer:
                window.document.addEventListener(me.MessageTypes.IFrameSizer, action);
                break;
            default:
                console.log('No event with type' + type + ' ');
                break;
        }

    };

    me.SendMessageToWindow = function(targetWindowObject,data, targetOrigin) {
        /// <summary>
        /// Sends a message via window.postMessage to the target window
        /// </summary>
        /// <param name="targetWindowObject">The window to send the message to</param>
        /// <param name="data">a JSON object representing the message. An attribute of messageType is required to be processed at the other end</param>
        /// <param name="targetOrigin">The target origin of the message use "*" for any target</param>

        if (targetWindowObject != window) {
            parent.postMessage(data, targetOrigin);
        }


    };

    /*--------------------------------------*/


    /*----------- Private methods ----------*/

    var dispatchEvents = function (event) {
        /// <summary>
        /// Creates events based on incoming messages and their data.type attributes
        /// </summary>

        var eventOrigin = me.getHostNameFromUrl(event.origin);

        // Do we trust the sender of this message
        if (eventOrigin !== trustedEventOrigin)
            return;

        var data = event.data;

        //check if custom eventType param exists and the event if from IFrameSizer
        if (!data.messageType && messageIsFromiFrameResizer(data)) {

            dispatchEvent(me.MessageTypes.IFrameSizer, data);

        } else {//if our own custom messages below template for new event dispatch           
            switch (data.messageType) {
            case me.MessageTypes.IFrameSizer:
                data = data;
                dispatchEvent(MessageTypes.MessageName, data);               
                break;
            }            
        }
    };      

    function messageIsFromiFrameResizer(data) {
        /// <summary>
        /// Uses the same test that iFrameSizer.js uses to check its messages
        /// </summary>
        /// <param name="data">The event.data to check</param>
        /// <returns type="">Boolean: true if the message is from iFrameSizer</returns>
        return me.MessageTypes.IFrameSizer === ('' + data).substr(0, me.MessageTypes.IFrameSizer.length); //''+Protects against non-string msg
    }

    function dispatchEvent(eventName, data) {
        /// <summary>
        /// Dispatches a custom event with the given name and data
        /// </summary>
        /// <param name="eventName">String: the event name</param>
        /// <param name="data">Object or string: the data object to pass as part of the event.(optional)</param>
        if (!data) {
            data = "";
        }
        var customEvent = new CustomEvent(eventName, { 'detail': data });
        window.document.dispatchEvent(customEvent);
    }

    me.stripTrailingSlash = function (string) {
        if (string.substr(-1) == '/') {
            return string.substr(0, string.length - 1);
        }
        return string;
    };

    me.getLocationAnchor = function (href) {
        var l = document.createElement("a");
        l.href = href;
        return l;
    };

    me.getHostNameFromUrl = function (href) {
        var locationAnchor = me.getLocationAnchor(href);
        return locationAnchor.hostname;
    };

    /*--------------------------------------*/

    //attach the dispatch events function to the post message event
    window.addEventListener('message', dispatchEvents, false, true);
    return me;
})();


Then you can attach to it like this:

WindowMessenger.AttachToMessageEvent(WindowMessenger.MessageTypes.IframeResizer, function(){});

Hi,

It's 2am here and I'm just back from a night out, so won't comment too much on your code just now. This was written to work along sided other messaging and both scripts, as you can see ignore anything not meant for them. The parentWindow.sendMessage() function was added to make the test scripts simpler and then exposed in the documentation as a lot of people who use this script needed help with deconflicting their own attempts to use window.postMessage().

One of my design goal has been to ensure that their is version independence between the host page script and the iFrame script, you can use v2.6.5 with v1.0.0 and it will still work, all be it with much less feature support.

I'm also inclined to think that writing a full blown router might be overkill for 99% of users of this. But as I said it's late here and I'm tired, so I should sleep on it.

Dave.

That said their could be a case for adding a parentIFrame.sendObject() method and this could be sent to an objectCallback() which you could then route however you like.

As I said before need to sleep on it.

That is really what I'm talking about just a more complex send object
option. I just included what I was doing as a use case and very very remote
possible join in. send/recieve object would be very useful and I'd happily
build that in next week some time (As I need to do it anyway).

But get some sleep and give me an email when you have time!

[image: 3sparks llc] contact@sethreid.co.nz

Seth Reid | Web Consultant/Developer

0221705966 | contact@sethreid.co.nz

On 6 December 2014 at 13:45, David J. Bradshaw notifications@github.com
wrote:

That said their could be a case for adding a parentIFrame.sendObject()
method and this could be sent to an objectCallback() which you could then
route however you like.

As I said before need to sleep on it.


Reply to this email directly or view it on GitHub
#129 (comment)
.

Having thought about this a bit more I think the simplest solution is to have the parentIFrame.sendMessage() method detect an object being past into it, then run it through JSON.stringify() and then send it as a new message type to the host page script.

The host page script and then detect the new message type, run JSON.parse() over it and then call messageCallback().

Doing it this way requires the smallest amount of work, supports all the target browsers and is transparent to the end user. The only downside of using JSON, is that it allows for slightly less data types to be transfered, but these are real edge cases.

https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/The_structured_clone_algorithm

Thinking a bit more we can just run everything through JSON.stringify() and JSON.parse() as it works with non-object data types, so it becomes two lines of extra code and an extra test case that way.

Released as V2.7.0.

Now some code like this should be included:

(from jstorage:)
// Break if no JSON support was found
if (typeof JSON.parse !== 'function' || typeof JSON.stringify !== 'function') {
throw new Error('No JSON support found, include //cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js to page');
}

So people include this to implement json support in older browsers.
https://github.com/douglascrockford/JSON-js

This is a breaking change in some older browsers...

I did think about that, but the JSON object is supported in IE8 up, which is the base level for this lib.

Yeah i just checked and noticed IE8 should support it natively.

Don't know if it worked on IE7 before this fix.

Nope, IE7 does not support postMessage.

Ok, should not break anything then 👍