UNPKG

@sovanderpol/tabtalk

Version:

Tabtalk enables browsing contexts within the same domain to communicate with each other.

148 lines (135 loc) 17.1 kB
(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?"); /***/ }) /******/ }); });