/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
/*global easyXDM, window, escape, unescape */

/** * The namespace for the transports */ easyXDM.transport = {};
/** * The namespace for the transport behaviors */ easyXDM.transport.behaviors = {};
/** * @class easyXDM.transport.behaviors.ReliableBehavior * This is a behavior that tries to make the underlying transport reliable by using acknowledgements. * @constructor * @param {Object} settings * @cfg {Number} timeout How long it should wait before resending * @cfg {Number} tries How many times it should try before giving up * @namespace easyXDM.transport.behaviors */ easyXDM.transport.behaviors.ReliableBehavior = function(settings){ var pub, // the public interface timer, // timer to wait for acks current, // the current message beging sent next, // the next message to be sent, to support piggybacking acks sendId = 0, // the id of the last message sent sendCount = 0, // how many times we hav tried resending maxTries = settings.tries || 5, timeout = settings.timeout, // receiveId = 0, // the id of the last message received callback; // the callback to execute when we have a confirmed success/failure // #ifdef debug easyXDM.Debug.trace("ReliableBehavior: settings.timeout=" + settings.timeout); easyXDM.Debug.trace("ReliableBehavior: settings.tries=" + settings.tries); // #endif return (pub = { incomming: function(message, origin){ var indexOf = message.indexOf("_"), ack = parseInt(message.substring(0, indexOf), 10), id; // #ifdef debug easyXDM.Debug.trace("ReliableBehavior: received ack: " + ack + ", last sent was: " + sendId); // #endif message = message.substring(indexOf + 1); indexOf = message.indexOf("_"); id = parseInt(message.substring(0, indexOf), 10); indexOf = message.indexOf("_"); message = message.substring(indexOf + 1); // #ifdef debug easyXDM.Debug.trace("ReliableBehavior: lastid " + receiveId + ", this " + id); // #endif if (timer && ack === sendId) { window.clearTimeout(timer); timer = null; // #ifdef debug easyXDM.Debug.trace("ReliableBehavior: message delivered"); // #endif if (callback) { window.setTimeout(function(){ callback(true); }, 0); } } if (id !== 0) { if (id !== receiveId) { receiveId = id; message = message.substring(id.length + 1); // #ifdef debug easyXDM.Debug.trace("ReliableBehavior: sending ack, passing on " + message); // #endif pub.down.outgoing(id + "_0_ack", origin); // we must give the other end time to pick up the ack window.setTimeout(function(){ pub.up.incomming(message, origin); }, settings.timeout / 2); } // #ifdef debug else { easyXDM.Debug.trace("ReliableBehavior: duplicate msgid " + id + ", resending ack"); pub.down.outgoing(id + "_0_ack", origin); } // #endif } }, outgoing: function(message, origin, fn){ callback = fn; sendCount = 0; current = { data: receiveId + "_" + (++sendId) + "_" + message, origin: origin }; // Keep resending until we have an ack (function send(){ timer = null; if (++sendCount > maxTries) { if (callback) { // #ifdef debug easyXDM.Debug.trace("ReliableBehavior: delivery failed"); // #endif window.setTimeout(function(){ callback(false); }, 0); } } else { // #ifdef debug easyXDM.Debug.trace("ReliableBehavior: " + (sendCount === 1 ? "sending " : "resending ") + sendId + ", tryCount " + sendCount); // #endif pub.down.outgoing(current.data, current.origin); timer = window.setTimeout(send, settings.timeout); } }()); }, destroy: function(){ if (timer) { window.clearInterval(timer); } pub.up.destroy(); }, callback: function(success){ pub.up.callback(success); } }); }; /** * @class easyXDM.transport.behaviors.QueueBehavior * This is a behavior that enables queueing of messages.
* It will buffer incomming messages and will 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. * @constructor * @param {Object} settings * @cfg {Number} maxLength The maximum length of each outgoing message. Set this to enable fragmentation. * @namespace easyXDM.transport.behaviors */ easyXDM.transport.behaviors.QueueBehavior = function(settings){ var pub, queue = [], waiting = false, incomming = "", destroying, maxLength = (settings) ? settings.maxLength : 0; function dispatch(){ if (waiting || queue.length === 0 || destroying) { return; } // #ifdef debug easyXDM.Debug.trace("dispatching from queue"); // #endif waiting = true; var message = queue.shift(); pub.down.outgoing(message.data, message.origin, function(success){ waiting = false; if (message.callback) { window.setTimeout(function(){ message.callback(success); }, 0); } dispatch(); }); } return (pub = { incomming: function(message, origin){ var indexOf = message.indexOf("_"), seq = parseInt(message.substring(0, indexOf), 10); incomming += message.substring(indexOf + 1); if (seq === 0) { // #ifdef debug easyXDM.Debug.trace("last fragment received"); // #endif pub.up.incomming(incomming, origin); incomming = ""; } // #ifdef debug else { easyXDM.Debug.trace("awaiting more fragments, seq=" + message); } // #endif }, outgoing: function(message, origin, fn){ var fragments = [], fragment; if (maxLength) { while (message.length !== 0) { fragment = message.substring(0, maxLength); message = message.substring(fragment.length); fragments.push(fragment); } } else { fragments.push(message); } while ((fragment = fragments.shift())) { // #ifdef debug easyXDM.Debug.trace("enqueuing"); // #endif queue.push({ data: fragments.length + "_" + fragment, origin: origin, callback: fragments.length === 0 ? fn : null }); } dispatch(); }, destroy: function(){ // #ifdef debug easyXDM.Debug.trace("QueueBehavior#destroy"); // #endif destroying = true; pub.up.destroy(); }, callback: function(success){ pub.up.callback(success); } }); }; /** * @class easyXDM.transport.behaviors.VerifyBehavior * This behavior will verify that communication with the remote end is possible, and will also sign all outgoing, * and verify all incomming messages. This removes the risk of someone hijacking the iframe to send malicious messages. * @constructor * @param {Object} settings * @cfg {Boolean} initiate If the verification should be initiated from this end. * @namespace easyXDM.transport.behaviors */ easyXDM.transport.behaviors.VerifyBehavior = function(settings){ var pub, mySecret, theirSecret, verified = false; if (typeof settings.initiate === "undefined") { throw new Error("settings.initiate is not set"); } function startVerification(){ // #ifdef debug easyXDM.Debug.trace("VerifyBehavior: requesting verification"); // #endif mySecret = Math.random().toString(16).substring(2); pub.down.outgoing(mySecret); } return (pub = { incomming: function(message, origin){ var indexOf = message.indexOf("_"); if (indexOf === -1) { if (message === mySecret) { // #ifdef debug easyXDM.Debug.trace("VerifyBehavior: verified, calling callback"); // #endif pub.up.callback(true); } else if (!theirSecret) { // #ifdef debug easyXDM.Debug.trace("VerifyBehavior: returning secret"); // #endif theirSecret = message; if (!settings.initiate) { startVerification(); } pub.down.outgoing(message); } } else { if (message.substring(0, indexOf) === theirSecret) { // #ifdef debug easyXDM.Debug.trace("VerifyBehavior: valid"); // #endif pub.up.incomming(message.substring(indexOf + 1), origin); } // #ifdef debug else { easyXDM.Debug.trace("VerifyBehavior: invalid secret:" + message.substring(0, indexOf) + ", was expecting:" + theirSecret); } // #endif } }, outgoing: function(message, origin, fn){ pub.down.outgoing(mySecret + "_" + message, origin, fn); }, destroy: function(){ pub.up.destroy(); }, callback: function(success){ if (settings.initiate) { startVerification(); } } }); };