L.Control.AutoUpdate = L.Control.extend({ options: { position: 'topleft', label: 'Automatic update', layer: undefined }, pressed: true, onAdd: function() { var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control'); this.link = L.DomUtil.create('a', 'leaflet-bar-part', container); this.iconStart = L.DomUtil.create('i', 'fa fa-play', this.link); this.link.href = '#'; L.DomEvent.on(this.link, 'click', this.cbClick, this); return container; }, switchButtons: function() { if (this.pressed) { this.pressed = false; this.iconStart.setAttribute('class', 'fa fa-pause'); this.autoUpdate(); } else { this.pressed = true; this.iconStart.setAttribute('class', 'fa fa-play'); this.stopUpdate(); } }, cbClick: function (e) { L.DomEvent.stopPropagation(e); this.intendedFunction(this.pressed); this.switchButtons(); }, intendedFunction: function() { alert('no function selected'); }, stopUpdate: function() { if (this.socket) { var s = this.socket; this.socket = null; s.close(); } }, autoUpdate: function() { var me = this; this.socket = new WebSocket('ws://' + window.location.host + '/ws'); this.socket.onerror = function(evt) { me.stopUpdate(); me.switchButtons(); }; this.socket.onclose = function(evt) { this.socket = null; } this.socket.onopen = function(evt) { // Sending pings every 5 secs to keep connection alive. var heartbeat = function() { if (heartbeat && me.socket) { me.socket.send("PING"); setTimeout(heartbeat, 8000); } else { // Prevent sending pings to re-opened sockets. heartbeat = null; } }; setTimeout(heartbeat, 8000); }; this.socket.onmessage = function(evt) { var json = evt.data; if (!(typeof json === "string")) { return; } var msg; try { msg = JSON.parse(json); } catch (err) { return; } if (msg.players) { me.options.layer.clearLayers(); me.options.layer.addData(msg.players); } var tilesData = msg.tiles; if (!tilesData) { return; } var invalidate = function(td) { var pyramid = new Array(9); var last = new Object(); pyramid[8] = last; for (var i = 0; i < td.length; i++) { var xz = td[i]; last[xz.X + "#" + xz.Z] = xz; } for (var p = 7; p >= 0; p--) { var prev = pyramid[p+1]; var curr = new Object(); pyramid[p] = curr; for (var k in prev) { if (prev.hasOwnProperty(k)) { var oxz = prev[k]; var nxz = { X: oxz.X >> 1, Z: oxz.Z >> 1 }; curr[nxz.X + "#" + nxz.Z] = nxz; } } } return function(x, y, z) { if (y > 8) { x >>= y - 8; z >>= y - 8; y = 8; } var level = pyramid[y]; var k = x + "#" + z; return level.hasOwnProperty(k); }; } (tilesData); var tiles = document.getElementsByTagName('img'); var re = /\/map\/([0-9]+)\/([0-9]+)\/([0-9]+).*/; for (var i = 0; i < tiles.length; i++) { var img = tiles[i]; var cl = img.getAttribute('class'); if (cl.indexOf('leaflet-tile-loaded') < 0) { continue; } var src = img.src; var coord = src.match(re); if (coord == null) { continue; } var y = parseInt(coord[1]); var x = parseInt(coord[2]); var z = parseInt(coord[3]); if (invalidate(x, y, z)) { var idx = src.lastIndexOf('#'); if (idx >= 0) { src = src.substring(0, idx); } img.src = src + '#' + Math.random(); } } }; } }); L.autoUpdate = function(cbLabel, cbFunc, layer, cbMap) { var control = new L.Control.AutoUpdate(); if (cbLabel) { control.options.label = cbLabel; } if (cbFunc) { control.intendedFunction = cbFunc; } if (layer) { control.options.layer = layer; } if (cbMap === '') { return control; } else if (cbMap) { cbMap.addControl(control); } else { map.addControl(control); } return control; };