1'use strict'; 2 3(function (global, factory) { 4 if (typeof define === 'function' && define.amd) { 5 define([], factory); 6 } else { 7 global.hermes = factory(); 8 } 9})(window, () => { 10 const callbacks = {}; 11 12 function on(name, callback) { 13 if (!(name in callbacks)) { 14 callbacks[name] = []; 15 } 16 callbacks[name].push(callback); 17 } 18 19 function off(name, callback) { 20 if (name in callbacks) { 21 if (typeof callback === 'function') { 22 const index = callbacks[name].indexOf(callback); 23 callbacks[name].splice(index, 1); 24 } 25 if (typeof callback !== 'function' || callbacks[name].length === 0) { 26 delete callbacks[name]; 27 } 28 } 29 } 30 31 function broadcast(name, data) { 32 if (name in callbacks) { 33 callbacks[name].forEach(function (callback) { 34 return callback(data); 35 }); 36 } 37 } 38 39 function broadcastChannelApiFactory() { 40 /** 41 * The BroadcastChannel API allows simple communication between 42 * browsing contexts (including tabs), sort of like a PubSub that 43 * works across different tabs. This is the ideal solution for 44 * messaging between different tabs, but it is relatively new. 45 * 46 * Support table for BroadcastChannel: http://caniuse.com/#feat=broadcastchannel 47 **/ 48 49 const channel = new BroadcastChannel('hermes'); 50 channel.onmessage = (e) => broadcast(e.data.name, e.data.data); 51 52 function send(name, data, ...args) { 53 const includeSelf = 54 args.length > 2 && args[2] !== undefined ? args[2] : false; 55 56 channel.postMessage({ name: name, data: data }); 57 if (includeSelf) { 58 broadcast(name, data); 59 } 60 } 61 62 return { on: on, off: off, send: send }; 63 } 64 65 function sharedWorkerApiFactory() { 66 /** 67 * A SharedWorker is a script that is run by the browser in the 68 * background. Different browsing contexts (including tabs) from the 69 * same origin have shared accesss to the SharedWorker instance and 70 * can communicate with it. We are taking advantage of these features 71 * to use it as a messaging channel which simply forwards messages it 72 * receives to the other connected tabs. 73 * 74 * Support table for SharedWorker: http://caniuse.com/#feat=sharedworkers 75 **/ 76 77 const selector = '[src$="hermes.js"],[src$="hermes.min.js"]'; 78 const script = document.querySelector(selector); 79 const scriptUrl = new URL(script.src); 80 const workerPath = scriptUrl.pathname.replace( 81 /hermes(\.min)?\.js/, 82 'hermes-worker$1.js', 83 ); 84 85 const worker = new SharedWorker(workerPath, 'hermes'); 86 87 worker.port.start(); 88 worker.port.onmessage = (e) => broadcast(e.data.name, e.data.data); 89 90 function send(name, data, ...args) { 91 const includeSelf = 92 args.length > 2 && args[2] !== undefined ? args[2] : false; 93 94 worker.port.postMessage({ name: name, data: data }); 95 if (includeSelf) { 96 broadcast(name, data); 97 } 98 } 99 100 return { on: on, off: off, send: send }; 101 } 102 103 function localStorageApiFactory() { 104 /** 105 * The localStorage is a key-value pair storage, and browser tabs from 106 * the same origin have shared access to it. Whenever something 107 * changes in the localStorage, the window object emits the `storage` 108 * event in the other tabs letting them know about the change. 109 * 110 * Support table for localStorage: http://caniuse.com/#search=webstorage 111 **/ 112 113 const storage = window.localStorage; 114 const prefix = '__hermes:'; 115 const queue = {}; 116 117 window.addEventListener('storage', function (e) { 118 if (e.key.indexOf(prefix) === 0 && e.oldValue === null) { 119 const name = e.key.replace(prefix, ''); 120 const data = JSON.parse(e.newValue); 121 broadcast(name, data); 122 } 123 }); 124 125 window.addEventListener('storage', function (e) { 126 if (e.key.indexOf(prefix) === 0 && e.newValue === null) { 127 const name = e.key.replace(prefix, ''); 128 if (name in queue) { 129 send(name, queue[name].shift()); 130 if (queue[name].length === 0) { 131 delete queue[name]; 132 } 133 } 134 } 135 }); 136 137 function send(name, data, ...args) { 138 const includeSelf = 139 args.length > 2 && args[2] !== undefined ? args[2] : false;
140 141 const key = prefix + name; 142 if (storage.getItem(key) === null) { 143 storage.setItem(key, JSON.stringify(data)); 144 storage.removeItem(key); 145 if (includeSelf) { 146 broadcast(name, data); 147 } 148 } else { 149 // The queueing system ensures that multiple calls to the send 150 // function using the same name does not override each other's 151 // values and makes sure that the next value is sent only when 152 // the previous one has already been deleted from the storage. 153 // NOTE: This could just be trying to solve a problem that is 154 // very unlikely to occur. 155 if ((!key) in queue) { 156 queue[key] = []; 157 } 158 queue[key].push(data); 159 } 160 } 161 162 return { on: on, off: off, send: send }; 163 } 164 165 function emptyApiFactory() { 166 /** 167 * When the browser does not support any of the APIs that we're using 168 * for messaging, just present an empty api that does just gives 169 * warnings regarding the lack of support. 170 **/ 171 172 function noop() { 173 console.warn('Hermes messaging is not supported.'); 174 } 175 176 return { on: noop, off: noop, send: noop }; 177 } 178 179 if ('BroadcastChannel' in window) { 180 return broadcastChannelApiFactory(); 181 } else if ('SharedWorker' in window) { 182 return sharedWorkerApiFactory(); 183 } else if ('localStorage' in window) { 184 return localStorageApiFactory(); 185 } else { 186 return emptyApiFactory(); 187 } 188});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.