| /** |
| * 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 (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);
|