1humhub.module('live.poll', function (module, require, $) { 2 var client = require('client'); 3 var event = require('event'); 4 var object = require('util').object; 5 6 var DEFAULT_MIN_INTERVAL = 10; 7 var DEFAULT_MAX_INTERVAL = 45; 8 9 var DEFAULT_IDLE_FACTOR = 0.1; 10 var DEFAULT_IDLE_INTERVAL = 20; 11 12 var EVENT_TYPE_REQUEST = 'request'; 13 var EVENT_TYPE_FOCUS = 'focus'; 14 var EVENT_TYPE_UPDATE = 'update'; 15 16 var counter = { 17 requests: 0, 18 updates: 0 19 }; 20 21 var PollClient = function (options) { 22 if (!options) { 23 module.log.error('Could not initialize PollClient. No options given!'); 24 return; 25 } 26 27 this.options = options; 28 this.options.minInterval = options.minInterval || DEFAULT_MIN_INTERVAL; 29 this.options.maxInterval = options.maxInterval || DEFAULT_MAX_INTERVAL; 30 this.options.idleFactor = options.idleFactor || DEFAULT_IDLE_FACTOR; 31 this.options.idleInterval = options.idleDelay || DEFAULT_IDLE_INTERVAL; 32 this.options.initTime = options.initTime || Date.now(); 33 34 this.init(); 35 }; 36 37 PollClient.prototype.init = function () { 38 if (!this.options.url) { 39 module.log.error('Could not initialize PollClient. No url option given!'); 40 return; 41 } 42 43 this.subscriberId = this.generateSubscriberId(); 44 this.focus = true; 45 this.delay = this.options.minInterval; 46 this.call = this.update.bind(this); 47 this.handle = this.handleUpdate.bind(this); 48 this.handleError = this.handleUpdateError.bind(this); 49 this.lastTs = this.options.initTime; 50 51 $(window) 52 .on('blur', this.onWindowBlur.bind(this)) 53 .on('focus', this.onWindowFocus.bind(this)); 54 55 var that = this; 56 $(document).on('mousemove keydown mousedown touchstart', object.debounce(function () { 57 that.stopIdle(); 58 }, 200)); 59 60 this.resetPollTimeout(); 61 this.startIdleTimer(); 62 63 this.initBroadCast(); 64 }; 65 66 PollClient.prototype.generateSubscriberId = function () { 67 return '_' + Math.random().toString(36).substr(2, 9); 68 }; 69 70 PollClient.prototype.initBroadCast = function () { 71 if (!window.BroadcastChannel) { 72 return; 73 } 74 75 this.channel = new BroadcastChannel('live.poll'); 76 77 var that = this; 78 this.channel.onmessage = function (evt) { 79 if (!evt.data) { 80 return; 81 } 82 83 if (evt.data.subscriberId === that.subscriberId) { 84 // We triggered the event, so nothing todo 85 return; 86 } 87 88 if (!that.focus) { 89 // Seems this is an inactive tab, so let others do the job... 90 that.resetPollTimeout(that.options.maxInterval); 91 } 92 93 switch (evt.data.type) { 94 case EVENT_TYPE_REQUEST: 95 // Another tab just started a request, so delay the timeout 96 that.resetPollTimeout(); 97 break; 98 case EVENT_TYPE_FOCUS: 99 // Another tab was focused, so increase delay and reset timeout 100 that.resetPollTimeout(that.options.maxInterval); 101 break; 102 case EVENT_TYPE_UPDATE: 103 // We received a response from another tab 104 that.handleUpdate(evt.data); 105 break; 106 } 107 } 108 }; 109 110 /** 111 * Handler called once the window was focused 112 */ 113 PollClient.prototype.onWindowFocus = function () { 114 this.focus = true; 115 this.stopIdle(); 116 this.broadCast(EVENT_TYPE_FOCUS); 117 }; 118 119 /** 120 * Handler called once the window was blurred 121 */ 122 PollClient.prototype.onWindowBlur = function () { 123 this.focus = false; 124 this.updateIdle(); 125 }; 126 127 /** 128 * Resets current timeout if set and starts polling with current delay 129 */ 130 PollClient.prototype.resetPollTimeout = function (delay) { 131 clearTimeout(this.timeout); 132 133 if (delay) { 134 this.setDelay(delay); 135 } 136 137 this.timeout = setTimeout(this.call, this.getDelay()); 138 }; 139 140 /** 141 * Resets current timeout if set and starts polling with current delay 142 */ 143 PollClient.prototype.startIdleTimer = function () { 144 setInterval($.proxy(this.updateIdle, this), (this.options.idleInterval * 1000)); 145 }; 146 147 /**
148 * Stops the idle behavior by resetting the delay to the min delay 149 */ 150 PollClient.prototype.stopIdle = function () { 151 this.setDelay(this.options.minInterval); 152 153 // Make sure we do not have to wait too long after idle end. 154 if (new Date() - this.lastTs > this.options.minInterval) { 155 this.resetPollTimeout(); 156 } 157 }; 158 159 /** 160 * Updates the delay by means of the idleFactor. 161 */ 162 PollClient.prototype.updateIdle = function () { 163 if (this.delay < this.options.maxInterval) { 164 this.setDelay(Math.ceil(this.delay + (this.delay * this.options.idleFactor))); 165 } 166 167 if (this.delay > this.options.maxInterval) { 168 this.setDelay(this.options.maxInterval); 169 } 170 }; 171 172 /** 173 * Runs an live update call and resets the timeout. 174 */ 175 PollClient.prototype.update = function () { 176 this.broadCast(EVENT_TYPE_REQUEST); 177 counter.requests++; 178 179 client.get(this.getCallOptions()) 180 .then(this.handle) 181 .catch(this.handleError); 182 183 }; 184 185 /** 186 * Returns the ajax call options 187 */ 188 PollClient.prototype.getCallOptions = function () { 189 return { 190 url: this.options.url, 191 data: { 192 last: this.lastTs 193 } 194 }; 195 }; 196 197 /** 198 * Handles the live update response. 199 */ 200 PollClient.prototype.handleUpdate = function (response) { 201 202 if (this.lastTs >= response.queryTime) { 203 // We already have a more recent update 204 return; 205 } 206 207 if (this.subscriberId === response.subscriberId) { 208 // Just to make sure we do not handle our own broadcast event 209 return; 210 } 211 212 // used for debugging only 213 counter.updates++; 214 215 this.lastTs = response.queryTime; 216 217 this.resetPollTimeout(); 218 219 if (!response.subscriberId) { 220 // If subscriberId is present, this data was already sent 221 this.broadCast(EVENT_TYPE_UPDATE, { 222 queryTime: response.queryTime, 223 events: response.events 224 }); 225 } 226 227 this.triggerEventUpdates(response); 228 }; 229 230 PollClient.prototype.triggerEventUpdates = function (response) { 231 if (object.isObject(response.events)) { 232 var events = this.groupEvents(response.events); 233 234 $.each(events, function (type, events) { 235 try { 236 // humhub.module.bla -> humhub:module:bla 237 event.trigger(type.replace(/\./g, ':'), [events, response]); 238 } catch (e) { 239 module.log.error(e); 240 } 241 }); 242 243 this.lastIds = Object.keys(response.events); 244 } 245 }; 246 247 PollClient.prototype.broadCast = function (type, data) { 248 data = data || {}; 249 250 if (!this.channel || data.subscriberId) { 251 return; 252 } 253 254 data.subscriberId = this.subscriberId; 255 data.type = type; 256 257 this.channel.postMessage(data); 258 }; 259 260 /** 261 * Groups the liveEvents by type and filters out duplicates. 262 */ 263 PollClient.prototype.groupEvents = function (events) { 264 var result = {}; 265 var that = this; 266 $.each(events, function (id, liveEvent) { 267 // Filter out already triggered events. 268 if (that.lastIds && that.lastIds.indexOf(id) > -1) { 269 return; // continue 270 } 271 272 if (!result[liveEvent.type]) { 273 result[liveEvent.type] = [liveEvent]; 274 } else { 275 result[liveEvent.type].push(liveEvent); 276 } 277 }); 278 279 return result; 280 }; 281 282 PollClient.prototype.handleUpdateError = function (e) { 283 if (!navigator.onLine) { 284 this.resetPollTimeout(this.options.maxInterval); 285 module.log.info('Poll request blocked due to offline status'); 286 } else { 287 module.log.error(e); 288 } 289 }; 290 291 /** 292 * Returns the delay in milliseconds. 293 * @returns {Number} 294 */ 295 PollClient.prototype.getDelay = function () { 296 return this.delay * 1000; 297 }; 298 299 /** 300 * Sets the delay in seconds 301 * @returns {Number} 302 */ 303 PollClient.prototype.setDelay = function (seconds) { 304 this.delay = seconds; 305 }; 306 307 module.export({ 308 PollClient: PollClient 309 }); 310});
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.