From 796d5277c47a0fc47b28ea782fd2db588087c1fd Mon Sep 17 00:00:00 2001 From: Tom Jacques Date: Mon, 27 Jul 2015 02:04:24 -0400 Subject: [PATCH 1/4] 0.3.0 beginnings --- src/crosstab.js | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/crosstab.js b/src/crosstab.js index 5a2bc91..4393aee 100644 --- a/src/crosstab.js +++ b/src/crosstab.js @@ -634,6 +634,18 @@ 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(message); + }; + + return crosstab.on(event, directListener, key); + }; // 10 minute timeout var CACHE_TIMEOUT = 10 * 60 * 1000; @@ -777,31 +789,21 @@ } } - // --- Check if crosstab is supported --- - if (!crosstab.supported) { - crosstab.broadcast = notSupported; - } else { - // ---- 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; - } + // ---- 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); - 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); + keepaliveLoop(); module.exports = crosstab; From ddac1fc5086c001f47d22657ed495a41e90f34e2 Mon Sep 17 00:00:00 2001 From: Tom Jacques Date: Fri, 21 Aug 2015 20:39:22 -0400 Subject: [PATCH 2/4] Change the way notSupported works -- will now act as a local only event emitter. --- src/crosstab.js | 93 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/src/crosstab.js b/src/crosstab.js index 4393aee..158357d 100644 --- a/src/crosstab.js +++ b/src/crosstab.js @@ -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 @@ -30,6 +32,7 @@ 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; @@ -37,6 +40,10 @@ // 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, @@ -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) { @@ -78,8 +94,12 @@ if (reasons.length > 0) { errorMsg += ': ' + reasons.join(', '); } - - throw new Error(errorMsg); + supportLogged = true; + var err = new Error(errorMsg); + + setTimeout(function() { + throw err; + }); } // --- Utility --- @@ -191,6 +211,22 @@ keys[val] = 1; 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 @@ -395,6 +431,10 @@ } function setLocalStorageItem(key, data) { + if (!crosstab.supported) { + return; + } + var storageItem = { id: crosstab.id, data: data, @@ -566,22 +606,6 @@ } }); - 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; - } - - util.generateId = function () { - /*jshint bitwise: false*/ - return util.now().toString() + pad((Math.random() * 0x7FFFFFFF) | 0, 10); - }; - // --- Setup message sending and handling --- function broadcast(event, data, destination) { if (!crosstab.supported) { @@ -599,7 +623,7 @@ // 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); } @@ -627,7 +651,10 @@ }; 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; @@ -789,12 +816,14 @@ } } - // ---- 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); + 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); + } crosstab.onDirect('PING', function (message) { if (util.now() - message.timestamp < PING_TIMEOUT) { @@ -803,7 +832,7 @@ }); setInterval(keepaliveLoop, TAB_KEEPALIVE); - keepaliveLoop(); + keepalive(); module.exports = crosstab; From 31f98f39cce54e84a26983fda280b7f2a32f7594 Mon Sep 17 00:00:00 2001 From: Tom Jacques Date: Fri, 21 Aug 2015 20:51:29 -0400 Subject: [PATCH 3/4] Whitespace fix --- src/crosstab.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/crosstab.js b/src/crosstab.js index 158357d..69a0e37 100644 --- a/src/crosstab.js +++ b/src/crosstab.js @@ -96,7 +96,7 @@ } supportLogged = true; var err = new Error(errorMsg); - + setTimeout(function() { throw err; }); @@ -211,7 +211,7 @@ keys[val] = 1; return keys; }, {}); - + util.pad = function(num, width, padChar) { padChar = padChar || '0'; var numStr = (num.toString()); @@ -434,7 +434,7 @@ if (!crosstab.supported) { return; } - + var storageItem = { id: crosstab.id, data: data, From 5a5772ef667f390c720a8d77c27aa8919af65f37 Mon Sep 17 00:00:00 2001 From: Tom Jacques Date: Sat, 22 Aug 2015 19:01:11 -0400 Subject: [PATCH 4/4] Start work on #39: Higher level API --- src/crosstab.js | 63 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/src/crosstab.js b/src/crosstab.js index 69a0e37..b917072 100644 --- a/src/crosstab.js +++ b/src/crosstab.js @@ -420,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; @@ -607,11 +607,31 @@ }); // --- Setup message sending and handling --- - function broadcast(event, data, destination) { + function makeReplyFn(message) { + return function(data) { + broadcast('reply-'+message.id, data, message.origin); + }; + } + + function emitMessage(message) { + eventHandler.emit(message.event, message, makeReplyFn(message)); + } + + 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, @@ -621,6 +641,20 @@ 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 (crosstab.supported && message.destination !== message.origin) { @@ -628,12 +662,12 @@ } 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 ---- @@ -642,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 { @@ -668,11 +705,23 @@ return; } - listener(message); + 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;