@sovanderpol/tabtalk
Version:
Tabtalk enables browsing contexts within the same domain to communicate with each other.
148 lines (135 loc) • 17.1 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else {
var a = factory();
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})(window, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./index.ts");
/******/ })
/************************************************************************/
/******/ ({
/***/ "./index.ts":
/*!******************!*\
!*** ./index.ts ***!
\******************/
/*! exports provided: Tabtalk */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_tabtalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/tabtalk */ \"./src/tabtalk.ts\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"Tabtalk\", function() { return _src_tabtalk__WEBPACK_IMPORTED_MODULE_0__[\"Tabtalk\"]; });\n\n\n\n\n//# sourceURL=webpack:///./index.ts?");
/***/ }),
/***/ "./src/tabtalk.ts":
/*!************************!*\
!*** ./src/tabtalk.ts ***!
\************************/
/*! exports provided: Tabtalk */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Tabtalk\", function() { return Tabtalk; });\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\n/* harmony import */ var _tabtalkMessageFactory__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./tabtalkMessageFactory */ \"./src/tabtalkMessageFactory.ts\");\n\n\nvar Tabtalk = /** @class */ (function () {\n function Tabtalk() {\n var _this = this;\n this.TABTALK_MESSAGE_KEY_PREFIX = 'TABTALK-';\n this.TABTALK_MESSAGE_EVENT_NAME = 'TABTALK_MESSAGE_RECEIVED';\n this.messageEventHandlersMap = {};\n this.garbageCollectionDelay = 3000;\n this.unsubscribeAllEventHandlers = function () {\n Object.keys(_this.messageEventHandlersMap).forEach(function (key) {\n var handler = _this.messageEventHandlersMap[key];\n window.removeEventListener(_this.TABTALK_MESSAGE_EVENT_NAME, handler);\n });\n _this.messageEventHandlersMap = {};\n };\n this.createTabtalkMessageEventHandler = function (callback) {\n return function (event) { return callback(event.detail); };\n };\n /**\n * Handles storage events that are caused by Tabtalk and transmits the serialized message if necessary.\n */\n this.onStorageUpdate = function (event) {\n var key = event.key, value = event.newValue;\n if (_this.isTabtalkMessage(key) && !_this.isMessageBeingDeleted(value)) {\n var message = _this.deserializeMessage(value);\n if (_this.isMessageAddressedToMe(message) || _this.isMessageAddressedToEveryone(message)) {\n var event_1 = new CustomEvent(_this.TABTALK_MESSAGE_EVENT_NAME, { detail: message });\n window.dispatchEvent(event_1);\n }\n }\n };\n /**\n * Deserializes a JSON string to a TabtalkMessage object.\n */\n this.deserializeMessage = function (message) {\n try {\n return JSON.parse(message);\n }\n catch (e) {\n throw new Error(\"[tabtalk] could not deserialize message contents: \" + e);\n }\n };\n this.getAllTabtalkMessageKeys = function () {\n var keys = [];\n for (var i = 0; i < localStorage.length; i++) {\n var key = localStorage.key(i);\n if (_this.isTabtalkMessage(key)) {\n keys.push(key);\n }\n }\n return keys;\n };\n /**\n * Removes a Tabtalk message from the local storage.\n */\n this.clearMessage = function (messageKey) {\n Object(_utils__WEBPACK_IMPORTED_MODULE_0__[\"throwIfLocalStorageUnavailable\"])();\n localStorage.removeItem(messageKey);\n };\n this.generateMessageKey = function (senderId) {\n return \"\" + _this.TABTALK_MESSAGE_KEY_PREFIX + senderId + \"-\" + Date.now();\n };\n this.isTabtalkMessage = function (storageEventKey) { return storageEventKey.match(new RegExp(\"^\" + _this.TABTALK_MESSAGE_KEY_PREFIX)); };\n this.isMessageAddressedToMe = function (message) { return message.meta.recipientId === _this.id; };\n this.isMessageAddressedToEveryone = function (message) { return message.meta.recipientId === null; };\n this.isMessageBeingDeleted = function (serializedMessage) { return !serializedMessage; };\n this.isMessageDueForGarbageCollection = function (message) { return message.meta.createdAt < (Date.now() - _this.garbageCollectionDelay); };\n Object(_utils__WEBPACK_IMPORTED_MODULE_0__[\"throwIfLocalStorageUnavailable\"])();\n this.id = Object(_utils__WEBPACK_IMPORTED_MODULE_0__[\"generateUuid\"])();\n window.addEventListener('storage', this.onStorageUpdate);\n }\n /**\n * Permanently deactivates the Tabtalk instance by removing event handlers. Note that the event\n * handlers of instances of Tabtalk within the same browsing context will be affected as well.\n */\n Tabtalk.prototype.destroy = function () {\n window.removeEventListener('storage', this.onStorageUpdate);\n this.unsubscribeAllEventHandlers();\n };\n Tabtalk.prototype.getId = function () {\n return this.id;\n };\n /**\n * Set the garbage collection delay. All messages that have existed for longer than this\n * delay will be considered due for garbage collection.\n * @param delay Time in milliseconds between creation and garbage collection.\n */\n Tabtalk.prototype.setGarbageCollectionDelay = function (delay) {\n this.garbageCollectionDelay = delay;\n };\n /**\n * Adds a Tabtalk message event handler with the passed callback.\n */\n Tabtalk.prototype.subscribe = function (callback) {\n if (!callback) {\n console.warn('[tabtalk] falsy callback passed.');\n return;\n }\n var handler = this.createTabtalkMessageEventHandler(callback);\n this.messageEventHandlersMap[callback.toString()] = handler;\n window.addEventListener(this.TABTALK_MESSAGE_EVENT_NAME, handler);\n };\n /**\n * Removes the Tabtalk message event handler that belongs to the passed callback.\n * If no callback is passed it will remove all the Tabtalk message event handlers.\n *\n * @param callbackToUnsubscribe (optional) The callback of the event listener to remove.\n */\n Tabtalk.prototype.unsubscribe = function (callbackToUnsubscribe) {\n if (callbackToUnsubscribe) {\n var handler = this.messageEventHandlersMap[callbackToUnsubscribe.toString()];\n window.removeEventListener(this.TABTALK_MESSAGE_EVENT_NAME, handler);\n delete this.messageEventHandlersMap[callbackToUnsubscribe.toString()];\n }\n else {\n this.unsubscribeAllEventHandlers();\n }\n };\n /**\n * Sends a message to all Tabtalk instances.\n */\n Tabtalk.prototype.broadcast = function (body) {\n this.sendMessage(null, body);\n };\n /**\n * Sends a message to a specific Tabtalk instance.\n */\n Tabtalk.prototype.sendMessage = function (recipientId, body) {\n var _this = this;\n if (recipientId === void 0) { recipientId = null; }\n Object(_utils__WEBPACK_IMPORTED_MODULE_0__[\"throwIfLocalStorageUnavailable\"])();\n var factory = new _tabtalkMessageFactory__WEBPACK_IMPORTED_MODULE_1__[\"TabtalkMessageFactory\"]();\n factory.setSenderId(this.id);\n factory.setRecipientId(recipientId);\n factory.setBody(body);\n var messageKey = this.generateMessageKey(this.id);\n var serializedMessage = JSON.stringify(factory.build());\n window.localStorage.setItem(messageKey, serializedMessage);\n setTimeout(function () { return _this.clearMessage(messageKey); }, this.garbageCollectionDelay);\n };\n /**\n * Removes all Tabtalk messages from local storage that are due for garbage collection.\n */\n Tabtalk.prototype.garbageCollect = function () {\n var _this = this;\n var tabtalkMessageKeys = this.getAllTabtalkMessageKeys();\n tabtalkMessageKeys.forEach(function (tabtalkMessageKey) {\n var message = localStorage.getItem(tabtalkMessageKey);\n if (message) {\n var deserializedMessage = _this.deserializeMessage(message);\n if (_this.isMessageDueForGarbageCollection(deserializedMessage)) {\n localStorage.removeItem(tabtalkMessageKey);\n }\n }\n ;\n });\n };\n return Tabtalk;\n}());\n\n\n\n//# sourceURL=webpack:///./src/tabtalk.ts?");
/***/ }),
/***/ "./src/tabtalkMessageFactory.ts":
/*!**************************************!*\
!*** ./src/tabtalkMessageFactory.ts ***!
\**************************************/
/*! exports provided: VALIDATION_ERROR_INVALID_SENDER_ID, TabtalkMessageFactory */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"VALIDATION_ERROR_INVALID_SENDER_ID\", function() { return VALIDATION_ERROR_INVALID_SENDER_ID; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"TabtalkMessageFactory\", function() { return TabtalkMessageFactory; });\nvar VALIDATION_ERROR_INVALID_SENDER_ID = 'Invalid senderId';\nvar TabtalkMessageFactory = /** @class */ (function () {\n function TabtalkMessageFactory() {\n var _this = this;\n this.setBody = function (body) {\n _this.body = body;\n };\n this.setRecipientId = function (recipientId) {\n _this.recipientId = recipientId;\n };\n this.setSenderId = function (senderId) {\n _this.senderId = senderId;\n };\n this.throwIfInvalidMessageProperties = function () {\n var validationErrors = [];\n if (typeof _this.senderId != 'string') {\n validationErrors.push(VALIDATION_ERROR_INVALID_SENDER_ID);\n }\n if (validationErrors.length) {\n throw new Error('[tabtalk] invalid Tabtalk message:' + validationErrors.join(', '));\n }\n };\n this.build = function () {\n _this.throwIfInvalidMessageProperties();\n return {\n body: _this.body,\n meta: {\n recipientId: _this.recipientId,\n senderId: _this.senderId,\n createdAt: Date.now()\n }\n };\n };\n }\n return TabtalkMessageFactory;\n}());\n\n\n\n//# sourceURL=webpack:///./src/tabtalkMessageFactory.ts?");
/***/ }),
/***/ "./src/utils.ts":
/*!**********************!*\
!*** ./src/utils.ts ***!
\**********************/
/*! exports provided: isLocalStorageAvailable, throwIfLocalStorageUnavailable, generateUuid, getRandomLetter, getRandomInteger */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"isLocalStorageAvailable\", function() { return isLocalStorageAvailable; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"throwIfLocalStorageUnavailable\", function() { return throwIfLocalStorageUnavailable; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"generateUuid\", function() { return generateUuid; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"getRandomLetter\", function() { return getRandomLetter; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"getRandomInteger\", function() { return getRandomInteger; });\nvar isLocalStorageAvailable = function () { return !!window.localStorage; };\nvar throwIfLocalStorageUnavailable = function () {\n if (!isLocalStorageAvailable()) {\n throw new Error('[tabtalk] local storage is not available.');\n }\n};\n/**\n * Returns a pseudo-random uuid.\n */\nvar generateUuid = function () {\n var uuid = '';\n for (var i = 0; i < 10; i++) {\n uuid = uuid.concat(getRandomLetter());\n }\n return uuid + \"-\" + Date.now();\n};\n/**\n * Returns a random letter ranging from a to z.\n * @param isUpperCase (default true) Determines whether the random letter should be upper case.\n */\nvar getRandomLetter = function (isUpperCase) {\n if (isUpperCase === void 0) { isUpperCase = true; }\n var letter = String.fromCharCode(97 + getRandomInteger(25));\n return isUpperCase ? letter.toUpperCase() : letter;\n};\nvar getRandomInteger = function (maxInteger) {\n return Math.round(Math.random() * Math.floor(maxInteger));\n};\n\n\n//# sourceURL=webpack:///./src/utils.ts?");
/***/ })
/******/ });
});