/** * IFrame Message Routing Utility. * Author: Julien Lecomte * Copyright (c) 2007, Yahoo! Inc. All rights reserved. * Code licensed under the BSD License: * http://developer.yahoo.net/yui/license.txt * * @requires yahoo, event * @namespace YAHOO.util * @title IFrame Message Routing Utility */ /** * The CrossFrame singleton allows iframes to safely communicate even * if they are on different domains. This utility requires a proxy HTML * file (proxy.html) * * @class CrossFrame */ YAHOO.util.CrossFrame = (function () { var r1 = /^(((top|parent|frames\[((['"][a-zA-Z\d-_]*['"])|\d+)\]))(\.|$))+/; var r2 = /top|parent|frames\[(?:(?:['"][a-zA-Z]*['"])|\d+)\]/; function parseQueryString(s) { var r, a, p; r = {}; a = s.split('&'); for (i = 0; i < a.length; i++) { var index = a[i].indexOf('='); if (index > 0) { r[a[i].substr(0, index)] = unescape(a[i].substr(index + 1)); } } return r; } if (YAHOO.env.ua.opera) { // Opera does not allow reading any property (including parent, frames) // if the domain of the caller and the domain of the target window do // not match. We work around this by chaining calls, and using Opera's // postMessage function... document.addEventListener("message", function (evt) { var o = parseQueryString(evt.data); if (YAHOO.lang.hasOwnProperty(o, "target") && YAHOO.lang.hasOwnProperty(o, "message") && YAHOO.lang.hasOwnProperty(o, "domain") && YAHOO.lang.hasOwnProperty(o, "uri")) { if (o.target.length > 0) { // Send the message to the next document in the chain. YAHOO.util.CrossFrame.send(null, o.target, o.message, o.domain, o.uri); } else { // Let the application know a message has been received. YAHOO.util.CrossFrame.onMessageEvent.fire(o.message, o.domain, o.uri); } } }, false); } return { /** * Fired when a message is received. * * @event onMessageEvent */ onMessageEvent: new YAHOO.util.CustomEvent("onMessage"), /** * Sends a message to an iframe, using the specified proxy. * * @method send * @param {string} proxy Complete path to the proxy file. * @param {string} target Target iframe e.g: parent.frames["foo"] * @param {string} message The message to send. */ send: function (proxy, target, message) { var m, t, d, u, s, el; // Match things like parent.frames["aaa"].top.frames[0].frames['bbb'] if (!r1.test(target)) { throw new Error("Invalid target: " + target); } if (YAHOO.env.ua.opera) { // Opera is the only A-grade browser that does not allow // reading properties like parent.frames when this document and // its parent are on separate domains. The solution is to use // the parent as a "hub" to route messages to the appropriate // IFrame, and use the Opera's postMessage function... m = r2.exec(target); // safe to eval... t = eval(m[0]).document; // Remove one element from the target chain. target = target.substr(m[0].length + 1); // Compose the message... d = arguments.length > 3 ? arguments[3] : document.domain; u = arguments.length > 4 ? arguments[4] : location.href; s = "target=" + escape(target) + "&message=" + escape(message) + "&domain=" + escape(d) + "&uri=" + escape(u); // ...and send it! t.postMessage(s); } else { // Create a new hidden iframe. el = document.createElement("iframe"); el.style.position = "absolute"; el.style.visibility = "hidden"; el.style.top = el.style.left = "0"; el.style.width = el.style.height = "0"; // Listen for the onload event. YAHOO.util.Event.addListener(el, "load", function () { // First, remove the event listener or the iframe // we intend to discard will not be freed... YAHOO.util.Event.removeListener(this, "load", arguments.callee); // Discard the iframe... setTimeout(function () { document.body.removeChild(el); }, 1000); }); // Compose the message... s = "target=" + escape(target) + "&message=" + escape(message) + "&domain=" + escape(document.domain) + "&uri=" + escape(location.href); // Set its src first... el.src = proxy + "#" + s; // ...and then append it to the body of the document. document.body.appendChild(el); } } }; })();