Skip to content

Instantly share code, notes, and snippets.

@jackdoerner
Last active August 29, 2015 14:12
Show Gist options
  • Save jackdoerner/f32c332ca285f5568cb4 to your computer and use it in GitHub Desktop.
Save jackdoerner/f32c332ca285f5568cb4 to your computer and use it in GitHub Desktop.
Crosstalk: fast cross-frame data transfer
/**********
Crosstalk.js
A library for fast data transfer between browser frames.
Copyright (c) 2013 Jack Doerner
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(global){
"use strict";
// Array Remove
if (!global.Array.prototype.remove) {
var Array = {};
Array.prototype = new global.Array;
Object.defineProperty(Array.prototype, 'remove', {
ennumerable:false,
writable:false,
value: function(from, to) {
var ii = from < 0 ? this.length + from : from,
seglen = ((to !== undefined && to < 0) ? (this.length + to) : (to || ii)) - ii + 1,
l = this.length - seglen;
if (seglen > 0 && seglen <= this.length) {
for (; ii <= l; ii++) {
this[ii] = this[ii + seglen];
}
this.length = l;
}
return this;
}
});
}
// PHP-like uniqid
var uniqid = function(prefix, more_entropy) {
// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + revised by: Kankrelune (http://www.webfaktory.info/)
if (typeof prefix === 'undefined') {
prefix = "";
}
var retId;
var formatSeed = function (seed, reqWidth) {
seed = parseInt(seed, 10).toString(16);
if (reqWidth < seed.length) { // so long we split
return seed.slice(seed.length - reqWidth);
}
if (reqWidth > seed.length) {
return Array(1 + (reqWidth - seed.length)).join('0') + seed;
}
return seed;
};
if (!uniqid.uniqidSeed) {
uniqid.uniqidSeed = Math.floor(Math.random() * 0x75bcd15);
}
uniqid.uniqidSeed++;
retId = prefix;
retId += formatSeed(parseInt(new Date().getTime() / 1000, 10), 8);
retId += formatSeed(uniqid.uniqidSeed, 5);
if (more_entropy) {
retId += (Math.random() * 10).toFixed(8).toString();
}
return retId;
}
function grep(haystack, grepFn) {
var result = [];
for (var ii = 0, len = haystack.length; ii < len; ii++) {
if (grepFn(haystack[ii])) result[result.length] = haystack[ii];
}
return result;
}
var C = function(conf){
/**
*
* conf: {
* onMessage: function(event),
* onParentData: function(data)
* onChildData: function(data, target)
* }
*
**/
//Setup
if (conf !== undefined && typeof conf.onMessage === 'function') C.onMessage = conf.onMessage;
if (conf !== undefined && typeof conf.onParentData === 'function') C.onParentData = conf.onParentData;
if (conf !== undefined && typeof conf.onChildData === 'function') C.onChildData = conf.onChildData;
if (!C.isReady) {
C.isReady = true;
C.uid = uniqid();
global.addEventListener('message', function(e) {
if (e.data.header !== undefined && e.data.header === 'Crosstalk.ready') {
var grepFn = function(d){return d.target.contentWindow === e.source;};
var childObjs = grep(C.children, grepFn);
if (childObjs.length > 0) {
childObjs[0].uid = e.data.uid;
if (typeof childObjs[0].onReady === 'function') childObjs[0].onReady();
} else {
C.addChild({
target:e.source.frameElement
});
}
} else if (e.data.header !== undefined && e.data.header === 'Crosstalk.data') {
if (e.source === global.parent) {
var msgData = C.dataChannel[e.data.payload];
delete C.dataChannel[e.data.payload];
if (typeof C.onParentData === 'function') C.onParentData(msgData);
} else {
var grepFn = function(d){return d.target.contentWindow === e.source;};
var childObjs = grep(C.children, grepFn);
if (childObjs.length > 0) {
var msgData = C.dataChannel[e.data.payload];
delete C.dataChannel[e.data.payload];
if (typeof childObjs[0].onData === 'function') {
childObjs[0].onData(msgData, childObjs[0].target);
} else if (typeof C.onChildData === 'function') {
C.onChildData(msgData, childObjs[0].target);
}
}
}
} else if (typeof C.onMessage === 'function') {
C.onMessage(e);
}
});
if (global.parent !== global) global.parent.postMessage({
header:'Crosstalk.ready',
uid: C.uid
}, '*');
}
return C;
};
C.addChild = function(conf) {
/**
*
* conf: {
* target: iframe element of child,
* onMessage: function(event),
* onData: function(data, source),
* onReady: function(source),
* onDisconnect: function()
* }
*
**/
if (conf === undefined || conf.target === undefined) return false;
var childuid = null,
childObj = null;
if (conf.target.contentWindow.Crosstalk !== undefined && conf.target.contentWindow.Crosstalk.uid) {
childuid = conf.target.contentWindow.Crosstalk.uid;
if (typeof conf.onReady === 'function') setTimeout(function(){conf.onReady(conf.target);}, 0);
}
var existingObjs = grep(C.children, function(d) {return d.target === conf.target;});
if (existingObjs.length > 0) {
childObj = existingObjs[0];
if (conf.onMessage !== undefined) childObj.onMessage = conf.onMessage;
if (conf.onData !== undefined) childObj.onData = conf.onData;
if (conf.onReady !== undefined) childObj.onReady = conf.onReady;
if (conf.onDisconnect !== undefined) childObj.onDisconnect = conf.onDisconnect;
} else {
childObj = {
uid:childuid,
target: conf.target,
onMessage: conf.onMessage,
onData: conf.onData,
onReady: conf.onReady,
onDisconnect: conf.onDisconnect
};
C.children[C.children.length] = childObj;
}
if (childObj.disconnectHandler === undefined) {
childObj.disconnectHandler = function(){
if (typeof childObj.onDisconnect === 'function') childObj.onDisconnect();
C.delChild(childObj.target);
};
childObj.target.addEventListener('load', childObj.disconnectHandler);
}
var sendData = function(data) {
if (childObj.target.contentWindow === null) {
C.delChild(childObj.target);
return false;
}
if (childObj.uid === null || childObj.target.contentWindow.Crosstalk === undefined) return false;
else {
var dataId = uniqid();
childObj.target.contentWindow.Crosstalk.dataChannel[dataId] = data;
childObj.target.contentWindow.postMessage({
header:'Crosstalk.data',
uid:childObj.uid,
payload: dataId
}, '*');
return true;
}
};
return sendData;
};
C.delChild = function(criteria) {
if (criteria === undefined || !criteria) return false;
var grepFn = function(el) { return el.target === criteria || el.uid === criteria;};
var results = grep(C.children, grepFn);
for (var ii = 0, len = results.length; ii < len; ii++) {
results[ii].target.removeEventListener('load', results[ii].disconnectHandler);
C.children.splice(C.children.indexOf(results[ii]), 1);
}
};
C.sendToParent = function(data) {
if (global.parent === global) return false;
else {
var dataId = uniqid();
global.parent.Crosstalk.dataChannel[dataId] = data;
global.parent.postMessage({
header:'Crosstalk.data',
uid:C.uid,
payload: dataId
}, '*');
return true;
}
};
C.uid = null;
C.dataChannel = {};
C.children = [];
global.Crosstalk = C;
})(window);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment