Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.3.0 #50

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 125 additions & 45 deletions src/crosstab.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* global define */
/* global global */
/*!
* crosstab JavaScript Library v0.2.11
* crosstab JavaScript Library v0.3.0
* https://github.com/tejacques/crosstab
*
* License: Apache 2.0 https://github.com/tejacques/crosstab/blob/master/LICENSE
Expand Down Expand Up @@ -30,13 +32,18 @@
var isMobile = (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(useragent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(useragent.substr(0, 4)));

var localStorage;
var localStorageAvailable = true;
try {
localStorage = window.localStorage;
localStorage = window['ie8-eventlistener/storage'] || window.localStorage;
} catch (e) {
// New versions of Firefox throw a Security exception
// if cookies are disabled. See
// https://bugzilla.mozilla.org/show_bug.cgi?id=1028153
localStorageAvailable = false;
setTimeout(function() {
throw e;
});
}

// When Safari on OS X or iOS is in private browsing mode,
Expand All @@ -46,20 +53,29 @@
// to add something to storage that exceeded the quota."
var setItemAllowed = true;
try {
localStorage.setItem('__crosstab', '');
localStorage.removeItem('__crosstab');
var testkey = 'crosstab.TEST_KEY';
var testvalue = (+new Date()).toString() + Math.random();
localStorage.setItem(testkey, testvalue);
localStorage.removeItem(testkey);
} catch (e) {
setTimeout(function() {
throw e;
});
setItemAllowed = false;
}

// Other reasons
var frozenTabEnvironment = false;

var supportLogged = false;
function notSupported() {
if (crosstab.supported) return;
if (crosstab.supported || supportLogged) {
return;
}

var errorMsg = 'crosstab not supported';
var reasons = [];
if (!localStorage) {
if (!localStorageAvailable) {
reasons.push('localStorage not availabe');
}
if (!window.addEventListener) {
Expand All @@ -78,8 +94,12 @@
if (reasons.length > 0) {
errorMsg += ': ' + reasons.join(', ');
}
supportLogged = true;
var err = new Error(errorMsg);

throw new Error(errorMsg);
setTimeout(function() {
throw err;
});
}

// --- Utility ---
Expand Down Expand Up @@ -192,6 +212,22 @@
return keys;
}, {});

util.pad = function(num, width, padChar) {
padChar = padChar || '0';
var numStr = (num.toString());

if (numStr.length >= width) {
return numStr;
}

return new Array(width - numStr.length + 1).join(padChar) + numStr;
};

util.generateId = function () {
/*jshint bitwise: false*/
return util.now().toString() + util.pad((Math.random() * 0x7FFFFFFF) | 0, 10);
};

// --- Events ---
// node.js style events, with the main difference being able
// to add/remove events by key.
Expand Down Expand Up @@ -384,7 +420,7 @@
var message = eventValue.data;
// only handle if this message was meant for this tab.
if (!message.destination || message.destination === crosstab.id) {
eventHandler.emit(message.event, message);
emitMessage(message);
}
} else if (event.key === util.keys.FROZEN_TAB_ENVIRONMENT) {
frozenTabEnvironment = eventValue.data;
Expand All @@ -395,6 +431,10 @@
}

function setLocalStorageItem(key, data) {
if (!crosstab.supported) {
return;
}

var storageItem = {
id: crosstab.id,
data: data,
Expand Down Expand Up @@ -566,28 +606,32 @@
}
});

function pad(num, width, padChar) {
padChar = padChar || '0';
var numStr = (num.toString());

if (numStr.length >= width) {
return numStr;
}

return new Array(width - numStr.length + 1).join(padChar) + numStr;
// --- Setup message sending and handling ---
function makeReplyFn(message) {
return function(data) {
broadcast('reply-'+message.id, data, message.origin);
};
}

util.generateId = function () {
/*jshint bitwise: false*/
return util.now().toString() + pad((Math.random() * 0x7FFFFFFF) | 0, 10);
};
function emitMessage(message) {
eventHandler.emit(message.event, message, makeReplyFn(message));
}

// --- Setup message sending and handling ---
function broadcast(event, data, destination) {
function broadcast(event, data, destination, callback, timeout) {
if (!crosstab.supported) {
notSupported();
}

// Handle parameter overloads
if (arguments.length === 1 && typeof (event) == 'object') {
var args = event;
event = args.event;
data = data || args.data;
destination = destination || args.destination;
callback = callback || args.callback;
timeout = timeout || args.timeout;
}

var message = {
id: util.generateId(),
event: event,
Expand All @@ -597,19 +641,33 @@
timestamp: util.now()
};

if (timeout > 0) {
message.timeout = timeout;
}

if (callback) {
crosstab.once('reply-'+message.id, callback);

if(timeout > 0) {
setTimeout(function() {
callback(null, 'timeout');
}, timeout);
}
}

// If the destination differs from the origin send it out, otherwise
// handle it locally
if (message.destination !== message.origin) {
if (crosstab.supported && message.destination !== message.origin) {
setLocalStorageItem(util.keys.MESSAGE_KEY, message);
}

if (!message.destination || message.destination === message.origin) {
eventHandler.emit(event, message);
emitMessage(message);
}
}

function broadcastMaster(event, data) {
broadcast(event, data, getMaster().id);
function broadcastMaster(event, data, callback, timeout) {
broadcast(event, data, getMaster().id, callback, timeout);
}

// ---- Return ----
Expand All @@ -618,7 +676,10 @@
setupComplete = true;
});

var crosstab = function (fn) {
var crosstab = function (arg) {
var fn = typeof (arg) == 'function' ? arg : function() {
crosstab.broadcast(arg);
};
if (setupComplete) {
fn();
} else {
Expand All @@ -627,13 +688,40 @@
};

crosstab.id = util.generateId();
crosstab.supported = !!localStorage && window.addEventListener && !isMobile && setItemAllowed;
crosstab.supported = localStorageAvailable
&& window.addEventListener
&& !isMobile
&& setItemAllowed;
crosstab.util = util;
crosstab.broadcast = broadcast;
crosstab.broadcastMaster = broadcastMaster;
crosstab.on = util.events.on;
crosstab.once = util.events.once;
crosstab.off = util.events.off;
crosstab.onDirect = function (event, listener, key) {
var directListener = function (message) {
// only handle direct messages
if (!message.destination || message.destination !== crosstab.id) {
return;
}

listener.apply(null, arguments);
};

return crosstab.on(event, directListener, key);
};
crosstab.onMaster = function (event, listener, key) {
var masterListener = function (message) {
// only handle messages from master
if (message.origin !== getMasterId()) {
return;
}

listener.apply(null, arguments);
};

return crosstab.on(event, masterListener, key);
};

// 10 minute timeout
var CACHE_TIMEOUT = 10 * 60 * 1000;
Expand Down Expand Up @@ -777,31 +865,23 @@
}
}

// --- Check if crosstab is supported ---
if (!crosstab.supported) {
crosstab.broadcast = notSupported;
} else {
if (window.addEventListener) {
// ---- Setup Storage Listener
window.addEventListener('storage', onStorageEvent, false);
// start with the `beforeunload` event due to IE11
window.addEventListener('beforeunload', unload, false);
// swap `beforeunload` to `unload` after DOM is loaded
window.addEventListener('DOMContentLoaded', swapUnloadEvents, false);
}

util.events.on('PING', function (message) {
// only handle direct messages
if (!message.destination || message.destination !== crosstab.id) {
return;
}

if (util.now() - message.timestamp < PING_TIMEOUT) {
crosstab.broadcast('PONG', null, message.origin);
}
});
crosstab.onDirect('PING', function (message) {
if (util.now() - message.timestamp < PING_TIMEOUT) {
crosstab.broadcast('PONG', null, message.origin);
}
});

setInterval(keepaliveLoop, TAB_KEEPALIVE);
keepaliveLoop();
}
setInterval(keepaliveLoop, TAB_KEEPALIVE);
keepalive();

module.exports = crosstab;

Expand Down