Updated Asterix SDK + Easy XDM
diff --git a/asterix-examples/src/main/resources/js/easyXDM/tests/easyTest.css b/asterix-examples/src/main/resources/js/easyXDM/tests/easyTest.css
new file mode 100644
index 0000000..0df87e3
--- /dev/null
+++ b/asterix-examples/src/main/resources/js/easyXDM/tests/easyTest.css
@@ -0,0 +1,14 @@
+.easyTest_messages {
+}
+
+.easyTest_error {
+ color: red;
+}
+
+.easyTest_success {
+ color: green;
+}
+
+.easyTest_msg {
+ color: black;
+}
\ No newline at end of file
diff --git a/asterix-examples/src/main/resources/js/easyXDM/tests/easyTest.js b/asterix-examples/src/main/resources/js/easyXDM/tests/easyTest.js
new file mode 100644
index 0000000..1da4812
--- /dev/null
+++ b/asterix-examples/src/main/resources/js/easyXDM/tests/easyTest.js
@@ -0,0 +1,262 @@
+var easyTest = (function(){
+ var _messages;
+ var _start;
+ var MessageType = {
+ Error: 1,
+ Success: 2,
+ Info: 3
+ };
+
+ /**
+ * Logs the message to the body
+ * @param {String} msg The message to displey
+ * @param {MessageType} type The messagetype
+ */
+ function _log(msg, type){
+ var el = _messages.appendChild(document.createElement("div"));
+ el.innerHTML = msg;
+ _messages.scrollTop = _messages.scrollHeight;
+ switch (type) {
+ case MessageType.Error:
+ el.className = "easyTest_error";
+ break;
+ case MessageType.Success:
+ el.className = "easyTest_success";
+ break;
+ default:
+ el.className = "easyTest_msg";
+ break;
+ }
+ }
+
+ var Assert = {
+ // Type checks
+ isTypeOf: function(type, obj){
+ return typeof obj === type;
+ },
+ isInstanceOf: function(type, obj){
+ return obj instanceof type;
+ },
+ isString: function(obj){
+ return this.isTypeOf("string", obj);
+ },
+ isNumber: function(obj){
+ return this.isTypeOf("number", obj);
+ },
+ isObject: function(obj){
+ return this.isTypeOf("object", obj);
+ },
+ isBoolean: function(obj){
+ return this.isTypeOf("boolean", obj);
+ },
+ isFunction: function(obj){
+ return this.isTypeOf("function", obj);
+ },
+ // Equality
+ areEqual: function(a, b){
+ return a == b;
+ },
+ areNotEqual: function(a, b){
+ return a != b;
+ },
+ // Identical
+ areSame: function(a, b){
+ return a === b;
+ },
+ areNotSame: function(a, b){
+ return a !== b;
+ }
+
+ };
+
+ function Test(test, fn){
+ var _scope, _steps = test.steps, _step, _stepIndex = 0;
+ var _timer, _runStep, _startedAt, _stepStartedAt;
+
+ /**
+ * Clean up and tear down the test.<br/>
+ * Calls back to notify that the test is complete
+ */
+ function _endTest(){
+ // Tear down the test
+ if (test.tearDown) {
+ try {
+ test.tearDown.call(_scope);
+ }
+ catch (ex) {
+ _log("Teardown '" + ex.message + "(" + ex.fileName + ", " + ex.lineNumber + ")" + "'", MessageType.Error);
+ }
+ }
+ for (var key in _scope) {
+ if (_scope.hasOwnProperty(key)) {
+ delete _scope[key];
+ }
+ }
+
+ // Call back
+ fn();
+ }
+
+ /**
+ * Used to notify the framework of the result of the test
+ * @param {String} name The name of the test
+ * @param {Boolean} result The result of the test
+ * @param {String} reason An optional reason why the test returned the result
+ */
+ function _notifyResult(name, result, reason){
+ var now = new Date().getTime();
+ var testsetTime = now - _start.getTime();
+ var testTime = now - _startedAt.getTime();
+ var stepTime = now - _stepStartedAt.getTime();
+
+ var times = testsetTime + "ms, " + testTime + "ms, " + stepTime + "ms - ";
+ if (result) {
+ _log(times + name + " succeeded! " + (reason || ""), MessageType.Success);
+ }
+ else {
+ _log(times + name + " failed! " + (reason || ""), MessageType.Error);
+ if (test.failedMessage) {
+ _log(test.failedMessage, MessageType.Info);
+ }
+ }
+ // Go to next step
+ if (result) {
+ _stepIndex++;
+ window.setTimeout(function(){
+ _runStep();
+ }, 0);
+ }
+ else {
+ _endTest();
+ }
+ }
+
+
+ /**
+ * Runs through the test step
+ */
+ _runStep = function(){
+ if (_stepIndex < _steps.length) {
+ // We still have steps to run
+ _step = _steps[_stepIndex];
+ _stepStartedAt = new Date();
+ if (_step.timeout) {
+ // This an asynchronous test
+ _timer = window.setTimeout(function(){
+ _notifyResult(_step.name, false, "Failed due to timeout.");
+ }, _step.timeout);
+ try {
+ _step.run.call(_scope);
+ }
+ catch (ex) {
+ //If it fails we cancel the timeout
+ window.clearTimeout(_timer);
+ _notifyResult(_step.name, false, "'" + ex.message + "(" + ex.fileName + ", " + ex.lineNumber + ")" + "'");
+ }
+ }
+ else {
+ // This is a synchronous test
+ try {
+ var result = _step.run.call(_scope);
+ _notifyResult(_step.name, result);
+ }
+ catch (ex) {
+ _notifyResult(_step.name, false, "'" + ex.message + "(" + ex.fileName + ", " + ex.lineNumber + ")" + "'");
+ }
+ }
+ }
+ else {
+ _endTest();
+ }
+ };
+
+ return {
+ /**
+ * Runs the test.
+ * Will first try to execute the setup method before continuing the steps
+ */
+ run: function(){
+ var excuse;
+ if (test.runIf) {
+ excuse = test.runIf();
+ }
+ if (excuse) {
+ _log("Skipping test ' " + test.name + "'. " + excuse);
+ fn();
+ }
+ else {
+ _log("Running test '" + test.name + "'");
+ _scope = {
+ Assert: Assert,
+ log: _log,
+ notifyResult: function(result){
+ window.clearTimeout(_timer);
+ _notifyResult(_step.name, result);
+ }
+ };
+ if (test.setUp) {
+ // Setup the test
+ try {
+ test.setUp.call(_scope);
+ _log("Setup succeeded", MessageType.Success);
+ }
+ catch (ex) {
+ _log("Setup failed", MessageType.Error);
+ }
+ }
+ _startedAt = new Date();
+ _runStep();
+ }
+ }
+ };
+ }
+
+ return {
+ /**
+ * Runs through all the tests
+ * @param {Array} tests The tests to run
+ */
+ test: function(testset){
+ var tests = [], testConfig, i = testset.length, test;
+
+ // Prepare the messaging facilities
+ if (!_messages) {
+ _messages = document.createElement("div");
+ _messages.className = "easyTest_messages";
+ (document.getElementById("messages") || document.body).appendChild(_messages);
+ }
+ else {
+ _messages.innerHTML = "";
+ }
+
+ // Convert the testset
+ while (i--) {
+ testConfig = testset[i];
+ if (!testConfig.steps) {
+ // Convert a single step test to a proper test
+ testConfig = {
+ steps: testConfig
+ };
+ }
+
+ tests.push(new Test(testConfig, function(){
+ // Get the next test to run
+ test = tests.pop();
+ if (test) {
+ // This is needed to avoid a strange bug in Opera,
+ window.setTimeout(function(){
+ test.run();
+ }, 0);
+ }
+ else {
+ // No more tests to run
+ _log("Test run complete", MessageType.Info);
+ }
+ }));
+ }
+ // Start the first test
+ _start = new Date();
+ tests.pop().run();
+ }
+ };
+}());
diff --git a/asterix-examples/src/main/resources/js/easyXDM/tests/easyXDM.debug.js b/asterix-examples/src/main/resources/js/easyXDM/tests/easyXDM.debug.js
new file mode 100644
index 0000000..c1c614a
--- /dev/null
+++ b/asterix-examples/src/main/resources/js/easyXDM/tests/easyXDM.debug.js
@@ -0,0 +1,2848 @@
+(function (window, document, location, setTimeout, decodeURIComponent, encodeURIComponent) {
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global JSON, XMLHttpRequest, window, escape, unescape, ActiveXObject */
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+var global = this;
+var channelId = Math.floor(Math.random() * 10000); // randomize the initial id in case of multiple closures loaded
+var emptyFn = Function.prototype;
+var reURI = /^((http.?:)\/\/([^:\/\s]+)(:\d+)*)/; // returns groups for protocol (2), domain (3) and port (4)
+var reParent = /[\-\w]+\/\.\.\//; // matches a foo/../ expression
+var reDoubleSlash = /([^:])\/\//g; // matches // anywhere but in the protocol
+var namespace = ""; // stores namespace under which easyXDM object is stored on the page (empty if object is global)
+var easyXDM = {};
+var _easyXDM = window.easyXDM; // map over global easyXDM in case of overwrite
+var IFRAME_PREFIX = "easyXDM_";
+var HAS_NAME_PROPERTY_BUG;
+var useHash = false; // whether to use the hash over the query
+var flashVersion; // will be set if using flash
+var HAS_FLASH_THROTTLED_BUG;
+var _trace = emptyFn;
+
+
+// http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
+function isHostMethod(object, property){
+ var t = typeof object[property];
+ return t == 'function' ||
+ (!!(t == 'object' && object[property])) ||
+ t == 'unknown';
+}
+
+function isHostObject(object, property){
+ return !!(typeof(object[property]) == 'object' && object[property]);
+}
+
+// end
+
+// http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
+function isArray(o){
+ return Object.prototype.toString.call(o) === '[object Array]';
+}
+
+// end
+function hasFlash(){
+ var name = "Shockwave Flash", mimeType = "application/x-shockwave-flash";
+
+ if (!undef(navigator.plugins) && typeof navigator.plugins[name] == "object") {
+ // adapted from the swfobject code
+ var description = navigator.plugins[name].description;
+ if (description && !undef(navigator.mimeTypes) && navigator.mimeTypes[mimeType] && navigator.mimeTypes[mimeType].enabledPlugin) {
+ flashVersion = description.match(/\d+/g);
+ }
+ }
+ if (!flashVersion) {
+ var flash;
+ try {
+ flash = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
+ flashVersion = Array.prototype.slice.call(flash.GetVariable("$version").match(/(\d+),(\d+),(\d+),(\d+)/), 1);
+ flash = null;
+ }
+ catch (notSupportedException) {
+ }
+ }
+ if (!flashVersion) {
+ return false;
+ }
+ var major = parseInt(flashVersion[0], 10), minor = parseInt(flashVersion[1], 10);
+ HAS_FLASH_THROTTLED_BUG = major > 9 && minor > 0;
+ return true;
+}
+
+/*
+ * Cross Browser implementation for adding and removing event listeners.
+ */
+var on, un;
+if (isHostMethod(window, "addEventListener")) {
+ on = function(target, type, listener){
+ _trace("adding listener " + type);
+ target.addEventListener(type, listener, false);
+ };
+ un = function(target, type, listener){
+ _trace("removing listener " + type);
+ target.removeEventListener(type, listener, false);
+ };
+}
+else if (isHostMethod(window, "attachEvent")) {
+ on = function(object, sEvent, fpNotify){
+ _trace("adding listener " + sEvent);
+ object.attachEvent("on" + sEvent, fpNotify);
+ };
+ un = function(object, sEvent, fpNotify){
+ _trace("removing listener " + sEvent);
+ object.detachEvent("on" + sEvent, fpNotify);
+ };
+}
+else {
+ throw new Error("Browser not supported");
+}
+
+/*
+ * Cross Browser implementation of DOMContentLoaded.
+ */
+var domIsReady = false, domReadyQueue = [], readyState;
+if ("readyState" in document) {
+ // If browser is WebKit-powered, check for both 'loaded' (legacy browsers) and
+ // 'interactive' (HTML5 specs, recent WebKit builds) states.
+ // https://bugs.webkit.org/show_bug.cgi?id=45119
+ readyState = document.readyState;
+ domIsReady = readyState == "complete" || (~ navigator.userAgent.indexOf('AppleWebKit/') && (readyState == "loaded" || readyState == "interactive"));
+}
+else {
+ // If readyState is not supported in the browser, then in order to be able to fire whenReady functions apropriately
+ // when added dynamically _after_ DOM load, we have to deduce wether the DOM is ready or not.
+ // We only need a body to add elements to, so the existence of document.body is enough for us.
+ domIsReady = !!document.body;
+}
+
+function dom_onReady(){
+ if (domIsReady) {
+ return;
+ }
+ domIsReady = true;
+ _trace("firing dom_onReady");
+ for (var i = 0; i < domReadyQueue.length; i++) {
+ domReadyQueue[i]();
+ }
+ domReadyQueue.length = 0;
+}
+
+
+if (!domIsReady) {
+ if (isHostMethod(window, "addEventListener")) {
+ on(document, "DOMContentLoaded", dom_onReady);
+ }
+ else {
+ on(document, "readystatechange", function(){
+ if (document.readyState == "complete") {
+ dom_onReady();
+ }
+ });
+ if (document.documentElement.doScroll && window === top) {
+ var doScrollCheck = function(){
+ if (domIsReady) {
+ return;
+ }
+ // http://javascript.nwbox.com/IEContentLoaded/
+ try {
+ document.documentElement.doScroll("left");
+ }
+ catch (e) {
+ setTimeout(doScrollCheck, 1);
+ return;
+ }
+ dom_onReady();
+ };
+ doScrollCheck();
+ }
+ }
+
+ // A fallback to window.onload, that will always work
+ on(window, "load", dom_onReady);
+}
+/**
+ * This will add a function to the queue of functions to be run once the DOM reaches a ready state.
+ * If functions are added after this event then they will be executed immediately.
+ * @param {function} fn The function to add
+ * @param {Object} scope An optional scope for the function to be called with.
+ */
+function whenReady(fn, scope){
+ if (domIsReady) {
+ fn.call(scope);
+ return;
+ }
+ domReadyQueue.push(function(){
+ fn.call(scope);
+ });
+}
+
+/**
+ * Returns an instance of easyXDM from the parent window with
+ * respect to the namespace.
+ *
+ * @return An instance of easyXDM (in the parent window)
+ */
+function getParentObject(){
+ var obj = parent;
+ if (namespace !== "") {
+ for (var i = 0, ii = namespace.split("."); i < ii.length; i++) {
+ if (!obj) {
+ throw new Error(ii.slice(0, i + 1).join('.') + ' is not an object');
+ }
+ obj = obj[ii[i]];
+ }
+ }
+ if (!obj || !obj.easyXDM) {
+ throw new Error('Could not find easyXDM in parent.' + namespace);
+ }
+ return obj.easyXDM;
+}
+
+/**
+ * Removes easyXDM variable from the global scope. It also returns control
+ * of the easyXDM variable to whatever code used it before.
+ *
+ * @param {String} ns A string representation of an object that will hold
+ * an instance of easyXDM.
+ * @return An instance of easyXDM
+ */
+function noConflict(ns){
+ if (typeof ns != "string" || !ns) {
+ throw new Error('namespace must be a non-empty string');
+ }
+ _trace("Settings namespace to '" + ns + "'");
+
+ window.easyXDM = _easyXDM;
+ namespace = ns;
+ if (namespace) {
+ IFRAME_PREFIX = "easyXDM_" + namespace.replace(".", "_") + "_";
+ }
+ return easyXDM;
+}
+
+/*
+ * Methods for working with URLs
+ */
+/**
+ * Get the domain name from a url.
+ * @param {String} url The url to extract the domain from.
+ * @return The domain part of the url.
+ * @type {String}
+ */
+function getDomainName(url){
+ if (!url) {
+ throw new Error("url is undefined or empty");
+ }
+ return url.match(reURI)[3];
+}
+
+/**
+ * Get the port for a given URL, or "" if none
+ * @param {String} url The url to extract the port from.
+ * @return The port part of the url.
+ * @type {String}
+ */
+function getPort(url){
+ if (!url) {
+ throw new Error("url is undefined or empty");
+ }
+ return url.match(reURI)[4] || "";
+}
+
+/**
+ * Returns a string containing the schema, domain and if present the port
+ * @param {String} url The url to extract the location from
+ * @return {String} The location part of the url
+ */
+function getLocation(url){
+ if (!url) {
+ throw new Error("url is undefined or empty");
+ }
+ if (/^file/.test(url)) {
+ throw new Error("The file:// protocol is not supported");
+ }
+ var m = url.toLowerCase().match(reURI);
+ var proto = m[2], domain = m[3], port = m[4] || "";
+ if ((proto == "http:" && port == ":80") || (proto == "https:" && port == ":443")) {
+ port = "";
+ }
+ return proto + "//" + domain + port;
+}
+
+/**
+ * Resolves a relative url into an absolute one.
+ * @param {String} url The path to resolve.
+ * @return {String} The resolved url.
+ */
+function resolveUrl(url){
+ if (!url) {
+ throw new Error("url is undefined or empty");
+ }
+
+ // replace all // except the one in proto with /
+ url = url.replace(reDoubleSlash, "$1/");
+
+ // If the url is a valid url we do nothing
+ if (!url.match(/^(http||https):\/\//)) {
+ // If this is a relative path
+ var path = (url.substring(0, 1) === "/") ? "" : location.pathname;
+ if (path.substring(path.length - 1) !== "/") {
+ path = path.substring(0, path.lastIndexOf("/") + 1);
+ }
+
+ url = location.protocol + "//" + location.host + path + url;
+ }
+
+ // reduce all 'xyz/../' to just ''
+ while (reParent.test(url)) {
+ url = url.replace(reParent, "");
+ }
+
+ _trace("resolved url '" + url + "'");
+ return url;
+}
+
+/**
+ * Appends the parameters to the given url.<br/>
+ * The base url can contain existing query parameters.
+ * @param {String} url The base url.
+ * @param {Object} parameters The parameters to add.
+ * @return {String} A new valid url with the parameters appended.
+ */
+function appendQueryParameters(url, parameters){
+ if (!parameters) {
+ throw new Error("parameters is undefined or null");
+ }
+
+ var hash = "", indexOf = url.indexOf("#");
+ if (indexOf !== -1) {
+ hash = url.substring(indexOf);
+ url = url.substring(0, indexOf);
+ }
+ var q = [];
+ for (var key in parameters) {
+ if (parameters.hasOwnProperty(key)) {
+ q.push(key + "=" + encodeURIComponent(parameters[key]));
+ }
+ }
+ return url + (useHash ? "#" : (url.indexOf("?") == -1 ? "?" : "&")) + q.join("&") + hash;
+}
+
+
+// build the query object either from location.query, if it contains the xdm_e argument, or from location.hash
+var query = (function(input){
+ input = input.substring(1).split("&");
+ var data = {}, pair, i = input.length;
+ while (i--) {
+ pair = input[i].split("=");
+ data[pair[0]] = decodeURIComponent(pair[1]);
+ }
+ return data;
+}(/xdm_e=/.test(location.search) ? location.search : location.hash));
+
+/*
+ * Helper methods
+ */
+/**
+ * Helper for checking if a variable/property is undefined
+ * @param {Object} v The variable to test
+ * @return {Boolean} True if the passed variable is undefined
+ */
+function undef(v){
+ return typeof v === "undefined";
+}
+
+/**
+ * A safe implementation of HTML5 JSON. Feature testing is used to make sure the implementation works.
+ * @return {JSON} A valid JSON conforming object, or null if not found.
+ */
+var getJSON = function(){
+ var cached = {};
+ var obj = {
+ a: [1, 2, 3]
+ }, json = "{\"a\":[1,2,3]}";
+
+ if (typeof JSON != "undefined" && typeof JSON.stringify === "function" && JSON.stringify(obj).replace((/\s/g), "") === json) {
+ // this is a working JSON instance
+ return JSON;
+ }
+ if (Object.toJSON) {
+ if (Object.toJSON(obj).replace((/\s/g), "") === json) {
+ // this is a working stringify method
+ cached.stringify = Object.toJSON;
+ }
+ }
+
+ if (typeof String.prototype.evalJSON === "function") {
+ obj = json.evalJSON();
+ if (obj.a && obj.a.length === 3 && obj.a[2] === 3) {
+ // this is a working parse method
+ cached.parse = function(str){
+ return str.evalJSON();
+ };
+ }
+ }
+
+ if (cached.stringify && cached.parse) {
+ // Only memoize the result if we have valid instance
+ getJSON = function(){
+ return cached;
+ };
+ return cached;
+ }
+ return null;
+};
+
+/**
+ * Applies properties from the source object to the target object.<br/>
+ * @param {Object} target The target of the properties.
+ * @param {Object} source The source of the properties.
+ * @param {Boolean} noOverwrite Set to True to only set non-existing properties.
+ */
+function apply(destination, source, noOverwrite){
+ var member;
+ for (var prop in source) {
+ if (source.hasOwnProperty(prop)) {
+ if (prop in destination) {
+ member = source[prop];
+ if (typeof member === "object") {
+ apply(destination[prop], member, noOverwrite);
+ }
+ else if (!noOverwrite) {
+ destination[prop] = source[prop];
+ }
+ }
+ else {
+ destination[prop] = source[prop];
+ }
+ }
+ }
+ return destination;
+}
+
+// This tests for the bug in IE where setting the [name] property using javascript causes the value to be redirected into [submitName].
+function testForNamePropertyBug(){
+ var form = document.body.appendChild(document.createElement("form")), input = form.appendChild(document.createElement("input"));
+ input.name = IFRAME_PREFIX + "TEST" + channelId; // append channelId in order to avoid caching issues
+ HAS_NAME_PROPERTY_BUG = input !== form.elements[input.name];
+ document.body.removeChild(form);
+ _trace("HAS_NAME_PROPERTY_BUG: " + HAS_NAME_PROPERTY_BUG);
+}
+
+/**
+ * Creates a frame and appends it to the DOM.
+ * @param config {object} This object can have the following properties
+ * <ul>
+ * <li> {object} prop The properties that should be set on the frame. This should include the 'src' property.</li>
+ * <li> {object} attr The attributes that should be set on the frame.</li>
+ * <li> {DOMElement} container Its parent element (Optional).</li>
+ * <li> {function} onLoad A method that should be called with the frames contentWindow as argument when the frame is fully loaded. (Optional)</li>
+ * </ul>
+ * @return The frames DOMElement
+ * @type DOMElement
+ */
+function createFrame(config){
+ _trace("creating frame: " + config.props.src);
+ if (undef(HAS_NAME_PROPERTY_BUG)) {
+ testForNamePropertyBug();
+ }
+ var frame;
+ // This is to work around the problems in IE6/7 with setting the name property.
+ // Internally this is set as 'submitName' instead when using 'iframe.name = ...'
+ // This is not required by easyXDM itself, but is to facilitate other use cases
+ if (HAS_NAME_PROPERTY_BUG) {
+ frame = document.createElement("<iframe name=\"" + config.props.name + "\"/>");
+ }
+ else {
+ frame = document.createElement("IFRAME");
+ frame.name = config.props.name;
+ }
+
+ frame.id = frame.name = config.props.name;
+ delete config.props.name;
+
+ if (typeof config.container == "string") {
+ config.container = document.getElementById(config.container);
+ }
+
+ if (!config.container) {
+ // This needs to be hidden like this, simply setting display:none and the like will cause failures in some browsers.
+ apply(frame.style, {
+ position: "absolute",
+ top: "-2000px",
+ // Avoid potential horizontal scrollbar
+ left: "0px"
+ });
+ config.container = document.body;
+ }
+
+ // HACK: IE cannot have the src attribute set when the frame is appended
+ // into the container, so we set it to "javascript:false" as a
+ // placeholder for now. If we left the src undefined, it would
+ // instead default to "about:blank", which causes SSL mixed-content
+ // warnings in IE6 when on an SSL parent page.
+ var src = config.props.src;
+ config.props.src = "javascript:false";
+
+ // transfer properties to the frame
+ apply(frame, config.props);
+
+ frame.border = frame.frameBorder = 0;
+ frame.allowTransparency = true;
+ config.container.appendChild(frame);
+
+ if (config.onLoad) {
+ on(frame, "load", config.onLoad);
+ }
+
+ // set the frame URL to the proper value (we previously set it to
+ // "javascript:false" to work around the IE issue mentioned above)
+ if(config.usePost) {
+ var form = config.container.appendChild(document.createElement('form')), input;
+ form.target = frame.name;
+ form.action = src;
+ form.method = 'POST';
+ if (typeof(config.usePost) === 'object') {
+ for (var i in config.usePost) {
+ if (config.usePost.hasOwnProperty(i)) {
+ if (HAS_NAME_PROPERTY_BUG) {
+ input = document.createElement('<input name="' + i + '"/>');
+ } else {
+ input = document.createElement("INPUT");
+ input.name = i;
+ }
+ input.value = config.usePost[i];
+ form.appendChild(input);
+ }
+ }
+ }
+ form.submit();
+ form.parentNode.removeChild(form);
+ } else {
+ frame.src = src;
+ }
+ config.props.src = src;
+
+ return frame;
+}
+
+/**
+ * Check whether a domain is allowed using an Access Control List.
+ * The ACL can contain * and ? as wildcards, or can be regular expressions.
+ * If regular expressions they need to begin with ^ and end with $.
+ * @param {Array/String} acl The list of allowed domains
+ * @param {String} domain The domain to test.
+ * @return {Boolean} True if the domain is allowed, false if not.
+ */
+function checkAcl(acl, domain){
+ // normalize into an array
+ if (typeof acl == "string") {
+ acl = [acl];
+ }
+ var re, i = acl.length;
+ while (i--) {
+ re = acl[i];
+ re = new RegExp(re.substr(0, 1) == "^" ? re : ("^" + re.replace(/(\*)/g, ".$1").replace(/\?/g, ".") + "$"));
+ if (re.test(domain)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Functions related to stacks
+ */
+/**
+ * Prepares an array of stack-elements suitable for the current configuration
+ * @param {Object} config The Transports configuration. See easyXDM.Socket for more.
+ * @return {Array} An array of stack-elements with the TransportElement at index 0.
+ */
+function prepareTransportStack(config){
+ var protocol = config.protocol, stackEls;
+ config.isHost = config.isHost || undef(query.xdm_p);
+ useHash = config.hash || false;
+ _trace("preparing transport stack");
+
+ if (!config.props) {
+ config.props = {};
+ }
+ if (!config.isHost) {
+ _trace("using parameters from query");
+ config.channel = query.xdm_c.replace(/["'<>\\]/g, "");
+ config.secret = query.xdm_s;
+ config.remote = query.xdm_e.replace(/["'<>\\]/g, "");
+ ;
+ protocol = query.xdm_p;
+ if (config.acl && !checkAcl(config.acl, config.remote)) {
+ throw new Error("Access denied for " + config.remote);
+ }
+ }
+ else {
+ config.remote = resolveUrl(config.remote);
+ config.channel = config.channel || "default" + channelId++;
+ config.secret = Math.random().toString(16).substring(2);
+ if (undef(protocol)) {
+ if (getLocation(location.href) == getLocation(config.remote)) {
+ /*
+ * Both documents has the same origin, lets use direct access.
+ */
+ protocol = "4";
+ }
+ else if (isHostMethod(window, "postMessage") || isHostMethod(document, "postMessage")) {
+ /*
+ * This is supported in IE8+, Firefox 3+, Opera 9+, Chrome 2+ and Safari 4+
+ */
+ protocol = "1";
+ }
+ else if (config.swf && isHostMethod(window, "ActiveXObject") && hasFlash()) {
+ /*
+ * The Flash transport superseedes the NixTransport as the NixTransport has been blocked by MS
+ */
+ protocol = "6";
+ }
+ else if (navigator.product === "Gecko" && "frameElement" in window && navigator.userAgent.indexOf('WebKit') == -1) {
+ /*
+ * This is supported in Gecko (Firefox 1+)
+ */
+ protocol = "5";
+ }
+ else if (config.remoteHelper) {
+ /*
+ * This is supported in all browsers that retains the value of window.name when
+ * navigating from one domain to another, and where parent.frames[foo] can be used
+ * to get access to a frame from the same domain
+ */
+ protocol = "2";
+ }
+ else {
+ /*
+ * This is supported in all browsers where [window].location is writable for all
+ * The resize event will be used if resize is supported and the iframe is not put
+ * into a container, else polling will be used.
+ */
+ protocol = "0";
+ }
+ _trace("selecting protocol: " + protocol);
+ }
+ else {
+ _trace("using protocol: " + protocol);
+ }
+ }
+ config.protocol = protocol; // for conditional branching
+ switch (protocol) {
+ case "0":// 0 = HashTransport
+ apply(config, {
+ interval: 100,
+ delay: 2000,
+ useResize: true,
+ useParent: false,
+ usePolling: false
+ }, true);
+ if (config.isHost) {
+ if (!config.local) {
+ _trace("looking for image to use as local");
+ // If no local is set then we need to find an image hosted on the current domain
+ var domain = location.protocol + "//" + location.host, images = document.body.getElementsByTagName("img"), image;
+ var i = images.length;
+ while (i--) {
+ image = images[i];
+ if (image.src.substring(0, domain.length) === domain) {
+ config.local = image.src;
+ break;
+ }
+ }
+ if (!config.local) {
+ _trace("no image found, defaulting to using the window");
+ // If no local was set, and we are unable to find a suitable file, then we resort to using the current window
+ config.local = window;
+ }
+ }
+
+ var parameters = {
+ xdm_c: config.channel,
+ xdm_p: 0
+ };
+
+ if (config.local === window) {
+ // We are using the current window to listen to
+ config.usePolling = true;
+ config.useParent = true;
+ config.local = location.protocol + "//" + location.host + location.pathname + location.search;
+ parameters.xdm_e = config.local;
+ parameters.xdm_pa = 1; // use parent
+ }
+ else {
+ parameters.xdm_e = resolveUrl(config.local);
+ }
+
+ if (config.container) {
+ config.useResize = false;
+ parameters.xdm_po = 1; // use polling
+ }
+ config.remote = appendQueryParameters(config.remote, parameters);
+ }
+ else {
+ apply(config, {
+ channel: query.xdm_c,
+ remote: query.xdm_e,
+ useParent: !undef(query.xdm_pa),
+ usePolling: !undef(query.xdm_po),
+ useResize: config.useParent ? false : config.useResize
+ });
+ }
+ stackEls = [new easyXDM.stack.HashTransport(config), new easyXDM.stack.ReliableBehavior({}), new easyXDM.stack.QueueBehavior({
+ encode: true,
+ maxLength: 4000 - config.remote.length
+ }), new easyXDM.stack.VerifyBehavior({
+ initiate: config.isHost
+ })];
+ break;
+ case "1":
+ stackEls = [new easyXDM.stack.PostMessageTransport(config)];
+ break;
+ case "2":
+ config.remoteHelper = resolveUrl(config.remoteHelper);
+ stackEls = [new easyXDM.stack.NameTransport(config), new easyXDM.stack.QueueBehavior(), new easyXDM.stack.VerifyBehavior({
+ initiate: config.isHost
+ })];
+ break;
+ case "3":
+ stackEls = [new easyXDM.stack.NixTransport(config)];
+ break;
+ case "4":
+ stackEls = [new easyXDM.stack.SameOriginTransport(config)];
+ break;
+ case "5":
+ stackEls = [new easyXDM.stack.FrameElementTransport(config)];
+ break;
+ case "6":
+ if (!flashVersion) {
+ hasFlash();
+ }
+ stackEls = [new easyXDM.stack.FlashTransport(config)];
+ break;
+ }
+ // this behavior is responsible for buffering outgoing messages, and for performing lazy initialization
+ stackEls.push(new easyXDM.stack.QueueBehavior({
+ lazy: config.lazy,
+ remove: true
+ }));
+ return stackEls;
+}
+
+/**
+ * Chains all the separate stack elements into a single usable stack.<br/>
+ * If an element is missing a necessary method then it will have a pass-through method applied.
+ * @param {Array} stackElements An array of stack elements to be linked.
+ * @return {easyXDM.stack.StackElement} The last element in the chain.
+ */
+function chainStack(stackElements){
+ var stackEl, defaults = {
+ incoming: function(message, origin){
+ this.up.incoming(message, origin);
+ },
+ outgoing: function(message, recipient){
+ this.down.outgoing(message, recipient);
+ },
+ callback: function(success){
+ this.up.callback(success);
+ },
+ init: function(){
+ this.down.init();
+ },
+ destroy: function(){
+ this.down.destroy();
+ }
+ };
+ for (var i = 0, len = stackElements.length; i < len; i++) {
+ stackEl = stackElements[i];
+ apply(stackEl, defaults, true);
+ if (i !== 0) {
+ stackEl.down = stackElements[i - 1];
+ }
+ if (i !== len - 1) {
+ stackEl.up = stackElements[i + 1];
+ }
+ }
+ return stackEl;
+}
+
+/**
+ * This will remove a stackelement from its stack while leaving the stack functional.
+ * @param {Object} element The elment to remove from the stack.
+ */
+function removeFromStack(element){
+ element.up.down = element.down;
+ element.down.up = element.up;
+ element.up = element.down = null;
+}
+
+/*
+ * Export the main object and any other methods applicable
+ */
+/**
+ * @class easyXDM
+ * A javascript library providing cross-browser, cross-domain messaging/RPC.
+ * @version 2.4.17.1
+ * @singleton
+ */
+apply(easyXDM, {
+ /**
+ * The version of the library
+ * @type {string}
+ */
+ version: "2.4.17.1",
+ /**
+ * This is a map containing all the query parameters passed to the document.
+ * All the values has been decoded using decodeURIComponent.
+ * @type {object}
+ */
+ query: query,
+ /**
+ * @private
+ */
+ stack: {},
+ /**
+ * Applies properties from the source object to the target object.<br/>
+ * @param {object} target The target of the properties.
+ * @param {object} source The source of the properties.
+ * @param {boolean} noOverwrite Set to True to only set non-existing properties.
+ */
+ apply: apply,
+
+ /**
+ * A safe implementation of HTML5 JSON. Feature testing is used to make sure the implementation works.
+ * @return {JSON} A valid JSON conforming object, or null if not found.
+ */
+ getJSONObject: getJSON,
+ /**
+ * This will add a function to the queue of functions to be run once the DOM reaches a ready state.
+ * If functions are added after this event then they will be executed immediately.
+ * @param {function} fn The function to add
+ * @param {object} scope An optional scope for the function to be called with.
+ */
+ whenReady: whenReady,
+ /**
+ * Removes easyXDM variable from the global scope. It also returns control
+ * of the easyXDM variable to whatever code used it before.
+ *
+ * @param {String} ns A string representation of an object that will hold
+ * an instance of easyXDM.
+ * @return An instance of easyXDM
+ */
+ noConflict: noConflict
+});
+
+// Expose helper functions so we can test them
+apply(easyXDM, {
+ checkAcl: checkAcl,
+ getDomainName: getDomainName,
+ getLocation: getLocation,
+ appendQueryParameters: appendQueryParameters
+});
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global console, _FirebugCommandLine, easyXDM, window, escape, unescape, isHostObject, undef, _trace, domIsReady, emptyFn, namespace */
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+var debug = {
+ _deferred: [],
+ flush: function(){
+ this.trace("... deferred messages ...");
+ for (var i = 0, len = this._deferred.length; i < len; i++) {
+ this.trace(this._deferred[i]);
+ }
+ this._deferred.length = 0;
+ this.trace("... end of deferred messages ...");
+ },
+ getTime: function(){
+ var d = new Date(), h = d.getHours() + "", m = d.getMinutes() + "", s = d.getSeconds() + "", ms = d.getMilliseconds() + "", zeros = "000";
+ if (h.length == 1) {
+ h = "0" + h;
+ }
+ if (m.length == 1) {
+ m = "0" + m;
+ }
+ if (s.length == 1) {
+ s = "0" + s;
+ }
+ ms = zeros.substring(ms.length) + ms;
+ return h + ":" + m + ":" + s + "." + ms;
+ },
+ /**
+ * Logs the message to console.log if available
+ * @param {String} msg The message to log
+ */
+ log: function(msg){
+ // Uses memoizing to cache the implementation
+ if (!isHostObject(window, "console") || undef(console.log)) {
+ /**
+ * Sets log to be an empty function since we have no output available
+ * @ignore
+ */
+ this.log = emptyFn;
+ }
+ else {
+ /**
+ * Sets log to be a wrapper around console.log
+ * @ignore
+ * @param {String} msg
+ */
+ this.log = function(msg){
+ console.log(location.host + (namespace ? ":" + namespace : "") + " - " + this.getTime() + ": " + msg);
+ };
+ }
+ this.log(msg);
+ },
+ /**
+ * Will try to trace the given message either to a DOMElement with the id "log",
+ * or by using console.info.
+ * @param {String} msg The message to trace
+ */
+ trace: function(msg){
+ // Uses memoizing to cache the implementation
+ if (!domIsReady) {
+ if (this._deferred.length === 0) {
+ easyXDM.whenReady(debug.flush, debug);
+ }
+ this._deferred.push(msg);
+ this.log(msg);
+ }
+ else {
+ var el = document.getElementById("log");
+ // is there a log element present?
+ if (el) {
+ /**
+ * Sets trace to be a function that outputs the messages to the DOMElement with id "log"
+ * @ignore
+ * @param {String} msg
+ */
+ this.trace = function(msg){
+ try {
+ el.appendChild(document.createElement("div")).appendChild(document.createTextNode(location.host + (namespace ? ":" + namespace : "") + " - " + this.getTime() + ":" + msg));
+ el.scrollTop = el.scrollHeight;
+ }
+ catch (e) {
+ //In case we are unloading
+ }
+ };
+ }
+ else if (isHostObject(window, "console") && !undef(console.info)) {
+ /**
+ * Sets trace to be a wrapper around console.info
+ * @ignore
+ * @param {String} msg
+ */
+ this.trace = function(msg){
+ console.info(location.host + (namespace ? ":" + namespace : "") + " - " + this.getTime() + ":" + msg);
+ };
+ }
+ else {
+ /**
+ * Create log window
+ * @ignore
+ */
+ var domain = location.host, windowname = domain.replace(/[\-.:]/g, "") + "easyxdm_log", logWin;
+ try {
+ logWin = window.open("", windowname, "width=800,height=200,status=0,navigation=0,scrollbars=1");
+ }
+ catch (e) {
+ }
+ if (logWin) {
+ var doc = logWin.document;
+ el = doc.getElementById("log");
+ if (!el) {
+ doc.write("<html><head><title>easyXDM log " + domain + "</title></head>");
+ doc.write("<body><div id=\"log\"></div></body></html>");
+ doc.close();
+ el = doc.getElementById("log");
+ }
+ this.trace = function(msg){
+ try {
+ el.appendChild(doc.createElement("div")).appendChild(doc.createTextNode(location.host + (namespace ? ":" + namespace : "") + " - " + this.getTime() + ":" + msg));
+ el.scrollTop = el.scrollHeight;
+ }
+ catch (e) {
+ //In case we are unloading
+ }
+ };
+ this.trace("---- new logger at " + location.href);
+ }
+
+ if (!el) {
+ // We are unable to use any logging
+ this.trace = emptyFn;
+ }
+ }
+ this.trace(msg);
+ }
+ },
+ /**
+ * Creates a method usable for tracing.
+ * @param {String} name The name the messages should be marked with
+ * @return {Function} A function that accepts a single string as argument.
+ */
+ getTracer: function(name){
+ return function(msg){
+ debug.trace(name + ": " + msg);
+ };
+ }
+};
+debug.log("easyXDM present on '" + location.href);
+easyXDM.Debug = debug;
+_trace = debug.getTracer("{Private}");
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyXDM, window, escape, unescape, isHostObject, isHostMethod, un, on, createFrame, debug */
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * @class easyXDM.DomHelper
+ * Contains methods for dealing with the DOM
+ * @singleton
+ */
+easyXDM.DomHelper = {
+ /**
+ * Provides a consistent interface for adding eventhandlers
+ * @param {Object} target The target to add the event to
+ * @param {String} type The name of the event
+ * @param {Function} listener The listener
+ */
+ on: on,
+ /**
+ * Provides a consistent interface for removing eventhandlers
+ * @param {Object} target The target to remove the event from
+ * @param {String} type The name of the event
+ * @param {Function} listener The listener
+ */
+ un: un,
+ /**
+ * Checks for the presence of the JSON object.
+ * If it is not present it will use the supplied path to load the JSON2 library.
+ * This should be called in the documents head right after the easyXDM script tag.
+ * http://json.org/json2.js
+ * @param {String} path A valid path to json2.js
+ */
+ requiresJSON: function(path){
+ if (!isHostObject(window, "JSON")) {
+ debug.log("loading external JSON");
+ // we need to encode the < in order to avoid an illegal token error
+ // when the script is inlined in a document.
+ document.write('<' + 'script type="text/javascript" src="' + path + '"><' + '/script>');
+ }
+ else {
+ debug.log("native JSON found");
+ }
+ }
+};
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyXDM, window, escape, unescape, debug */
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+(function(){
+ // The map containing the stored functions
+ var _map = {};
+
+ /**
+ * @class easyXDM.Fn
+ * This contains methods related to function handling, such as storing callbacks.
+ * @singleton
+ * @namespace easyXDM
+ */
+ easyXDM.Fn = {
+ /**
+ * Stores a function using the given name for reference
+ * @param {String} name The name that the function should be referred by
+ * @param {Function} fn The function to store
+ * @namespace easyXDM.fn
+ */
+ set: function(name, fn){
+ this._trace("storing function " + name);
+ _map[name] = fn;
+ },
+ /**
+ * Retrieves the function referred to by the given name
+ * @param {String} name The name of the function to retrieve
+ * @param {Boolean} del If the function should be deleted after retrieval
+ * @return {Function} The stored function
+ * @namespace easyXDM.fn
+ */
+ get: function(name, del){
+ this._trace("retrieving function " + name);
+ var fn = _map[name];
+ if (!fn) {
+ this._trace(name + " not found");
+ }
+
+ if (del) {
+ delete _map[name];
+ }
+ return fn;
+ }
+ };
+
+ easyXDM.Fn._trace = debug.getTracer("easyXDM.Fn");
+}());
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyXDM, window, escape, unescape, chainStack, prepareTransportStack, getLocation, debug */
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * @class easyXDM.Socket
+ * This class creates a transport channel between two domains that is usable for sending and receiving string-based messages.<br/>
+ * The channel is reliable, supports queueing, and ensures that the message originates from the expected domain.<br/>
+ * Internally different stacks will be used depending on the browsers features and the available parameters.
+ * <h2>How to set up</h2>
+ * Setting up the provider:
+ * <pre><code>
+ * var socket = new easyXDM.Socket({
+ * local: "name.html",
+ * onReady: function(){
+ * // you need to wait for the onReady callback before using the socket
+ * socket.postMessage("foo-message");
+ * },
+ * onMessage: function(message, origin) {
+ * alert("received " + message + " from " + origin);
+ * }
+ * });
+ * </code></pre>
+ * Setting up the consumer:
+ * <pre><code>
+ * var socket = new easyXDM.Socket({
+ * remote: "http://remotedomain/page.html",
+ * remoteHelper: "http://remotedomain/name.html",
+ * onReady: function(){
+ * // you need to wait for the onReady callback before using the socket
+ * socket.postMessage("foo-message");
+ * },
+ * onMessage: function(message, origin) {
+ * alert("received " + message + " from " + origin);
+ * }
+ * });
+ * </code></pre>
+ * If you are unable to upload the <code>name.html</code> file to the consumers domain then remove the <code>remoteHelper</code> property
+ * and easyXDM will fall back to using the HashTransport instead of the NameTransport when not able to use any of the primary transports.
+ * @namespace easyXDM
+ * @constructor
+ * @cfg {String/Window} local The url to the local name.html document, a local static file, or a reference to the local window.
+ * @cfg {Boolean} lazy (Consumer only) Set this to true if you want easyXDM to defer creating the transport until really needed.
+ * @cfg {String} remote (Consumer only) The url to the providers document.
+ * @cfg {String} remoteHelper (Consumer only) The url to the remote name.html file. This is to support NameTransport as a fallback. Optional.
+ * @cfg {Number} delay The number of milliseconds easyXDM should try to get a reference to the local window. Optional, defaults to 2000.
+ * @cfg {Number} interval The interval used when polling for messages. Optional, defaults to 300.
+ * @cfg {String} channel (Consumer only) The name of the channel to use. Can be used to set consistent iframe names. Must be unique. Optional.
+ * @cfg {Function} onMessage The method that should handle incoming messages.<br/> This method should accept two arguments, the message as a string, and the origin as a string. Optional.
+ * @cfg {Function} onReady A method that should be called when the transport is ready. Optional.
+ * @cfg {DOMElement|String} container (Consumer only) The element, or the id of the element that the primary iframe should be inserted into. If not set then the iframe will be positioned off-screen. Optional.
+ * @cfg {Array/String} acl (Provider only) Here you can specify which '[protocol]://[domain]' patterns that should be allowed to act as the consumer towards this provider.<br/>
+ * This can contain the wildcards ? and *. Examples are 'http://example.com', '*.foo.com' and '*dom?.com'. If you want to use reqular expressions then you pattern needs to start with ^ and end with $.
+ * If none of the patterns match an Error will be thrown.
+ * @cfg {Object} props (Consumer only) Additional properties that should be applied to the iframe. This can also contain nested objects e.g: <code>{style:{width:"100px", height:"100px"}}</code>.
+ * Properties such as 'name' and 'src' will be overrided. Optional.
+ */
+easyXDM.Socket = function(config){
+ var trace = debug.getTracer("easyXDM.Socket");
+ trace("constructor");
+
+ // create the stack
+ var stack = chainStack(prepareTransportStack(config).concat([{
+ incoming: function(message, origin){
+ config.onMessage(message, origin);
+ },
+ callback: function(success){
+ if (config.onReady) {
+ config.onReady(success);
+ }
+ }
+ }])), recipient = getLocation(config.remote);
+
+ // set the origin
+ this.origin = getLocation(config.remote);
+
+ /**
+ * Initiates the destruction of the stack.
+ */
+ this.destroy = function(){
+ stack.destroy();
+ };
+
+ /**
+ * Posts a message to the remote end of the channel
+ * @param {String} message The message to send
+ */
+ this.postMessage = function(message){
+ stack.outgoing(message, recipient);
+ };
+
+ stack.init();
+};
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyXDM, window, escape, unescape, undef,, chainStack, prepareTransportStack, debug, getLocation */
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * @class easyXDM.Rpc
+ * Creates a proxy object that can be used to call methods implemented on the remote end of the channel, and also to provide the implementation
+ * of methods to be called from the remote end.<br/>
+ * The instantiated object will have methods matching those specified in <code>config.remote</code>.<br/>
+ * This requires the JSON object present in the document, either natively, using json.org's json2 or as a wrapper around library spesific methods.
+ * <h2>How to set up</h2>
+ * <pre><code>
+ * var rpc = new easyXDM.Rpc({
+ * // this configuration is equal to that used by the Socket.
+ * remote: "http://remotedomain/...",
+ * onReady: function(){
+ * // you need to wait for the onReady callback before using the proxy
+ * rpc.foo(...
+ * }
+ * },{
+ * local: {..},
+ * remote: {..}
+ * });
+ * </code></pre>
+ *
+ * <h2>Exposing functions (procedures)</h2>
+ * <pre><code>
+ * var rpc = new easyXDM.Rpc({
+ * ...
+ * },{
+ * local: {
+ * nameOfMethod: {
+ * method: function(arg1, arg2, success, error){
+ * ...
+ * }
+ * },
+ * // with shorthand notation
+ * nameOfAnotherMethod: function(arg1, arg2, success, error){
+ * }
+ * },
+ * remote: {...}
+ * });
+ * </code></pre>
+
+ * The function referenced by [method] will receive the passed arguments followed by the callback functions <code>success</code> and <code>error</code>.<br/>
+ * To send a successfull result back you can use
+ * <pre><code>
+ * return foo;
+ * </pre></code>
+ * or
+ * <pre><code>
+ * success(foo);
+ * </pre></code>
+ * To return an error you can use
+ * <pre><code>
+ * throw new Error("foo error");
+ * </code></pre>
+ * or
+ * <pre><code>
+ * error("foo error");
+ * </code></pre>
+ *
+ * <h2>Defining remotely exposed methods (procedures/notifications)</h2>
+ * The definition of the remote end is quite similar:
+ * <pre><code>
+ * var rpc = new easyXDM.Rpc({
+ * ...
+ * },{
+ * local: {...},
+ * remote: {
+ * nameOfMethod: {}
+ * }
+ * });
+ * </code></pre>
+ * To call a remote method use
+ * <pre><code>
+ * rpc.nameOfMethod("arg1", "arg2", function(value) {
+ * alert("success: " + value);
+ * }, function(message) {
+ * alert("error: " + message + );
+ * });
+ * </code></pre>
+ * Both the <code>success</code> and <code>errror</code> callbacks are optional.<br/>
+ * When called with no callback a JSON-RPC 2.0 notification will be executed.
+ * Be aware that you will not be notified of any errors with this method.
+ * <br/>
+ * <h2>Specifying a custom serializer</h2>
+ * If you do not want to use the JSON2 library for non-native JSON support, but instead capabilities provided by some other library
+ * then you can specify a custom serializer using <code>serializer: foo</code>
+ * <pre><code>
+ * var rpc = new easyXDM.Rpc({
+ * ...
+ * },{
+ * local: {...},
+ * remote: {...},
+ * serializer : {
+ * parse: function(string){ ... },
+ * stringify: function(object) {...}
+ * }
+ * });
+ * </code></pre>
+ * If <code>serializer</code> is set then the class will not attempt to use the native implementation.
+ * @namespace easyXDM
+ * @constructor
+ * @param {Object} config The underlying transports configuration. See easyXDM.Socket for available parameters.
+ * @param {Object} jsonRpcConfig The description of the interface to implement.
+ */
+easyXDM.Rpc = function(config, jsonRpcConfig){
+ var trace = debug.getTracer("easyXDM.Rpc");
+ trace("constructor");
+
+ // expand shorthand notation
+ if (jsonRpcConfig.local) {
+ for (var method in jsonRpcConfig.local) {
+ if (jsonRpcConfig.local.hasOwnProperty(method)) {
+ var member = jsonRpcConfig.local[method];
+ if (typeof member === "function") {
+ jsonRpcConfig.local[method] = {
+ method: member
+ };
+ }
+ }
+ }
+ }
+
+ // create the stack
+ var stack = chainStack(prepareTransportStack(config).concat([new easyXDM.stack.RpcBehavior(this, jsonRpcConfig), {
+ callback: function(success){
+ if (config.onReady) {
+ config.onReady(success);
+ }
+ }
+ }]));
+
+ // set the origin
+ this.origin = getLocation(config.remote);
+
+
+ /**
+ * Initiates the destruction of the stack.
+ */
+ this.destroy = function(){
+ stack.destroy();
+ };
+
+ stack.init();
+};
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyXDM, window, escape, unescape, getLocation, appendQueryParameters, createFrame, debug, un, on, apply, whenReady, getParentObject, IFRAME_PREFIX*/
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * @class easyXDM.stack.SameOriginTransport
+ * SameOriginTransport is a transport class that can be used when both domains have the same origin.<br/>
+ * This can be useful for testing and for when the main application supports both internal and external sources.
+ * @namespace easyXDM.stack
+ * @constructor
+ * @param {Object} config The transports configuration.
+ * @cfg {String} remote The remote document to communicate with.
+ */
+easyXDM.stack.SameOriginTransport = function(config){
+ var trace = debug.getTracer("easyXDM.stack.SameOriginTransport");
+ trace("constructor");
+ var pub, frame, send, targetOrigin;
+
+ return (pub = {
+ outgoing: function(message, domain, fn){
+ send(message);
+ if (fn) {
+ fn();
+ }
+ },
+ destroy: function(){
+ trace("destroy");
+ if (frame) {
+ frame.parentNode.removeChild(frame);
+ frame = null;
+ }
+ },
+ onDOMReady: function(){
+ trace("init");
+ targetOrigin = getLocation(config.remote);
+
+ if (config.isHost) {
+ // set up the iframe
+ apply(config.props, {
+ src: appendQueryParameters(config.remote, {
+ xdm_e: location.protocol + "//" + location.host + location.pathname,
+ xdm_c: config.channel,
+ xdm_p: 4 // 4 = SameOriginTransport
+ }),
+ name: IFRAME_PREFIX + config.channel + "_provider"
+ });
+ frame = createFrame(config);
+ easyXDM.Fn.set(config.channel, function(sendFn){
+ send = sendFn;
+ setTimeout(function(){
+ pub.up.callback(true);
+ }, 0);
+ return function(msg){
+ pub.up.incoming(msg, targetOrigin);
+ };
+ });
+ }
+ else {
+ send = getParentObject().Fn.get(config.channel, true)(function(msg){
+ pub.up.incoming(msg, targetOrigin);
+ });
+ setTimeout(function(){
+ pub.up.callback(true);
+ }, 0);
+ }
+ },
+ init: function(){
+ whenReady(pub.onDOMReady, pub);
+ }
+ });
+};
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global global, easyXDM, window, getLocation, appendQueryParameters, createFrame, debug, apply, whenReady, IFRAME_PREFIX, namespace, resolveUrl, getDomainName, HAS_FLASH_THROTTLED_BUG, getPort, query*/
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * @class easyXDM.stack.FlashTransport
+ * FlashTransport is a transport class that uses an SWF with LocalConnection to pass messages back and forth.
+ * @namespace easyXDM.stack
+ * @constructor
+ * @param {Object} config The transports configuration.
+ * @cfg {String} remote The remote domain to communicate with.
+ * @cfg {String} secret the pre-shared secret used to secure the communication.
+ * @cfg {String} swf The path to the swf file
+ * @cfg {Boolean} swfNoThrottle Set this to true if you want to take steps to avoid beeing throttled when hidden.
+ * @cfg {String || DOMElement} swfContainer Set this if you want to control where the swf is placed
+ */
+easyXDM.stack.FlashTransport = function(config){
+ var trace = debug.getTracer("easyXDM.stack.FlashTransport");
+ trace("constructor");
+ if (!config.swf) {
+ throw new Error("Path to easyxdm.swf is missing");
+ }
+ var pub, // the public interface
+ frame, send, targetOrigin, swf, swfContainer;
+
+ function onMessage(message, origin){
+ setTimeout(function(){
+ trace("received message");
+ pub.up.incoming(message, targetOrigin);
+ }, 0);
+ }
+
+ /**
+ * This method adds the SWF to the DOM and prepares the initialization of the channel
+ */
+ function addSwf(domain){
+ trace("creating factory with SWF from " + domain);
+ // the differentiating query argument is needed in Flash9 to avoid a caching issue where LocalConnection would throw an error.
+ var url = config.swf + "?host=" + config.isHost;
+ var id = "easyXDM_swf_" + Math.floor(Math.random() * 10000);
+
+ // prepare the init function that will fire once the swf is ready
+ easyXDM.Fn.set("flash_loaded" + domain.replace(/[\-.]/g, "_"), function(){
+ easyXDM.stack.FlashTransport[domain].swf = swf = swfContainer.firstChild;
+ var queue = easyXDM.stack.FlashTransport[domain].queue;
+ for (var i = 0; i < queue.length; i++) {
+ queue[i]();
+ }
+ queue.length = 0;
+ });
+
+ if (config.swfContainer) {
+ swfContainer = (typeof config.swfContainer == "string") ? document.getElementById(config.swfContainer) : config.swfContainer;
+ }
+ else {
+ // create the container that will hold the swf
+ swfContainer = document.createElement('div');
+
+ // http://bugs.adobe.com/jira/browse/FP-4796
+ // http://tech.groups.yahoo.com/group/flexcoders/message/162365
+ // https://groups.google.com/forum/#!topic/easyxdm/mJZJhWagoLc
+ apply(swfContainer.style, HAS_FLASH_THROTTLED_BUG && config.swfNoThrottle ? {
+ height: "20px",
+ width: "20px",
+ position: "fixed",
+ right: 0,
+ top: 0
+ } : {
+ height: "1px",
+ width: "1px",
+ position: "absolute",
+ overflow: "hidden",
+ right: 0,
+ top: 0
+ });
+ document.body.appendChild(swfContainer);
+ }
+
+ // create the object/embed
+ var flashVars = "callback=flash_loaded" + domain.replace(/[\-.]/g, "_") + "&proto=" + global.location.protocol + "&domain=" + getDomainName(global.location.href) + "&port=" + getPort(global.location.href) + "&ns=" + namespace;
+ flashVars += "&log=true";
+ swfContainer.innerHTML = "<object height='20' width='20' type='application/x-shockwave-flash' id='" + id + "' data='" + url + "'>" +
+ "<param name='allowScriptAccess' value='always'></param>" +
+ "<param name='wmode' value='transparent'>" +
+ "<param name='movie' value='" +
+ url +
+ "'></param>" +
+ "<param name='flashvars' value='" +
+ flashVars +
+ "'></param>" +
+ "<embed type='application/x-shockwave-flash' FlashVars='" +
+ flashVars +
+ "' allowScriptAccess='always' wmode='transparent' src='" +
+ url +
+ "' height='1' width='1'></embed>" +
+ "</object>";
+ }
+
+ return (pub = {
+ outgoing: function(message, domain, fn){
+ swf.postMessage(config.channel, message.toString());
+ if (fn) {
+ fn();
+ }
+ },
+ destroy: function(){
+ trace("destroy");
+ try {
+ swf.destroyChannel(config.channel);
+ }
+ catch (e) {
+ }
+ swf = null;
+ if (frame) {
+ frame.parentNode.removeChild(frame);
+ frame = null;
+ }
+ },
+ onDOMReady: function(){
+ trace("init");
+
+ targetOrigin = config.remote;
+
+ // Prepare the code that will be run after the swf has been intialized
+ easyXDM.Fn.set("flash_" + config.channel + "_init", function(){
+ setTimeout(function(){
+ trace("firing onReady");
+ pub.up.callback(true);
+ });
+ });
+
+ // set up the omMessage handler
+ easyXDM.Fn.set("flash_" + config.channel + "_onMessage", onMessage);
+
+ config.swf = resolveUrl(config.swf); // reports have been made of requests gone rogue when using relative paths
+ var swfdomain = getDomainName(config.swf);
+ var fn = function(){
+ // set init to true in case the fn was called was invoked from a separate instance
+ easyXDM.stack.FlashTransport[swfdomain].init = true;
+ swf = easyXDM.stack.FlashTransport[swfdomain].swf;
+ // create the channel
+ swf.createChannel(config.channel, config.secret, getLocation(config.remote), config.isHost);
+
+ if (config.isHost) {
+ // if Flash is going to be throttled and we want to avoid this
+ if (HAS_FLASH_THROTTLED_BUG && config.swfNoThrottle) {
+ apply(config.props, {
+ position: "fixed",
+ right: 0,
+ top: 0,
+ height: "20px",
+ width: "20px"
+ });
+ }
+ // set up the iframe
+ apply(config.props, {
+ src: appendQueryParameters(config.remote, {
+ xdm_e: getLocation(location.href),
+ xdm_c: config.channel,
+ xdm_p: 6, // 6 = FlashTransport
+ xdm_s: config.secret
+ }),
+ name: IFRAME_PREFIX + config.channel + "_provider"
+ });
+ frame = createFrame(config);
+ }
+ };
+
+ if (easyXDM.stack.FlashTransport[swfdomain] && easyXDM.stack.FlashTransport[swfdomain].init) {
+ // if the swf is in place and we are the consumer
+ fn();
+ }
+ else {
+ // if the swf does not yet exist
+ if (!easyXDM.stack.FlashTransport[swfdomain]) {
+ // add the queue to hold the init fn's
+ easyXDM.stack.FlashTransport[swfdomain] = {
+ queue: [fn]
+ };
+ addSwf(swfdomain);
+ }
+ else {
+ easyXDM.stack.FlashTransport[swfdomain].queue.push(fn);
+ }
+ }
+ },
+ init: function(){
+ whenReady(pub.onDOMReady, pub);
+ }
+ });
+};
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyXDM, window, escape, unescape, getLocation, appendQueryParameters, createFrame, debug, un, on, apply, whenReady, IFRAME_PREFIX*/
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * @class easyXDM.stack.PostMessageTransport
+ * PostMessageTransport is a transport class that uses HTML5 postMessage for communication.<br/>
+ * <a href="http://msdn.microsoft.com/en-us/library/ms644944(VS.85).aspx">http://msdn.microsoft.com/en-us/library/ms644944(VS.85).aspx</a><br/>
+ * <a href="https://developer.mozilla.org/en/DOM/window.postMessage">https://developer.mozilla.org/en/DOM/window.postMessage</a>
+ * @namespace easyXDM.stack
+ * @constructor
+ * @param {Object} config The transports configuration.
+ * @cfg {String} remote The remote domain to communicate with.
+ */
+easyXDM.stack.PostMessageTransport = function(config){
+ var trace = debug.getTracer("easyXDM.stack.PostMessageTransport");
+ trace("constructor");
+ var pub, // the public interface
+ frame, // the remote frame, if any
+ callerWindow, // the window that we will call with
+ targetOrigin; // the domain to communicate with
+ /**
+ * Resolves the origin from the event object
+ * @private
+ * @param {Object} event The messageevent
+ * @return {String} The scheme, host and port of the origin
+ */
+ function _getOrigin(event){
+ if (event.origin) {
+ // This is the HTML5 property
+ return getLocation(event.origin);
+ }
+ if (event.uri) {
+ // From earlier implementations
+ return getLocation(event.uri);
+ }
+ if (event.domain) {
+ // This is the last option and will fail if the
+ // origin is not using the same schema as we are
+ return location.protocol + "//" + event.domain;
+ }
+ throw "Unable to retrieve the origin of the event";
+ }
+
+ /**
+ * This is the main implementation for the onMessage event.<br/>
+ * It checks the validity of the origin and passes the message on if appropriate.
+ * @private
+ * @param {Object} event The messageevent
+ */
+ function _window_onMessage(event){
+ var origin = _getOrigin(event);
+ trace("received message '" + event.data + "' from " + origin);
+ if (origin == targetOrigin && event.data.substring(0, config.channel.length + 1) == config.channel + " ") {
+ pub.up.incoming(event.data.substring(config.channel.length + 1), origin);
+ }
+ }
+
+ return (pub = {
+ outgoing: function(message, domain, fn){
+ callerWindow.postMessage(config.channel + " " + message, domain || targetOrigin);
+ if (fn) {
+ fn();
+ }
+ },
+ destroy: function(){
+ trace("destroy");
+ un(window, "message", _window_onMessage);
+ if (frame) {
+ callerWindow = null;
+ frame.parentNode.removeChild(frame);
+ frame = null;
+ }
+ },
+ onDOMReady: function(){
+ trace("init");
+ targetOrigin = getLocation(config.remote);
+ if (config.isHost) {
+ // add the event handler for listening
+ var waitForReady = function(event){
+ if (event.data == config.channel + "-ready") {
+ trace("firing onReady");
+ // replace the eventlistener
+ callerWindow = ("postMessage" in frame.contentWindow) ? frame.contentWindow : frame.contentWindow.document;
+ un(window, "message", waitForReady);
+ on(window, "message", _window_onMessage);
+ setTimeout(function(){
+ pub.up.callback(true);
+ }, 0);
+ }
+ };
+ on(window, "message", waitForReady);
+
+ // set up the iframe
+ apply(config.props, {
+ src: appendQueryParameters(config.remote, {
+ xdm_e: getLocation(location.href),
+ xdm_c: config.channel,
+ xdm_p: 1 // 1 = PostMessage
+ }),
+ name: IFRAME_PREFIX + config.channel + "_provider"
+ });
+ frame = createFrame(config);
+ }
+ else {
+ // add the event handler for listening
+ on(window, "message", _window_onMessage);
+ callerWindow = ("postMessage" in window.parent) ? window.parent : window.parent.document;
+ callerWindow.postMessage(config.channel + "-ready", targetOrigin);
+
+ setTimeout(function(){
+ pub.up.callback(true);
+ }, 0);
+ }
+ },
+ init: function(){
+ whenReady(pub.onDOMReady, pub);
+ }
+ });
+};
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyXDM, window, escape, unescape, getLocation, appendQueryParameters, createFrame, debug, apply, query, whenReady, IFRAME_PREFIX*/
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * @class easyXDM.stack.FrameElementTransport
+ * FrameElementTransport is a transport class that can be used with Gecko-browser as these allow passing variables using the frameElement property.<br/>
+ * Security is maintained as Gecho uses Lexical Authorization to determine under which scope a function is running.
+ * @namespace easyXDM.stack
+ * @constructor
+ * @param {Object} config The transports configuration.
+ * @cfg {String} remote The remote document to communicate with.
+ */
+easyXDM.stack.FrameElementTransport = function(config){
+ var trace = debug.getTracer("easyXDM.stack.FrameElementTransport");
+ trace("constructor");
+ var pub, frame, send, targetOrigin;
+
+ return (pub = {
+ outgoing: function(message, domain, fn){
+ send.call(this, message);
+ if (fn) {
+ fn();
+ }
+ },
+ destroy: function(){
+ trace("destroy");
+ if (frame) {
+ frame.parentNode.removeChild(frame);
+ frame = null;
+ }
+ },
+ onDOMReady: function(){
+ trace("init");
+ targetOrigin = getLocation(config.remote);
+
+ if (config.isHost) {
+ // set up the iframe
+ apply(config.props, {
+ src: appendQueryParameters(config.remote, {
+ xdm_e: getLocation(location.href),
+ xdm_c: config.channel,
+ xdm_p: 5 // 5 = FrameElementTransport
+ }),
+ name: IFRAME_PREFIX + config.channel + "_provider"
+ });
+ frame = createFrame(config);
+ frame.fn = function(sendFn){
+ delete frame.fn;
+ send = sendFn;
+ setTimeout(function(){
+ pub.up.callback(true);
+ }, 0);
+ // remove the function so that it cannot be used to overwrite the send function later on
+ return function(msg){
+ pub.up.incoming(msg, targetOrigin);
+ };
+ };
+ }
+ else {
+ // This is to mitigate origin-spoofing
+ if (document.referrer && getLocation(document.referrer) != query.xdm_e) {
+ window.top.location = query.xdm_e;
+ }
+ send = window.frameElement.fn(function(msg){
+ pub.up.incoming(msg, targetOrigin);
+ });
+ pub.up.callback(true);
+ }
+ },
+ init: function(){
+ whenReady(pub.onDOMReady, pub);
+ }
+ });
+};
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyXDM, window, escape, unescape, undef, getLocation, appendQueryParameters, resolveUrl, createFrame, debug, un, apply, whenReady, IFRAME_PREFIX*/
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * @class easyXDM.stack.NameTransport
+ * NameTransport uses the window.name property to relay data.
+ * The <code>local</code> parameter needs to be set on both the consumer and provider,<br/>
+ * and the <code>remoteHelper</code> parameter needs to be set on the consumer.
+ * @constructor
+ * @param {Object} config The transports configuration.
+ * @cfg {String} remoteHelper The url to the remote instance of hash.html - this is only needed for the host.
+ * @namespace easyXDM.stack
+ */
+easyXDM.stack.NameTransport = function(config){
+ var trace = debug.getTracer("easyXDM.stack.NameTransport");
+ trace("constructor");
+ if (config.isHost && undef(config.remoteHelper)) {
+ trace("missing remoteHelper");
+ throw new Error("missing remoteHelper");
+ }
+
+ var pub; // the public interface
+ var isHost, callerWindow, remoteWindow, readyCount, callback, remoteOrigin, remoteUrl;
+
+ function _sendMessage(message){
+ var url = config.remoteHelper + (isHost ? "#_3" : "#_2") + config.channel;
+ trace("sending message " + message);
+ trace("navigating to '" + url + "'");
+ callerWindow.contentWindow.sendMessage(message, url);
+ }
+
+ function _onReady(){
+ if (isHost) {
+ if (++readyCount === 2 || !isHost) {
+ pub.up.callback(true);
+ }
+ }
+ else {
+ _sendMessage("ready");
+ trace("calling onReady");
+ pub.up.callback(true);
+ }
+ }
+
+ function _onMessage(message){
+ trace("received message " + message);
+ pub.up.incoming(message, remoteOrigin);
+ }
+
+ function _onLoad(){
+ if (callback) {
+ setTimeout(function(){
+ callback(true);
+ }, 0);
+ }
+ }
+
+ return (pub = {
+ outgoing: function(message, domain, fn){
+ callback = fn;
+ _sendMessage(message);
+ },
+ destroy: function(){
+ trace("destroy");
+ callerWindow.parentNode.removeChild(callerWindow);
+ callerWindow = null;
+ if (isHost) {
+ remoteWindow.parentNode.removeChild(remoteWindow);
+ remoteWindow = null;
+ }
+ },
+ onDOMReady: function(){
+ trace("init");
+ isHost = config.isHost;
+ readyCount = 0;
+ remoteOrigin = getLocation(config.remote);
+ config.local = resolveUrl(config.local);
+
+ if (isHost) {
+ // Register the callback
+ easyXDM.Fn.set(config.channel, function(message){
+ trace("received initial message " + message);
+ if (isHost && message === "ready") {
+ // Replace the handler
+ easyXDM.Fn.set(config.channel, _onMessage);
+ _onReady();
+ }
+ });
+
+ // Set up the frame that points to the remote instance
+ remoteUrl = appendQueryParameters(config.remote, {
+ xdm_e: config.local,
+ xdm_c: config.channel,
+ xdm_p: 2
+ });
+ apply(config.props, {
+ src: remoteUrl + '#' + config.channel,
+ name: IFRAME_PREFIX + config.channel + "_provider"
+ });
+ remoteWindow = createFrame(config);
+ }
+ else {
+ config.remoteHelper = config.remote;
+ easyXDM.Fn.set(config.channel, _onMessage);
+ }
+
+ // Set up the iframe that will be used for the transport
+ var onLoad = function(){
+ // Remove the handler
+ var w = callerWindow || this;
+ un(w, "load", onLoad);
+ easyXDM.Fn.set(config.channel + "_load", _onLoad);
+ (function test(){
+ if (typeof w.contentWindow.sendMessage == "function") {
+ _onReady();
+ }
+ else {
+ setTimeout(test, 50);
+ }
+ }());
+ };
+
+ callerWindow = createFrame({
+ props: {
+ src: config.local + "#_4" + config.channel
+ },
+ onLoad: onLoad
+ });
+ },
+ init: function(){
+ whenReady(pub.onDOMReady, pub);
+ }
+ });
+};
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyXDM, window, escape, unescape, getLocation, createFrame, debug, un, on, apply, whenReady, IFRAME_PREFIX*/
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * @class easyXDM.stack.HashTransport
+ * HashTransport is a transport class that uses the IFrame URL Technique for communication.<br/>
+ * <a href="http://msdn.microsoft.com/en-us/library/bb735305.aspx">http://msdn.microsoft.com/en-us/library/bb735305.aspx</a><br/>
+ * @namespace easyXDM.stack
+ * @constructor
+ * @param {Object} config The transports configuration.
+ * @cfg {String/Window} local The url to the local file used for proxying messages, or the local window.
+ * @cfg {Number} delay The number of milliseconds easyXDM should try to get a reference to the local window.
+ * @cfg {Number} interval The interval used when polling for messages.
+ */
+easyXDM.stack.HashTransport = function(config){
+ var trace = debug.getTracer("easyXDM.stack.HashTransport");
+ trace("constructor");
+ var pub;
+ var me = this, isHost, _timer, pollInterval, _lastMsg, _msgNr, _listenerWindow, _callerWindow;
+ var useParent, _remoteOrigin;
+
+ function _sendMessage(message){
+ trace("sending message '" + (_msgNr + 1) + " " + message + "' to " + _remoteOrigin);
+ if (!_callerWindow) {
+ trace("no caller window");
+ return;
+ }
+ var url = config.remote + "#" + (_msgNr++) + "_" + message;
+ ((isHost || !useParent) ? _callerWindow.contentWindow : _callerWindow).location = url;
+ }
+
+ function _handleHash(hash){
+ _lastMsg = hash;
+ trace("received message '" + _lastMsg + "' from " + _remoteOrigin);
+ pub.up.incoming(_lastMsg.substring(_lastMsg.indexOf("_") + 1), _remoteOrigin);
+ }
+
+ /**
+ * Checks location.hash for a new message and relays this to the receiver.
+ * @private
+ */
+ function _pollHash(){
+ if (!_listenerWindow) {
+ return;
+ }
+ var href = _listenerWindow.location.href, hash = "", indexOf = href.indexOf("#");
+ if (indexOf != -1) {
+ hash = href.substring(indexOf);
+ }
+ if (hash && hash != _lastMsg) {
+ trace("poll: new message");
+ _handleHash(hash);
+ }
+ }
+
+ function _attachListeners(){
+ trace("starting polling");
+ _timer = setInterval(_pollHash, pollInterval);
+ }
+
+ return (pub = {
+ outgoing: function(message, domain){
+ _sendMessage(message);
+ },
+ destroy: function(){
+ window.clearInterval(_timer);
+ if (isHost || !useParent) {
+ _callerWindow.parentNode.removeChild(_callerWindow);
+ }
+ _callerWindow = null;
+ },
+ onDOMReady: function(){
+ isHost = config.isHost;
+ pollInterval = config.interval;
+ _lastMsg = "#" + config.channel;
+ _msgNr = 0;
+ useParent = config.useParent;
+ _remoteOrigin = getLocation(config.remote);
+ if (isHost) {
+ apply(config.props, {
+ src: config.remote,
+ name: IFRAME_PREFIX + config.channel + "_provider"
+ });
+ if (useParent) {
+ config.onLoad = function(){
+ _listenerWindow = window;
+ _attachListeners();
+ pub.up.callback(true);
+ };
+ }
+ else {
+ var tries = 0, max = config.delay / 50;
+ (function getRef(){
+ if (++tries > max) {
+ trace("unable to get reference to _listenerWindow, giving up");
+ throw new Error("Unable to reference listenerwindow");
+ }
+ try {
+ _listenerWindow = _callerWindow.contentWindow.frames[IFRAME_PREFIX + config.channel + "_consumer"];
+ }
+ catch (ex) {
+ }
+ if (_listenerWindow) {
+ _attachListeners();
+ trace("got a reference to _listenerWindow");
+ pub.up.callback(true);
+ }
+ else {
+ setTimeout(getRef, 50);
+ }
+ }());
+ }
+ _callerWindow = createFrame(config);
+ }
+ else {
+ _listenerWindow = window;
+ _attachListeners();
+ if (useParent) {
+ _callerWindow = parent;
+ pub.up.callback(true);
+ }
+ else {
+ apply(config, {
+ props: {
+ src: config.remote + "#" + config.channel + new Date(),
+ name: IFRAME_PREFIX + config.channel + "_consumer"
+ },
+ onLoad: function(){
+ pub.up.callback(true);
+ }
+ });
+ _callerWindow = createFrame(config);
+ }
+ }
+ },
+ init: function(){
+ whenReady(pub.onDOMReady, pub);
+ }
+ });
+};
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyXDM, window, escape, unescape, debug */
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * @class easyXDM.stack.ReliableBehavior
+ * This is a behavior that tries to make the underlying transport reliable by using acknowledgements.
+ * @namespace easyXDM.stack
+ * @constructor
+ * @param {Object} config The behaviors configuration.
+ */
+easyXDM.stack.ReliableBehavior = function(config){
+ var trace = debug.getTracer("easyXDM.stack.ReliableBehavior");
+ trace("constructor");
+ var pub, // the public interface
+ callback; // the callback to execute when we have a confirmed success/failure
+ var idOut = 0, idIn = 0, currentMessage = "";
+
+ return (pub = {
+ incoming: function(message, origin){
+ trace("incoming: " + message);
+ var indexOf = message.indexOf("_"), ack = message.substring(0, indexOf).split(",");
+ message = message.substring(indexOf + 1);
+
+ if (ack[0] == idOut) {
+ trace("message delivered");
+ currentMessage = "";
+ if (callback) {
+ callback(true);
+ callback = null;
+ }
+ }
+ if (message.length > 0) {
+ trace("sending ack, and passing on " + message);
+ pub.down.outgoing(ack[1] + "," + idOut + "_" + currentMessage, origin);
+ if (idIn != ack[1]) {
+ idIn = ack[1];
+ pub.up.incoming(message, origin);
+ }
+ }
+
+ },
+ outgoing: function(message, origin, fn){
+ currentMessage = message;
+ callback = fn;
+ pub.down.outgoing(idIn + "," + (++idOut) + "_" + message, origin);
+ }
+ });
+};
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyXDM, window, escape, unescape, debug, undef, removeFromStack*/
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * @class easyXDM.stack.QueueBehavior
+ * This is a behavior that enables queueing of messages. <br/>
+ * It will buffer incoming messages and dispach these as fast as the underlying transport allows.
+ * This will also fragment/defragment messages so that the outgoing message is never bigger than the
+ * set length.
+ * @namespace easyXDM.stack
+ * @constructor
+ * @param {Object} config The behaviors configuration. Optional.
+ * @cfg {Number} maxLength The maximum length of each outgoing message. Set this to enable fragmentation.
+ */
+easyXDM.stack.QueueBehavior = function(config){
+ var trace = debug.getTracer("easyXDM.stack.QueueBehavior");
+ trace("constructor");
+ var pub, queue = [], waiting = true, incoming = "", destroying, maxLength = 0, lazy = false, doFragment = false;
+
+ function dispatch(){
+ if (config.remove && queue.length === 0) {
+ trace("removing myself from the stack");
+ removeFromStack(pub);
+ return;
+ }
+ if (waiting || queue.length === 0 || destroying) {
+ return;
+ }
+ trace("dispatching from queue");
+ waiting = true;
+ var message = queue.shift();
+
+ pub.down.outgoing(message.data, message.origin, function(success){
+ waiting = false;
+ if (message.callback) {
+ setTimeout(function(){
+ message.callback(success);
+ }, 0);
+ }
+ dispatch();
+ });
+ }
+ return (pub = {
+ init: function(){
+ if (undef(config)) {
+ config = {};
+ }
+ if (config.maxLength) {
+ maxLength = config.maxLength;
+ doFragment = true;
+ }
+ if (config.lazy) {
+ lazy = true;
+ }
+ else {
+ pub.down.init();
+ }
+ },
+ callback: function(success){
+ waiting = false;
+ var up = pub.up; // in case dispatch calls removeFromStack
+ dispatch();
+ up.callback(success);
+ },
+ incoming: function(message, origin){
+ if (doFragment) {
+ var indexOf = message.indexOf("_"), seq = parseInt(message.substring(0, indexOf), 10);
+ incoming += message.substring(indexOf + 1);
+ if (seq === 0) {
+ trace("received the last fragment");
+ if (config.encode) {
+ incoming = decodeURIComponent(incoming);
+ }
+ pub.up.incoming(incoming, origin);
+ incoming = "";
+ }
+ else {
+ trace("waiting for more fragments, seq=" + message);
+ }
+ }
+ else {
+ pub.up.incoming(message, origin);
+ }
+ },
+ outgoing: function(message, origin, fn){
+ if (config.encode) {
+ message = encodeURIComponent(message);
+ }
+ var fragments = [], fragment;
+ if (doFragment) {
+ // fragment into chunks
+ while (message.length !== 0) {
+ fragment = message.substring(0, maxLength);
+ message = message.substring(fragment.length);
+ fragments.push(fragment);
+ }
+ // enqueue the chunks
+ while ((fragment = fragments.shift())) {
+ trace("enqueuing");
+ queue.push({
+ data: fragments.length + "_" + fragment,
+ origin: origin,
+ callback: fragments.length === 0 ? fn : null
+ });
+ }
+ }
+ else {
+ queue.push({
+ data: message,
+ origin: origin,
+ callback: fn
+ });
+ }
+ if (lazy) {
+ pub.down.init();
+ }
+ else {
+ dispatch();
+ }
+ },
+ destroy: function(){
+ trace("destroy");
+ destroying = true;
+ pub.down.destroy();
+ }
+ });
+};
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyXDM, window, escape, unescape, undef, debug */
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * @class easyXDM.stack.VerifyBehavior
+ * This behavior will verify that communication with the remote end is possible, and will also sign all outgoing,
+ * and verify all incoming messages. This removes the risk of someone hijacking the iframe to send malicious messages.
+ * @namespace easyXDM.stack
+ * @constructor
+ * @param {Object} config The behaviors configuration.
+ * @cfg {Boolean} initiate If the verification should be initiated from this end.
+ */
+easyXDM.stack.VerifyBehavior = function(config){
+ var trace = debug.getTracer("easyXDM.stack.VerifyBehavior");
+ trace("constructor");
+ if (undef(config.initiate)) {
+ throw new Error("settings.initiate is not set");
+ }
+ var pub, mySecret, theirSecret, verified = false;
+
+ function startVerification(){
+ trace("requesting verification");
+ mySecret = Math.random().toString(16).substring(2);
+ pub.down.outgoing(mySecret);
+ }
+
+ return (pub = {
+ incoming: function(message, origin){
+ var indexOf = message.indexOf("_");
+ if (indexOf === -1) {
+ if (message === mySecret) {
+ trace("verified, calling callback");
+ pub.up.callback(true);
+ }
+ else if (!theirSecret) {
+ trace("returning secret");
+ theirSecret = message;
+ if (!config.initiate) {
+ startVerification();
+ }
+ pub.down.outgoing(message);
+ }
+ }
+ else {
+ if (message.substring(0, indexOf) === theirSecret) {
+ pub.up.incoming(message.substring(indexOf + 1), origin);
+ }
+ }
+ },
+ outgoing: function(message, origin, fn){
+ pub.down.outgoing(mySecret + "_" + message, origin, fn);
+ },
+ callback: function(success){
+ if (config.initiate) {
+ startVerification();
+ }
+ }
+ });
+};
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyXDM, window, escape, unescape, undef, getJSON, debug, emptyFn, isArray */
+//
+// easyXDM
+// http://easyxdm.net/
+// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * @class easyXDM.stack.RpcBehavior
+ * This uses JSON-RPC 2.0 to expose local methods and to invoke remote methods and have responses returned over the the string based transport stack.<br/>
+ * Exposed methods can return values synchronous, asyncronous, or bet set up to not return anything.
+ * @namespace easyXDM.stack
+ * @constructor
+ * @param {Object} proxy The object to apply the methods to.
+ * @param {Object} config The definition of the local and remote interface to implement.
+ * @cfg {Object} local The local interface to expose.
+ * @cfg {Object} remote The remote methods to expose through the proxy.
+ * @cfg {Object} serializer The serializer to use for serializing and deserializing the JSON. Should be compatible with the HTML5 JSON object. Optional, will default to JSON.
+ */
+easyXDM.stack.RpcBehavior = function(proxy, config){
+ var trace = debug.getTracer("easyXDM.stack.RpcBehavior");
+ var pub, serializer = config.serializer || getJSON();
+ var _callbackCounter = 0, _callbacks = {};
+
+ /**
+ * Serializes and sends the message
+ * @private
+ * @param {Object} data The JSON-RPC message to be sent. The jsonrpc property will be added.
+ */
+ function _send(data){
+ data.jsonrpc = "2.0";
+ pub.down.outgoing(serializer.stringify(data));
+ }
+
+ /**
+ * Creates a method that implements the given definition
+ * @private
+ * @param {Object} The method configuration
+ * @param {String} method The name of the method
+ * @return {Function} A stub capable of proxying the requested method call
+ */
+ function _createMethod(definition, method){
+ var slice = Array.prototype.slice;
+
+ trace("creating method " + method);
+ return function(){
+ trace("executing method " + method);
+ var l = arguments.length, callback, message = {
+ method: method
+ };
+
+ if (l > 0 && typeof arguments[l - 1] === "function") {
+ //with callback, procedure
+ if (l > 1 && typeof arguments[l - 2] === "function") {
+ // two callbacks, success and error
+ callback = {
+ success: arguments[l - 2],
+ error: arguments[l - 1]
+ };
+ message.params = slice.call(arguments, 0, l - 2);
+ }
+ else {
+ // single callback, success
+ callback = {
+ success: arguments[l - 1]
+ };
+ message.params = slice.call(arguments, 0, l - 1);
+ }
+ _callbacks["" + (++_callbackCounter)] = callback;
+ message.id = _callbackCounter;
+ }
+ else {
+ // no callbacks, a notification
+ message.params = slice.call(arguments, 0);
+ }
+ if (definition.namedParams && message.params.length === 1) {
+ message.params = message.params[0];
+ }
+ // Send the method request
+ _send(message);
+ };
+ }
+
+ /**
+ * Executes the exposed method
+ * @private
+ * @param {String} method The name of the method
+ * @param {Number} id The callback id to use
+ * @param {Function} method The exposed implementation
+ * @param {Array} params The parameters supplied by the remote end
+ */
+ function _executeMethod(method, id, fn, params){
+ if (!fn) {
+ trace("requested to execute non-existent procedure " + method);
+ if (id) {
+ _send({
+ id: id,
+ error: {
+ code: -32601,
+ message: "Procedure not found."
+ }
+ });
+ }
+ return;
+ }
+
+ trace("requested to execute procedure " + method);
+ var success, error;
+ if (id) {
+ success = function(result){
+ success = emptyFn;
+ _send({
+ id: id,
+ result: result
+ });
+ };
+ error = function(message, data){
+ error = emptyFn;
+ var msg = {
+ id: id,
+ error: {
+ code: -32099,
+ message: message
+ }
+ };
+ if (data) {
+ msg.error.data = data;
+ }
+ _send(msg);
+ };
+ }
+ else {
+ success = error = emptyFn;
+ }
+ // Call local method
+ if (!isArray(params)) {
+ params = [params];
+ }
+ try {
+ var result = fn.method.apply(fn.scope, params.concat([success, error]));
+ if (!undef(result)) {
+ success(result);
+ }
+ }
+ catch (ex1) {
+ error(ex1.message);
+ }
+ }
+
+ return (pub = {
+ incoming: function(message, origin){
+ var data = serializer.parse(message);
+ if (data.method) {
+ trace("received request to execute method " + data.method + (data.id ? (" using callback id " + data.id) : ""));
+ // A method call from the remote end
+ if (config.handle) {
+ config.handle(data, _send);
+ }
+ else {
+ _executeMethod(data.method, data.id, config.local[data.method], data.params);
+ }
+ }
+ else {
+ trace("received return value destined to callback with id " + data.id);
+ // A method response from the other end
+ var callback = _callbacks[data.id];
+ if (data.error) {
+ if (callback.error) {
+ callback.error(data.error);
+ }
+ else {
+ trace("unhandled error returned.");
+ }
+ }
+ else if (callback.success) {
+ callback.success(data.result);
+ }
+ delete _callbacks[data.id];
+ }
+ },
+ init: function(){
+ trace("init");
+ if (config.remote) {
+ trace("creating stubs");
+ // Implement the remote sides exposed methods
+ for (var method in config.remote) {
+ if (config.remote.hasOwnProperty(method)) {
+ proxy[method] = _createMethod(config.remote[method], method);
+ }
+ }
+ }
+ pub.down.init();
+ },
+ destroy: function(){
+ trace("destroy");
+ for (var method in config.remote) {
+ if (config.remote.hasOwnProperty(method) && proxy.hasOwnProperty(method)) {
+ delete proxy[method];
+ }
+ }
+ pub.down.destroy();
+ }
+ });
+};
+global.easyXDM = easyXDM;
+})(window, document, location, window.setTimeout, decodeURIComponent, encodeURIComponent);
diff --git a/asterix-examples/src/main/resources/js/easyXDM/tests/index.html b/asterix-examples/src/main/resources/js/easyXDM/tests/index.html
new file mode 100644
index 0000000..0232da9
--- /dev/null
+++ b/asterix-examples/src/main/resources/js/easyXDM/tests/index.html
@@ -0,0 +1,91 @@
+<!doctype html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=100">
+ <title>easyTest results</title>
+ <script type="text/javascript" src="../easyXDM.debug.js">
+ // The first instance of easyXDM
+ </script>
+ <script type="text/javascript">
+ easyXDM._test_global = true;
+ </script>
+ <script type="text/javascript" src="../easyXDM.debug.js">
+ // The second instance of easyXDM (to test noConflict)
+ </script>
+ <script type="text/javascript">
+ // Used in the noConflict test
+ var modules = {
+ easyXDM: easyXDM.noConflict("modules")
+ };
+ </script>
+ <script type="text/javascript">
+ if (location.href.indexOf("src") !== -1) {
+ if (typeof JSON === "undefined") {
+ document.write(unescape("%3Cscript src='../../tools/json2.js' type='text/javascript'%3E%3C/script%3E"));
+ }
+ }
+ else {
+ if (typeof JSON === "undefined") {
+ document.write(unescape("%3Cscript src='../json2.js' type='text/javascript'%3E%3C/script%3E"));
+ }
+ }
+ </script>
+ <script type="text/javascript" src="easyTest.js">
+ </script>
+ <link type="text/css" rel="stylesheet" href="easyTest.css"/>
+ <script type="text/javascript" src="tests.js">
+ </script>
+ <style type="text/css">
+
+ #embedded {
+ height: 100px;
+ }
+
+ #embedded iframe {
+ height: 50px;
+ }
+
+ #log {
+ height: 100px;
+ border: 1px dotted black;
+ overflow: auto
+ }
+
+ .easyTest_messages {
+ height: 300px;
+ border: 1px dotted black;
+ overflow: auto;
+ }
+ </style>
+ </head>
+ <body onload="runTests();">
+ <h1>easyXDM test suite</h1>
+ <h4>easyTest messages</h4>
+ <div id="messages">
+ </div>
+ <h4>easyXDM tracelog</h4>
+ <!-- easyXDM.Debug.trace(msg) will output its messages to any element with the id "log" -->
+ <div id="log">
+ </div>
+ <div id="embedded">
+ </div>
+ <script type="text/javascript">
+ if (location.host.indexOf("easyxdm.net") !== -1) {
+ var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+ document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+ }
+ </script>
+ <script type="text/javascript">
+ if (location.host.indexOf("easyxdm.net") !== -1) {
+ try {
+ var pageTracker = _gat._getTracker("UA-9535591-3");
+ pageTracker._setDomainName(".easyxdm.net");
+ pageTracker._trackPageview();
+ }
+ catch (err) {
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/asterix-examples/src/main/resources/js/easyXDM/tests/s.gif b/asterix-examples/src/main/resources/js/easyXDM/tests/s.gif
new file mode 100644
index 0000000..1d11fa9
--- /dev/null
+++ b/asterix-examples/src/main/resources/js/easyXDM/tests/s.gif
Binary files differ
diff --git a/asterix-examples/src/main/resources/js/easyXDM/tests/test_namespace.html b/asterix-examples/src/main/resources/js/easyXDM/tests/test_namespace.html
new file mode 100644
index 0000000..63d7a3e
--- /dev/null
+++ b/asterix-examples/src/main/resources/js/easyXDM/tests/test_namespace.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>easyXDM</title>
+ <script type="text/javascript" src="easyXDM.debug.js">
+ </script>
+ <script type="text/javascript">
+ var modules = {
+ easyXDM: easyXDM.noConflict("modules")
+ };
+
+ var transport;
+ window.onload = function(){
+ transport = new modules.easyXDM.Socket({
+ local: "../name.html",
+ swf: "../easyxdm.swf",
+ onMessage: function(message, origin){
+ // Echo back the message
+ transport.postMessage(message);
+ }
+ });
+ };
+ window.onunload = function(){
+ if (transport) {
+ transport.destroy();
+ }
+ };
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/asterix-examples/src/main/resources/js/easyXDM/tests/test_rpc.html b/asterix-examples/src/main/resources/js/easyXDM/tests/test_rpc.html
new file mode 100644
index 0000000..11a489f
--- /dev/null
+++ b/asterix-examples/src/main/resources/js/easyXDM/tests/test_rpc.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<html>
+ <head>
+ <title>easyXDM</title>
+ <script type="text/javascript" src="../easyXDM.debug.js">
+ </script>
+ <script type="text/javascript">
+ if (location.href.indexOf("src") !== -1) {
+ if (typeof JSON === "undefined") {
+ document.write(unescape("%3Cscript src='../../tools/json2.js' type='text/javascript'%3E%3C/script%3E"));
+ }
+ }
+ else {
+ if (typeof JSON === "undefined") {
+ document.write(unescape("%3Cscript src='../json2.js' type='text/javascript'%3E%3C/script%3E"));
+ }
+ }
+ </script>
+ <script type="text/javascript">
+ easyXDM.DomHelper.requiresJSON("../json2.js");
+ </script>
+ <script type="text/javascript">
+ var remote = new easyXDM.Rpc({
+ local: "../name.html",
+ swf: "../easyxdm.swf"
+ }, {
+ remote: {
+ voidCallback: {}
+ },
+ local: {
+ voidMethod: function(msg){
+ // Echo back the message
+ remote.voidCallback(msg);
+ },
+ asyncMethod: function(msg, fn){
+ window.setTimeout(function(){
+ // Echo back the msg
+ fn(msg);
+ }, 100);
+ },
+ // use the shorthand notation
+ method: function(msg){
+ // Echo back the msg
+ return msg;
+ },
+ error: function(){
+ var a, b = a.c;
+ },
+ namedParamsMethod: {
+ method: function(params){
+ return params.msg;
+ }
+ }
+ }
+ });
+ window.onunload = function(){
+ if (remote) {
+ remote.destroy();
+ }
+ };
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/asterix-examples/src/main/resources/js/easyXDM/tests/test_transport.html b/asterix-examples/src/main/resources/js/easyXDM/tests/test_transport.html
new file mode 100644
index 0000000..eb405cf
--- /dev/null
+++ b/asterix-examples/src/main/resources/js/easyXDM/tests/test_transport.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html>
+ <head>
+ <title>easyXDM</title>
+ <script type="text/javascript" src="../easyXDM.debug.js">
+ </script>
+ <script type="text/javascript">
+ var transport;
+ window.onload = function(){
+ transport = new easyXDM.Socket({
+ local: "../name.html",
+ swf: "../easyxdm.swf",
+ onMessage: function(message, origin){
+ // Echo back the message
+ transport.postMessage(message);
+ }
+ });
+ };
+ window.onunload = function(){
+ if (transport) {
+ transport.destroy();
+ }
+ };
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/asterix-examples/src/main/resources/js/easyXDM/tests/tests.js b/asterix-examples/src/main/resources/js/easyXDM/tests/tests.js
new file mode 100644
index 0000000..d0a940a
--- /dev/null
+++ b/asterix-examples/src/main/resources/js/easyXDM/tests/tests.js
@@ -0,0 +1,855 @@
+/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
+/*global easyTest, easyXDM, window, modules*/
+var REMOTE = (function(){
+ var remote = location.href;
+ switch (location.host) {
+ case "provider.easyxdm.net":
+ location.href = remote.replace("provider", "consumer");
+ break;
+ case "easyxdm.net":
+ remote = remote.replace("easyxdm.net", "consumer.easyxdm.net");
+ break;
+ case "consumer.easyxdm.net":
+ remote = remote.replace("consumer", "provider");
+ break;
+ case "xdm1":
+ remote = remote.replace("xdm1", "xdm2");
+ break;
+ }
+ return remote.substring(0, remote.lastIndexOf("/"));
+}());
+
+var LOCAL = location.protocol + "//" + location.host + location.pathname.substring(0, location.pathname.lastIndexOf("/"));
+
+function runTests(){
+ var i = 0;
+ easyTest.test([/**Tests for the presence of namespaces and classes*/{
+ name: "Check that the library is complete",
+ steps: [{
+ name: "check for the presence of easyXDM",
+ run: function(){
+ if (this.Assert.isObject(easyXDM) && this.Assert.isString(easyXDM.version)) {
+ this.log("found easyXDM, version=" + easyXDM.version);
+ return true;
+ }
+ return false;
+ }
+ }, {
+ name: "check for the presence of easyXDM.DomHelper",
+ run: function(){
+ return this.Assert.isObject(easyXDM.DomHelper);
+ }
+ }, {
+ name: "check for the presence of easyXDM.Socket",
+ run: function(){
+ return this.Assert.isFunction(easyXDM.Socket);
+ }
+ }, {
+ name: "check for the presence of easyXDM.Rpc",
+ run: function(){
+ return this.Assert.isFunction(easyXDM.Rpc);
+ }
+ }, {
+ name: "check for the presence of easyXDM.stack",
+ run: function(){
+ return this.Assert.isObject(easyXDM.stack);
+ }
+ }, {
+ name: "check for the presence of easyXDM.stack.SameOriginTransport",
+ run: function(){
+ return this.Assert.isFunction(easyXDM.stack.SameOriginTransport);
+ }
+ }, {
+ name: "check for the presence of easyXDM.stack.PostMessageTransport",
+ run: function(){
+ return this.Assert.isFunction(easyXDM.stack.PostMessageTransport);
+ }
+ }, {
+ name: "check for the presence of easyXDM.stack.FlashTransport",
+ run: function(){
+ return this.Assert.isFunction(easyXDM.stack.FlashTransport);
+ }
+ }, {
+ name: "check for the presence of easyXDM.stack.NameTransport",
+ run: function(){
+ return this.Assert.isFunction(easyXDM.stack.NameTransport);
+ }
+ }, {
+ name: "check for the presence of easyXDM.stack.HashTransport",
+ run: function(){
+ return this.Assert.isFunction(easyXDM.stack.HashTransport);
+ }
+ }, {
+ name: "check for the presence of easyXDM.stack.ReliableBehavior",
+ run: function(){
+ return this.Assert.isFunction(easyXDM.stack.ReliableBehavior);
+ }
+ }, {
+ name: "check for the presence of easyXDM.stack.QueueBehavior",
+ run: function(){
+ return this.Assert.isFunction(easyXDM.stack.QueueBehavior);
+ }
+ }, {
+ name: "check for the presence of easyXDM.stack.VerifyBehavior",
+ run: function(){
+ return this.Assert.isFunction(easyXDM.stack.VerifyBehavior);
+ }
+ }, {
+ name: "check for the presence of easyXDM.stack.RpcBehavior",
+ run: function(){
+ return this.Assert.isFunction(easyXDM.stack.RpcBehavior);
+ }
+ }]
+ }, {
+ name: "Check helper functions",
+ runIf: function(){
+ if (location.href.indexOf("/src/") == -1) {
+ return "This can only be run in development.";
+ }
+ },
+ setUp: function(){
+ this.url1 = "http://foo.bar/a/b/c?d=e#f";
+ this.url2 = "http://foo.bar:80/a/b/c?d=e#f";
+ this.url3 = "http://foo.bar:88/a/b/c?d=e#f";
+ this.url4 = "hTtp://Foo.Bar:88/a/b/c?d=e#f";
+
+ },
+ steps: [{
+ name: "getDomainName",
+ run: function(){
+ return easyXDM.getDomainName(this.url1) === "foo.bar";
+ }
+ }, {
+ name: "getLocation",
+ run: function(){
+ return easyXDM.getLocation(this.url1) === "http://foo.bar";
+ }
+ }, {
+ name: "getLocation with standard port",
+ run: function(){
+ return easyXDM.getLocation(this.url2) === "http://foo.bar";
+ }
+ }, {
+ name: "getLocation with non-standard port",
+ run: function(){
+ return easyXDM.getLocation(this.url3) === "http://foo.bar:88";
+ }
+ }, {
+ name: "getLocation with capitals",
+ run: function(){
+ return easyXDM.getLocation(this.url4) === "http://foo.bar:88";
+ }
+ }, {
+ name: "appendQueryParameters",
+ run: function(){
+ return easyXDM.appendQueryParameters(this.url2, {
+ g: "h"
+ }) ===
+ "http://foo.bar:80/a/b/c?d=e&g=h#f";
+ }
+ }]
+ }, {
+ name: "Check the ACL feature",
+ runIf: function(){
+ if (location.href.indexOf("/src/") == -1) {
+ return "This can only be run in development.";
+ }
+ },
+ setUp: function(){
+ this.acl = ["http://www.domain.invalid", "*.domaina.com", "http://dom?inb.com", "^http://domc{3}ain\\.com$"];
+ },
+ steps: [{
+ name: "Match complete string",
+ run: function(){
+ return easyXDM.checkAcl(this.acl, "http://www.domain.invalid");
+ }
+ }, {
+ name: "Match *",
+ run: function(){
+ return easyXDM.checkAcl(this.acl, "http://www.domaina.com");
+ }
+ }, {
+ name: "Match ?",
+ run: function(){
+ return easyXDM.checkAcl(this.acl, "http://domainb.com");
+ }
+ }, {
+ name: "Match RegExp",
+ run: function(){
+ return easyXDM.checkAcl(this.acl, "http://domcccain.com");
+ }
+ }, {
+ name: "No match",
+ run: function(){
+ return !easyXDM.checkAcl(this.acl, "http://foo.com");
+ }
+ }]
+ }, {
+ name: "test easyXDM.Socket{SameOriginTransport}",
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ },
+ steps: [{
+ name: "onReady is fired",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ var messages = 0;
+ this.transport = new easyXDM.Socket({
+ protocol: "4",
+ remote: LOCAL + "/test_transport.html",
+ onMessage: function(message, origin){
+ if (scope.expectedMessage === message) {
+ if (++messages === 2) {
+ scope.notifyResult(true);
+ }
+ }
+ },
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ });
+ }
+ }, {
+ name: "message is echoed back",
+ timeout: 5000,
+ run: function(){
+ this.transport.postMessage(this.expectedMessage);
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }, {
+ name: "destroy",
+ run: function(){
+ this.transport.destroy();
+ return ((document.getElementsByTagName("iframe").length === 0));
+ }
+ }]
+ }, {
+ name: "test easyXDM.Socket{PostMessageTransport}",
+ runIf: function(){
+ if (!("postMessage" in window || "postMessage" in document)) {
+ return "This test requires the HTML5 postMessage interface.";
+ }
+ },
+
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ },
+ steps: [{
+ name: "onReady is fired",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ var messages = 0;
+ this.transport = new easyXDM.Socket({
+ protocol: "1",
+ local: "../name.html",
+ remote: REMOTE + "/test_transport.html",
+ onMessage: function(message, origin){
+ if (scope.expectedMessage === message) {
+ if (++messages === 2) {
+ scope.notifyResult(true);
+ }
+ }
+ },
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ });
+ }
+ }, {
+ name: "message is echoed back",
+ timeout: 5000,
+ run: function(){
+ this.transport.postMessage(this.expectedMessage);
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }, {
+ name: "destroy",
+ run: function(){
+ this.transport.destroy();
+ return ((document.getElementsByTagName("iframe").length === 0));
+ }
+ }]
+ }, {
+ name: "test easyXDM.Socket{FlashTransport}",
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ },
+ steps: [{
+ name: "onReady is fired",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ var messages = 0;
+ this.transport = new easyXDM.Socket({
+ protocol: "6",
+ remote: REMOTE + "/test_transport.html",
+ swf: REMOTE + "/../easyxdm.swf",
+ onMessage: function(message, origin){
+ if (scope.expectedMessage === message) {
+ if (++messages === 2) {
+ scope.notifyResult(true);
+ }
+ }
+ },
+ container: "embedded",
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ });
+ }
+ }, {
+ name: "message is echoed back",
+ timeout: 5000,
+ run: function(){
+ this.transport.postMessage(this.expectedMessage);
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }, {
+ name: "destroy",
+ run: function(){
+ this.transport.destroy();
+ return ((document.getElementsByTagName("iframe").length === 0));
+ }
+ }]
+ }, {
+ name: "test easyXDM.Socket{FrameElementTransport}",
+ runIf: function(){
+ if (!(navigator.product === "Gecko" && "frameElement" in window && !("postMessage" in window) && navigator.userAgent.indexOf('WebKit') == -1)) {
+ return "This test requires an older Gecko browser and the presence of the frameElement object";
+ }
+ },
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ },
+ steps: [{
+ name: "onReady is fired",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ var messages = 0;
+ this.transport = new easyXDM.Socket({
+ protocol: "5",
+ remote: REMOTE + "/test_transport.html",
+ onMessage: function(message, origin){
+ if (scope.expectedMessage === message) {
+ if (++messages === 2) {
+ scope.notifyResult(true);
+ }
+ }
+ },
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ });
+ }
+ }, {
+ name: "message is echoed back",
+ timeout: 5000,
+ run: function(){
+ this.transport.postMessage(this.expectedMessage);
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }, {
+ name: "destroy",
+ run: function(){
+ this.transport.destroy();
+ return ((document.getElementsByTagName("iframe").length === 0));
+ }
+ }]
+ }, {
+ name: "test easyXDM.Socket{NameTransport}",
+ failedMessage: "This might fail in modern browsers due to restrictions in referencing existing windows",
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ },
+ tearDown: function(){
+ this.transport.destroy();
+ if (document.getElementsByTagName("iframe").length !== 0) {
+ throw new Error("iframe still present");
+ }
+ },
+ steps: [{
+ name: "onReady is fired",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ var messages = 0;
+ this.transport = new easyXDM.Socket({
+ protocol: "2", // This is just to override the automatic selection
+ local: "../name.html",
+ remote: REMOTE + "/test_transport.html",
+ remoteHelper: REMOTE + "/../name.html",
+ onMessage: function(message, origin){
+ if (scope.expectedMessage === message) {
+ if (++messages === 2) {
+ scope.notifyResult(true);
+ }
+ }
+ },
+ container: document.getElementById("embedded"),
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ });
+ }
+ }, {
+ name: "message is echoed back",
+ timeout: 5000,
+ run: function(){
+ this.transport.postMessage(this.expectedMessage);
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }]
+ }, {
+ name: "test easyXDM.Socket{HashTransport} using window",
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ },
+ tearDown: function(){
+ this.transport.destroy();
+ if (document.getElementsByTagName("iframe").length !== 0) {
+ throw new Error("iframe still present");
+ }
+ },
+ steps: [{
+ name: "onReady is fired",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ var messages = 0;
+ this.transport = new easyXDM.Socket({
+ protocol: "0", // This is just to override the automatic selection
+ local: window,
+ remote: REMOTE + "/test_transport.html",
+ onMessage: function(message, origin){
+ if (scope.expectedMessage === message) {
+ if (++messages === 2) {
+ scope.notifyResult(true);
+ }
+ }
+ },
+ container: document.getElementById("embedded"),
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ });
+ }
+ }, {
+ name: "message is echoed back",
+ timeout: 5000,
+ run: function(){
+ this.transport.postMessage(this.expectedMessage);
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }]
+ }, {
+ name: "test easyXDM.Socket{HashTransport} with no blank local, available image and resize",
+ failedMessage: "This might fail in modern browsers due to restrictions in referencing existing windows",
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ this.img = document.createElement("img");
+ this.img.src = "s.gif";
+ document.body.appendChild(this.img);
+ },
+ tearDown: function(){
+ document.body.removeChild(this.img);
+ this.transport.destroy();
+ if (document.getElementsByTagName("iframe").length !== 0) {
+ throw new Error("iframe still present");
+ }
+ },
+ steps: [{
+ name: "onReady is fired",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ var messages = 0;
+ this.transport = new easyXDM.Socket({
+ protocol: "0", // This is just to override the automatic selection
+ remote: REMOTE + "/test_transport.html",
+ onMessage: function(message, origin){
+ if (++messages === 2) {
+ scope.notifyResult(true);
+ }
+ },
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ });
+ }
+ }, {
+ name: "message is echoed back",
+ timeout: 5000,
+ run: function(){
+ this.transport.postMessage(this.expectedMessage);
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }]
+ }, {
+ name: "test easyXDM.Socket{HashTransport} with s.gif and polling",
+ failedMessage: "This might fail in modern browsers due to restrictions in referencing existing windows",
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ },
+ tearDown: function(){
+ this.transport.destroy();
+ if (document.getElementsByTagName("iframe").length !== 0) {
+ throw new Error("iframe still present");
+ }
+ },
+ steps: [{
+ name: "onReady is fired",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ var messages = 0;
+ this.transport = new easyXDM.Socket({
+ protocol: "0", // This is just to override the automatic selection
+ local: "s.gif",
+ remote: REMOTE + "/test_transport.html",
+ onMessage: function(message, origin){
+ if (++messages === 2) {
+ scope.notifyResult(true);
+ }
+ },
+ container: document.getElementById("embedded"),
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ });
+ }
+ }, {
+ name: "message is echoed back",
+ timeout: 5000,
+ run: function(){
+ this.transport.postMessage(this.expectedMessage);
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }]
+ }, {
+ name: "test easyXDM.Socket{HashTransport} with fragmentation (8192)",
+ failedMessage: "This might fail in modern browsers due to restrictions in referencing existing windows",
+ setUp: function(){
+ var i = 11;
+ this.expectedMessage = "aaaa";
+ while (i--) {
+ this.expectedMessage += this.expectedMessage;
+ }
+ },
+ tearDown: function(){
+ this.transport.destroy();
+ if (document.getElementsByTagName("iframe").length !== 0) {
+ throw new Error("iframe still present");
+ }
+ },
+ steps: [{
+ name: "onReady is fired",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ this.transport = new easyXDM.Socket({
+ protocol: "0", // This is just to override the automatic selection
+ local: "../name.html",
+ remote: REMOTE + "/test_transport.html",
+ onMessage: function(message, origin){
+ scope.notifyResult(scope.expectedMessage === message);
+ },
+ container: document.getElementById("embedded"),
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ });
+ }
+ }, {
+ name: "message is echoed back",
+ timeout: 5000,
+ run: function(){
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }]
+ }, {
+ name: "test easyXDM.noConflict {SameOriginTransport}",
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ },
+ steps: [{
+ name: "window.easyXDM is released {namespace}",
+ timeout: 5000,
+ run: function(){
+ this.notifyResult(window.easyXDM._test_global && !modules.easyXDM._test_global);
+ }
+ }, {
+ name: "onReady is fired {namespace}",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ var messages = 0;
+ this.transport = new modules.easyXDM.Socket({
+ protocol: "4",
+ remote: LOCAL + "/test_namespace.html",
+ onMessage: function(message, origin){
+ if (scope.expectedMessage === message) {
+ if (++messages === 2) {
+ scope.notifyResult(true);
+ }
+ }
+ },
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ });
+ }
+ }, {
+ name: "message is echoed back {namespace}",
+ timeout: 5000,
+ run: function(){
+ this.transport.postMessage(this.expectedMessage);
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }, {
+ name: "destroy {namespace}",
+ run: function(){
+ this.transport.destroy();
+ return ((document.getElementsByTagName("iframe").length === 0));
+ }
+ }]
+ }, {
+ name: "test easyXDM.Socket{}",
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ },
+ tearDown: function(){
+ this.transport.destroy();
+ if (document.getElementsByTagName("iframe").length !== 0) {
+ throw new Error("iframe still present");
+ }
+ },
+ steps: [{
+ name: "onReady is fired",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ this.transport = new easyXDM.Socket({
+ local: "../name.html",
+ swf: REMOTE + "/../easyxdm.swf",
+ remote: REMOTE + "/test_transport.html",
+ onMessage: function(message, origin){
+ scope.notifyResult((scope.expectedMessage === message));
+ },
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ });
+ }
+ }, {
+ name: "message is echoed back",
+ timeout: 5000,
+ run: function(){
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }]
+ }, {
+ name: "test easyXDM.Socket{} using hash for passing data",
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ },
+ tearDown: function(){
+ this.transport.destroy();
+ if (document.getElementsByTagName("iframe").length !== 0) {
+ throw new Error("iframe still present");
+ }
+ },
+ steps: [{
+ name: "onReady is fired",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ this.transport = new easyXDM.Socket({
+ local: "../name.html",
+ swf: REMOTE + "/../easyxdm.swf",
+ remote: REMOTE + "/test_transport.html?foo=bar",
+ hash: true,
+ onMessage: function(message, origin){
+ scope.notifyResult((scope.expectedMessage === message));
+ },
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ });
+ }
+ }, {
+ name: "message is echoed back",
+ timeout: 5000,
+ run: function(){
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }]
+ }, {
+ name: "test easyXDM.Socket{} with buffering",
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ },
+ tearDown: function(){
+ this.transport.destroy();
+ if (document.getElementsByTagName("iframe").length !== 0) {
+ throw new Error("iframe still present");
+ }
+ },
+ steps: [{
+ name: "Buffered messages are sent",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ this.transport = new easyXDM.Socket({
+ local: "../name.html",
+ swf: REMOTE + "/../easyxdm.swf",
+ remote: REMOTE + "/test_transport.html",
+ onMessage: function(message, origin){
+ scope.notifyResult((scope.expectedMessage === message));
+ }
+ });
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }]
+ }, {
+ name: "test easyXDM.Socket{} with query parameters",
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ },
+ tearDown: function(){
+ this.transport.destroy();
+ if (document.getElementsByTagName("iframe").length !== 0) {
+ throw new Error("iframe still present");
+ }
+ },
+ steps: [{
+ name: "onReady is fired",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ this.transport = new easyXDM.Socket({
+ local: "../name.html",
+ swf: REMOTE + "/../easyxdm.swf",
+ remote: REMOTE + "/test_transport.html?a=b&c=d#foo,faa",
+ onMessage: function(message, origin){
+ scope.notifyResult((scope.expectedMessage === message));
+ },
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ });
+ }
+ }, {
+ name: "message is echoed back",
+ timeout: 5000,
+ run: function(){
+ this.transport.postMessage(this.expectedMessage);
+ }
+ }]
+ }, {
+ name: "test easyXDM.Rpc",
+ setUp: function(){
+ this.expectedMessage = ++i + "_abcd1234%@¤/";
+ },
+ tearDown: function(){
+ this.remote.destroy();
+ if (document.getElementsByTagName("iframe").length !== 0) {
+ throw new Error("iframe still present");
+ }
+ },
+ steps: [{
+ name: "onReady is fired",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ this.remote = new easyXDM.Rpc({
+ local: "../name.html",
+ swf: REMOTE + "/../easyxdm.swf",
+ remote: REMOTE + "/test_rpc.html",
+ remoteHelper: REMOTE + "/../name.html",
+ container: document.getElementById("embedded"),
+ onReady: function(){
+ scope.notifyResult(true);
+ }
+ }, {
+ remote: {
+ voidMethod: {},
+ asyncMethod: {},
+ method: {},
+ error: {},
+ nonexistent: {},
+ namedParamsMethod: {
+ namedParams: true
+ }
+ },
+ local: {
+ voidCallback: {
+ method: function(message){
+ scope.notifyResult((scope.expectedMessage === message));
+ }
+ }
+ }
+ });
+ }
+ }, {
+ name: "void method",
+ timeout: 5000,
+ run: function(){
+ this.remote.voidMethod(this.expectedMessage);
+ }
+ }, {
+ name: "async method",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ this.remote.asyncMethod(this.expectedMessage, function(message){
+ scope.notifyResult((scope.expectedMessage === message));
+ });
+ }
+ }, {
+ name: "regular method",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ this.remote.method(this.expectedMessage, function(message){
+ scope.notifyResult((scope.expectedMessage === message));
+ });
+ }
+ }, {
+ name: "with error",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ this.remote.error(this.expectedMessage, function(){
+ this.notifyResult(false, "success handler called");
+ }, function(message){
+ scope.notifyResult(true);
+ });
+ }
+ }, {
+ name: "calling nonexistent method",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ this.remote.nonexistent(this.expectedMessage, function(){
+ this.notifyResult(false, "success handler called");
+ }, function(message){
+ scope.notifyResult(true);
+ });
+ }
+ }, {
+ name: "using named parameters",
+ timeout: 5000,
+ run: function(){
+ var scope = this;
+ this.remote.namedParamsMethod({
+ msg: this.expectedMessage
+ }, function(message){
+ scope.notifyResult((scope.expectedMessage === message));
+ });
+ }
+ }]
+ }]);
+}