Repository: nakating/PUBG-maphack-map Branch: master Commit: 8b728dfeb7a8 Files: 14 Total size: 81.3 KB Directory structure: gitextract_r809efrd/ ├── .gitattributes ├── .gitignore ├── .vscode/ │ └── launch.json ├── LICENSE ├── README.md ├── exp.json ├── index.js ├── package.json └── static/ ├── index.css ├── index.html ├── index.js ├── libs/ │ └── socket.io.js ├── radar.js └── utils.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ * text=auto *.html text *.css text *.js text *.jpg binary ================================================ FILE: .gitignore ================================================ node_modules/ ================================================ FILE: .vscode/launch.json ================================================ { "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Launch Program", "program": "${workspaceRoot}\\index.js" } ] } ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 Naka Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # PUBG-Radar ![](pics/browser.png) ## READ ME FIRST! This is just a radar! This can not working alone. It needs a memory reader like: [jussihi/PUBG-map-hack](https://github.com/jussihi/PUBG-map-hack). So please dont ask why it's not working, get your memory reader first! ## Feature * Display player (location, health, facing direction) * Display item * Display Vehicle * Track current player location (change that with `/?id=PLAYERINDEX`) * Select map (change with `/?map=(1|2)` or pick one at random if omitted) ## Build instructions 1. Install [Nodejs](https://nodejs.org.). 2. To your Working directory and run `npm install` to install package. 3. Run services `node index.js`. 4. The Map will running at `localhost:7890`. ## Configure Configure your memory reader to work with PUBG-Radar. ### **[PUBG-map-hack](https://github.com/jussihi/PUBG-map-hack)** edit your PUBG-map-hack `CURLWrapper.hpp` file: 1. Change `CURLOPT_URL` to `http://127.0.0.1:7890/` 2. Change `CURLOPT_CUSTOMREQUEST` to `POST` 3. Save and compile. exp: ```C++ int sendData(std::string& w_data) { try { struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Content-Type: application/json"); curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 0L); curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(m_curl, CURLOPT_URL, "http://127.0.0.1:7890/"); // <---- here curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, "POST"); // <---- here // curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 30L); curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, w_data.data()); curl_easy_setopt(m_curl, CURLOPT_NOSIGNAL, 1); curl_easy_perform(m_curl); curl_slist_free_all(headers); curl_easy_reset(m_curl); } catch (std::exception& e) { std::cout << e.what() << std::endl; return -1; } } ``` ### custom reader HTTP POST that JSON struct blow, send it to `http://127.0.0.1:7890'. ```json { "locations": { "players": [{ "t": 0, "x": 1.00, "y": 1.00, "hp": 1.00, "r": 1.00 }], "items": [{ "x": 1.00, "y": 1.00, "n": "scar" }], "vehicles": [{ "x": 1.00, "y": 1.00, "v": "buggy" }] } } // t team id // x actorLocation X // y actorLocation Y // r relativeRotation Y // n name ``` ## Changelog ### 2017-12-21 16:10:08 * Add desert map supported, made by [@ivq](https://github.com/ivq) ### 2017-12-16 01:37:18 * facing direction arrow is no longer displayed when player is driving or parachuting. * try to add a meter but failed, some junk code here. ### 2017-12-14 01:09:28 * rename project. * code refactoring, now code is clean and upgradable. ### 2017-11-6 00:16:33 * Add facing direction arrow. **to enable this feature you need edit your PUBG-map-hack `GameDataParser.hpp`**: ```C++ if (std::find(playerIDs.begin(), playerIDs.end(), curActorID) != playerIDs.end()) { int64_t rootCmpPtr = _Reader->readType(curActor + 0x188); int64_t playerState = _Reader->readType(curActor + 0x3D0); Vector3 actorLocation = _Reader->readVec(rootCmpPtr + 0x2DC); Vector3 relativeRotation = _Reader->readVec(rootCmpPtr + 0x02DC); // <---- here // ... w_data["players"].emplace_back(json::object({ { "t", actorTeam }, {"hp", hp}, { "x", actorLocation.X },{ "y", actorLocation.Y }, {"r", relativeRotation.Y } })); // <---- and here } ``` ### 2017-11-5 21:47:58 * Add fullscreen support (ios safari ONLY). ### 2017-11-4 04:04:44 * Dead body is a black dot right now. * Add a Health pie chart, The player's health has a visual effect. **to enable this feature you need edit your PUBG-map-hack `GameDataParser.hpp`**: ```C++ if (std::find(playerIDs.begin(), playerIDs.end(), curActorID) != playerIDs.end()) { // ... float hp = _Reader->readType(curActor + 0x113C); // <---- here w_data["players"].emplace_back(json::object({ { "t", actorTeam }, {"hp", hp}, { "x", actorLocation.X },{ "y", actorLocation.Y }/*,{ "z", actorLocation.Z }*/ })); // <---- and here } ``` ## Special thanks [uuaing](https://github.com/uuaing) for facing direction and draw arrow functions. [myloft](https://github.com/myloft) for the curActor decryption ================================================ FILE: exp.json ================================================ { "locations": { "players": [{ "t": 0, "x": 1.0, "y": 1.0, "hp": 1.00, "r": 1.00 }], "items": [{ "x": 1.0, "y": 1.0, "n": "" }], "vehicles": [{ "x": 1.0, "y": 1.0, "v": "" }] } } ================================================ FILE: index.js ================================================ var express = require('express'); var app = express(); var server = require('http').Server(app); var io = require('socket.io').listen(server); var bodyParser = require('body-parser'); var port = 7890; // 地图数据 var DUMPDATA = {}; // socket clients var CLIENTS = []; // 静态文件 app.use(express.static('static')); //body-parser 解析json格式数据 app.use(bodyParser.json({ limit: '1mb' })); //此项必须在 bodyParser.json 下面,为参数编码 app.use(bodyParser.urlencoded({ extended: true })); // POST 请求 app.post('/', function (req, res) { DUMPDATA = req.body; res.end('confirm'); for (var index = 0; index < CLIENTS.length; index++) { var socket = CLIENTS[index]; socket.emit('update', DUMPDATA); } }); // websocket io.on('connection', function (socket) { CLIENTS.push(socket); console.log(`a user connected, current users: [${CLIENTS.length}]`); socket.on('disconnect', function () { for (var index = 0; index < CLIENTS.length; index++) { if (CLIENTS[index] === socket) { CLIENTS.splice(index, 1); } } console.log(`a user disconnect, current users: [${CLIENTS.length}]`); }); }); server.listen(port, function () { var host = server.address().address; var port = server.address().port; console.log(`server running at localhost:${port}`); }); ================================================ FILE: package.json ================================================ { "name": "PUBG-Radar", "version": "2.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "body-parser": "^1.18.2", "express": "^4.16.2", "socket.io": "^2.0.4" } } ================================================ FILE: static/index.css ================================================ body { padding: 0; margin: 0; } .container { position: absolute; bottom: 0; left: 0; right: 0; top: 0; } .container canvas { display: block; } ================================================ FILE: static/index.html ================================================ PUBG - Radar
================================================ FILE: static/index.js ================================================ $(function () { function onResize() { var height = window.innerHeight; var is_safari = navigator.userAgent.indexOf("Safari") > -1; if ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i))) { if (is_safari) { height += 80; } } $('#radar').attr("width", window.innerWidth).attr("height", height); } window.addEventListener('resize', onResize); onResize(); // 禁止移动端弹性webview document.ontouchmove = function (event) { event.preventDefault(); } }) $(function () { var radar = new Radar($('#radar')[0]); var socket = io(); var socketUpdateCounter = new Utils.MinsCounter(); socket.on('update', function (snapshot) { locations = snapshot; socketUpdateCounter.update(); redraw(); }); var locations = {}; var trackPlayerIndex = parseInt(Utils.getParameterByName('id') || 0); var maps = ['mapErangel.jpg', 'mapMiramar.jpg']; var mapParameter = Utils.getParameterByName('map'); if (mapParameter === '1') { radar.setMap(maps[0]); } else if (mapParameter === '2') { radar.setMap(maps[1]); } else { radar.setMap(maps[Math.random() < .5 ? 1 : 0]); } // 手势支持 var hammertime = new Hammer.Manager($('.container')[0]); hammertime.add(new Hammer.Pan({ threshold: 0 })); hammertime.add(new Hammer.Pinch({ threshold: 0 })); // 拖动 var lastDelta = { x: 0, y: 0 } hammertime.on('panmove', function (ev) { radar.setMove(ev.deltaX - lastDelta.x, ev.deltaY - lastDelta.y); lastDelta.x = ev.deltaX; lastDelta.y = ev.deltaY; redraw(); }); hammertime.on('panend', function (ev) { lastDelta = { x: 0, y: 0 } }); // 缩放 var lastScale = 0; hammertime.on('pinchmove', function (ev) { var size = 0.6; if (lastScale > ev.scale) { size = -size; } radar.setZoom(Math.pow(1.1, size)); lastScale = ev.scale; redraw(); }); hammertime.on('pinchend', function () { lastScale = 0; }); // 鼠标滚轮缩放 $('.container').on("mousewheel DOMMouseScroll", function (e) { var evt = e.originalEvent; var delta = evt.wheelDelta ? evt.wheelDelta / 40 : evt.detail ? -evt.detail : 0; if (delta) { radar.setZoom(Math.pow(1.1, delta)); redraw(); } return evt.preventDefault() && false; }); function redraw() { radar.clear(); // 视角追踪 if (locations.players && locations.players[trackPlayerIndex]) { var player = locations.players[trackPlayerIndex]; radar.setFocus(player.x, player.y); } // draw map radar.map(); drawPlayers(); drawItems(); drawVehicles(); drawMisc(); } function drawPlayers() { if (!locations.players) { return; } var players = locations.players; for (var i = players.length - 1; i >= 0; i--) { var player = players[i]; var color = ""; if (i == trackPlayerIndex) { color = '#00BB00'; } else if (players[trackPlayerIndex].t == player.t) { color = '#0033BB'; } else { color = '#ff0000'; } if (player.hp == 0) { color = '#000000'; radar.dot(player.x, player.y, color); } else { if(player.r != 0){ radar.lineWithAngle(player.x, player.y, 15, 6, player.r, color); } radar.dot(player.x, player.y, color); radar.pieChart(player.x, player.y, ((100 - player.hp) / 100), 'gray') } radar.text(player.x, player.y, i, 'white'); } } function drawItems() { if (!locations.items) { return; } var items = locations.items; for (var i = items.length - 1; i >= 0; i--) { var item = items[i]; radar.text(item.x, item.y, item.n, 'red'); } } function drawVehicles() { if (!locations.vehicles) { return; } var vehicles = locations.vehicles; for (var i = vehicles.length - 1; i >= 0; i--) { var vehicle = vehicles[i]; var name = ""; if (vehicle.v.indexOf('_') >= 0) { name = vehicle.v.split('_')[0]; } radar.text(vehicle.x, vehicle.y, name, 'orange'); } } function drawMisc() { // radar.floatText(0, 10, "Update: " + socketUpdateCounter.getPerSec() + "ps"); } }); ================================================ FILE: static/libs/socket.io.js ================================================ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.io=e():t.io=e()}(this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return t[r].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function r(t,e){"object"===("undefined"==typeof t?"undefined":o(t))&&(e=t,t=void 0),e=e||{};var n,r=i(t),s=r.source,u=r.id,h=r.path,f=p[u]&&h in p[u].nsps,l=e.forceNew||e["force new connection"]||!1===e.multiplex||f;return l?(c("ignoring socket cache for %s",s),n=a(s,e)):(p[u]||(c("new io instance for %s",s),p[u]=a(s,e)),n=p[u]),r.query&&!e.query&&(e.query=r.query),n.socket(r.path,e)}var o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=n(1),s=n(7),a=n(13),c=n(3)("socket.io-client");t.exports=e=r;var p=e.managers={};e.protocol=s.protocol,e.connect=r,e.Manager=n(13),e.Socket=n(37)},function(t,e,n){(function(e){"use strict";function r(t,n){var r=t;n=n||e.location,null==t&&(t=n.protocol+"//"+n.host),"string"==typeof t&&("/"===t.charAt(0)&&(t="/"===t.charAt(1)?n.protocol+t:n.host+t),/^(https?|wss?):\/\//.test(t)||(i("protocol-less url %s",t),t="undefined"!=typeof n?n.protocol+"//"+t:"https://"+t),i("parse %s",t),r=o(t)),r.port||(/^(http|ws)$/.test(r.protocol)?r.port="80":/^(http|ws)s$/.test(r.protocol)&&(r.port="443")),r.path=r.path||"/";var s=r.host.indexOf(":")!==-1,a=s?"["+r.host+"]":r.host;return r.id=r.protocol+"://"+a+":"+r.port,r.href=r.protocol+"://"+a+(n&&n.port===r.port?"":":"+r.port),r}var o=n(2),i=n(3)("socket.io-client:url");t.exports=r}).call(e,function(){return this}())},function(t,e){var n=/^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,r=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];t.exports=function(t){var e=t,o=t.indexOf("["),i=t.indexOf("]");o!=-1&&i!=-1&&(t=t.substring(0,o)+t.substring(o,i).replace(/:/g,";")+t.substring(i,t.length));for(var s=n.exec(t||""),a={},c=14;c--;)a[r[c]]=s[c]||"";return o!=-1&&i!=-1&&(a.source=e,a.host=a.host.substring(1,a.host.length-1).replace(/;/g,":"),a.authority=a.authority.replace("[","").replace("]","").replace(/;/g,":"),a.ipv6uri=!0),a}},function(t,e,n){(function(r){function o(){return!("undefined"==typeof window||!window.process||"renderer"!==window.process.type)||("undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))}function i(t){var n=this.useColors;if(t[0]=(n?"%c":"")+this.namespace+(n?" %c":" ")+t[0]+(n?"%c ":" ")+"+"+e.humanize(this.diff),n){var r="color: "+this.color;t.splice(1,0,r,"color: inherit");var o=0,i=0;t[0].replace(/%[a-zA-Z%]/g,function(t){"%%"!==t&&(o++,"%c"===t&&(i=o))}),t.splice(i,0,r)}}function s(){return"object"==typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function a(t){try{null==t?e.storage.removeItem("debug"):e.storage.debug=t}catch(n){}}function c(){var t;try{t=e.storage.debug}catch(n){}return!t&&"undefined"!=typeof r&&"env"in r&&(t=r.env.DEBUG),t}function p(){try{return window.localStorage}catch(t){}}e=t.exports=n(5),e.log=s,e.formatArgs=i,e.save=a,e.load=c,e.useColors=o,e.storage="undefined"!=typeof chrome&&"undefined"!=typeof chrome.storage?chrome.storage.local:p(),e.colors=["lightseagreen","forestgreen","goldenrod","dodgerblue","darkorchid","crimson"],e.formatters.j=function(t){try{return JSON.stringify(t)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}},e.enable(c())}).call(e,n(4))},function(t,e){function n(){throw new Error("setTimeout has not been defined")}function r(){throw new Error("clearTimeout has not been defined")}function o(t){if(u===setTimeout)return setTimeout(t,0);if((u===n||!u)&&setTimeout)return u=setTimeout,setTimeout(t,0);try{return u(t,0)}catch(e){try{return u.call(null,t,0)}catch(e){return u.call(this,t,0)}}}function i(t){if(h===clearTimeout)return clearTimeout(t);if((h===r||!h)&&clearTimeout)return h=clearTimeout,clearTimeout(t);try{return h(t)}catch(e){try{return h.call(null,t)}catch(e){return h.call(this,t)}}}function s(){y&&l&&(y=!1,l.length?d=l.concat(d):m=-1,d.length&&a())}function a(){if(!y){var t=o(s);y=!0;for(var e=d.length;e;){for(l=d,d=[];++m1)for(var n=1;n100)){var e=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(t);if(e){var n=parseFloat(e[1]),r=(e[2]||"ms").toLowerCase();switch(r){case"years":case"year":case"yrs":case"yr":case"y":return n*u;case"days":case"day":case"d":return n*p;case"hours":case"hour":case"hrs":case"hr":case"h":return n*c;case"minutes":case"minute":case"mins":case"min":case"m":return n*a;case"seconds":case"second":case"secs":case"sec":case"s":return n*s;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return n;default:return}}}}function r(t){return t>=p?Math.round(t/p)+"d":t>=c?Math.round(t/c)+"h":t>=a?Math.round(t/a)+"m":t>=s?Math.round(t/s)+"s":t+"ms"}function o(t){return i(t,p,"day")||i(t,c,"hour")||i(t,a,"minute")||i(t,s,"second")||t+" ms"}function i(t,e,n){if(!(t0)return n(t);if("number"===i&&isNaN(t)===!1)return e["long"]?o(t):r(t);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(t))}},function(t,e,n){function r(){}function o(t){var n=""+t.type;return e.BINARY_EVENT!==t.type&&e.BINARY_ACK!==t.type||(n+=t.attachments+"-"),t.nsp&&"/"!==t.nsp&&(n+=t.nsp+","),null!=t.id&&(n+=t.id),null!=t.data&&(n+=JSON.stringify(t.data)),h("encoded %j as %s",t,n),n}function i(t,e){function n(t){var n=d.deconstructPacket(t),r=o(n.packet),i=n.buffers;i.unshift(r),e(i)}d.removeBlobs(t,n)}function s(){this.reconstructor=null}function a(t){var n=0,r={type:Number(t.charAt(0))};if(null==e.types[r.type])return u();if(e.BINARY_EVENT===r.type||e.BINARY_ACK===r.type){for(var o="";"-"!==t.charAt(++n)&&(o+=t.charAt(n),n!=t.length););if(o!=Number(o)||"-"!==t.charAt(n))throw new Error("Illegal attachments");r.attachments=Number(o)}if("/"===t.charAt(n+1))for(r.nsp="";++n;){var i=t.charAt(n);if(","===i)break;if(r.nsp+=i,n===t.length)break}else r.nsp="/";var s=t.charAt(n+1);if(""!==s&&Number(s)==s){for(r.id="";++n;){var i=t.charAt(n);if(null==i||Number(i)!=i){--n;break}if(r.id+=t.charAt(n),n===t.length)break}r.id=Number(r.id)}return t.charAt(++n)&&(r=c(r,t.substr(n))),h("decoded %s as %j",t,r),r}function c(t,e){try{t.data=JSON.parse(e)}catch(n){return u()}return t}function p(t){this.reconPack=t,this.buffers=[]}function u(){return{type:e.ERROR,data:"parser error"}}var h=n(3)("socket.io-parser"),f=n(8),l=n(9),d=n(11),y=n(12);e.protocol=4,e.types=["CONNECT","DISCONNECT","EVENT","ACK","ERROR","BINARY_EVENT","BINARY_ACK"],e.CONNECT=0,e.DISCONNECT=1,e.EVENT=2,e.ACK=3,e.ERROR=4,e.BINARY_EVENT=5,e.BINARY_ACK=6,e.Encoder=r,e.Decoder=s,r.prototype.encode=function(t,n){if(t.type!==e.EVENT&&t.type!==e.ACK||!l(t.data)||(t.type=t.type===e.EVENT?e.BINARY_EVENT:e.BINARY_ACK),h("encoding packet %j",t),e.BINARY_EVENT===t.type||e.BINARY_ACK===t.type)i(t,n);else{var r=o(t);n([r])}},f(s.prototype),s.prototype.add=function(t){var n;if("string"==typeof t)n=a(t),e.BINARY_EVENT===n.type||e.BINARY_ACK===n.type?(this.reconstructor=new p(n),0===this.reconstructor.reconPack.attachments&&this.emit("decoded",n)):this.emit("decoded",n);else{if(!y(t)&&!t.base64)throw new Error("Unknown type: "+t);if(!this.reconstructor)throw new Error("got binary data when not reconstructing a packet");n=this.reconstructor.takeBinaryData(t),n&&(this.reconstructor=null,this.emit("decoded",n))}},s.prototype.destroy=function(){this.reconstructor&&this.reconstructor.finishedReconstruction()},p.prototype.takeBinaryData=function(t){if(this.buffers.push(t),this.buffers.length===this.reconPack.attachments){var e=d.reconstructPacket(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null},p.prototype.finishedReconstruction=function(){this.reconPack=null,this.buffers=[]}},function(t,e,n){function r(t){if(t)return o(t)}function o(t){for(var e in r.prototype)t[e]=r.prototype[e];return t}t.exports=r,r.prototype.on=r.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+t]=this._callbacks["$"+t]||[]).push(e),this},r.prototype.once=function(t,e){function n(){this.off(t,n),e.apply(this,arguments)}return n.fn=e,this.on(t,n),this},r.prototype.off=r.prototype.removeListener=r.prototype.removeAllListeners=r.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var n=this._callbacks["$"+t];if(!n)return this;if(1==arguments.length)return delete this._callbacks["$"+t],this;for(var r,o=0;o0&&!this.encoding){var t=this.packetBuffer.shift();this.packet(t)}},r.prototype.cleanup=function(){h("cleanup");for(var t=this.subs.length,e=0;e=this._reconnectionAttempts)h("reconnect failed"),this.backoff.reset(),this.emitAll("reconnect_failed"),this.reconnecting=!1;else{var e=this.backoff.duration();h("will wait %dms before reconnect attempt",e),this.reconnecting=!0;var n=setTimeout(function(){t.skipReconnect||(h("attempting reconnect"),t.emitAll("reconnect_attempt",t.backoff.attempts),t.emitAll("reconnecting",t.backoff.attempts),t.skipReconnect||t.open(function(e){e?(h("reconnect attempt error"),t.reconnecting=!1,t.reconnect(),t.emitAll("reconnect_error",e.data)):(h("reconnect success"),t.onreconnect())}))},e);this.subs.push({destroy:function(){clearTimeout(n)}})}},r.prototype.onreconnect=function(){var t=this.backoff.attempts;this.reconnecting=!1,this.backoff.reset(),this.updateSocketIds(),this.emitAll("reconnect",t)}},function(t,e,n){t.exports=n(15),t.exports.parser=n(22)},function(t,e,n){(function(e){function r(t,n){if(!(this instanceof r))return new r(t,n);n=n||{},t&&"object"==typeof t&&(n=t,t=null),t?(t=u(t),n.hostname=t.host,n.secure="https"===t.protocol||"wss"===t.protocol,n.port=t.port,t.query&&(n.query=t.query)):n.host&&(n.hostname=u(n.host).host),this.secure=null!=n.secure?n.secure:e.location&&"https:"===location.protocol,n.hostname&&!n.port&&(n.port=this.secure?"443":"80"),this.agent=n.agent||!1,this.hostname=n.hostname||(e.location?location.hostname:"localhost"),this.port=n.port||(e.location&&location.port?location.port:this.secure?443:80),this.query=n.query||{},"string"==typeof this.query&&(this.query=h.decode(this.query)),this.upgrade=!1!==n.upgrade,this.path=(n.path||"/engine.io").replace(/\/$/,"")+"/",this.forceJSONP=!!n.forceJSONP,this.jsonp=!1!==n.jsonp,this.forceBase64=!!n.forceBase64,this.enablesXDR=!!n.enablesXDR,this.timestampParam=n.timestampParam||"t",this.timestampRequests=n.timestampRequests,this.transports=n.transports||["polling","websocket"],this.transportOptions=n.transportOptions||{},this.readyState="",this.writeBuffer=[],this.prevBufferLen=0,this.policyPort=n.policyPort||843,this.rememberUpgrade=n.rememberUpgrade||!1,this.binaryType=null,this.onlyBinaryUpgrades=n.onlyBinaryUpgrades,this.perMessageDeflate=!1!==n.perMessageDeflate&&(n.perMessageDeflate||{}),!0===this.perMessageDeflate&&(this.perMessageDeflate={}),this.perMessageDeflate&&null==this.perMessageDeflate.threshold&&(this.perMessageDeflate.threshold=1024),this.pfx=n.pfx||null,this.key=n.key||null,this.passphrase=n.passphrase||null,this.cert=n.cert||null,this.ca=n.ca||null,this.ciphers=n.ciphers||null,this.rejectUnauthorized=void 0===n.rejectUnauthorized||n.rejectUnauthorized,this.forceNode=!!n.forceNode;var o="object"==typeof e&&e;o.global===o&&(n.extraHeaders&&Object.keys(n.extraHeaders).length>0&&(this.extraHeaders=n.extraHeaders),n.localAddress&&(this.localAddress=n.localAddress)),this.id=null,this.upgrades=null,this.pingInterval=null,this.pingTimeout=null,this.pingIntervalTimer=null,this.pingTimeoutTimer=null,this.open()}function o(t){var e={};for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}var i=n(16),s=n(8),a=n(3)("engine.io-client:socket"),c=n(36),p=n(22),u=n(2),h=n(30);t.exports=r,r.priorWebsocketSuccess=!1,s(r.prototype),r.protocol=p.protocol,r.Socket=r,r.Transport=n(21),r.transports=n(16),r.parser=n(22),r.prototype.createTransport=function(t){a('creating transport "%s"',t);var e=o(this.query);e.EIO=p.protocol,e.transport=t;var n=this.transportOptions[t]||{};this.id&&(e.sid=this.id);var r=new i[t]({query:e,socket:this,agent:n.agent||this.agent,hostname:n.hostname||this.hostname,port:n.port||this.port,secure:n.secure||this.secure,path:n.path||this.path,forceJSONP:n.forceJSONP||this.forceJSONP,jsonp:n.jsonp||this.jsonp,forceBase64:n.forceBase64||this.forceBase64,enablesXDR:n.enablesXDR||this.enablesXDR,timestampRequests:n.timestampRequests||this.timestampRequests,timestampParam:n.timestampParam||this.timestampParam,policyPort:n.policyPort||this.policyPort,pfx:n.pfx||this.pfx,key:n.key||this.key,passphrase:n.passphrase||this.passphrase,cert:n.cert||this.cert,ca:n.ca||this.ca,ciphers:n.ciphers||this.ciphers,rejectUnauthorized:n.rejectUnauthorized||this.rejectUnauthorized,perMessageDeflate:n.perMessageDeflate||this.perMessageDeflate,extraHeaders:n.extraHeaders||this.extraHeaders,forceNode:n.forceNode||this.forceNode,localAddress:n.localAddress||this.localAddress,requestTimeout:n.requestTimeout||this.requestTimeout,protocols:n.protocols||void 0});return r},r.prototype.open=function(){var t;if(this.rememberUpgrade&&r.priorWebsocketSuccess&&this.transports.indexOf("websocket")!==-1)t="websocket";else{if(0===this.transports.length){var e=this;return void setTimeout(function(){e.emit("error","No transports available")},0)}t=this.transports[0]}this.readyState="opening";try{t=this.createTransport(t)}catch(n){return this.transports.shift(),void this.open()}t.open(),this.setTransport(t)},r.prototype.setTransport=function(t){a("setting transport %s",t.name);var e=this;this.transport&&(a("clearing existing transport %s",this.transport.name),this.transport.removeAllListeners()),this.transport=t,t.on("drain",function(){e.onDrain()}).on("packet",function(t){e.onPacket(t)}).on("error",function(t){e.onError(t)}).on("close",function(){e.onClose("transport close")})},r.prototype.probe=function(t){function e(){if(f.onlyBinaryUpgrades){var e=!this.supportsBinary&&f.transport.supportsBinary;h=h||e}h||(a('probe transport "%s" opened',t),u.send([{type:"ping",data:"probe"}]),u.once("packet",function(e){if(!h)if("pong"===e.type&&"probe"===e.data){if(a('probe transport "%s" pong',t),f.upgrading=!0,f.emit("upgrading",u),!u)return;r.priorWebsocketSuccess="websocket"===u.name,a('pausing current transport "%s"',f.transport.name),f.transport.pause(function(){h||"closed"!==f.readyState&&(a("changing transport and sending upgrade packet"),p(),f.setTransport(u),u.send([{type:"upgrade"}]),f.emit("upgrade",u),u=null,f.upgrading=!1,f.flush())})}else{a('probe transport "%s" failed',t);var n=new Error("probe error");n.transport=u.name,f.emit("upgradeError",n)}}))}function n(){h||(h=!0,p(),u.close(),u=null)}function o(e){var r=new Error("probe error: "+e);r.transport=u.name,n(),a('probe transport "%s" failed because of error: %s',t,e),f.emit("upgradeError",r)}function i(){o("transport closed")}function s(){o("socket closed")}function c(t){u&&t.name!==u.name&&(a('"%s" works - aborting "%s"',t.name,u.name),n())}function p(){u.removeListener("open",e),u.removeListener("error",o),u.removeListener("close",i),f.removeListener("close",s),f.removeListener("upgrading",c)}a('probing transport "%s"',t);var u=this.createTransport(t,{probe:1}),h=!1,f=this;r.priorWebsocketSuccess=!1,u.once("open",e),u.once("error",o),u.once("close",i),this.once("close",s),this.once("upgrading",c),u.open()},r.prototype.onOpen=function(){if(a("socket open"),this.readyState="open",r.priorWebsocketSuccess="websocket"===this.transport.name,this.emit("open"),this.flush(),"open"===this.readyState&&this.upgrade&&this.transport.pause){a("starting upgrade probes");for(var t=0,e=this.upgrades.length;t1?{type:b[o],data:t.substring(1)}:{type:b[o]}:w}var i=new Uint8Array(t),o=i[0],s=f(t,1);return k&&"blob"===n&&(s=new k([s])),{type:b[o],data:s}},e.decodeBase64Packet=function(t,e){var n=b[t.charAt(0)];if(!p)return{type:n,data:{base64:!0,data:t.substr(1)}};var r=p.decode(t.substr(1));return"blob"===e&&k&&(r=new k([r])),{type:n,data:r}},e.encodePayload=function(t,n,r){function o(t){return t.length+":"+t}function i(t,r){e.encodePacket(t,!!s&&n,!1,function(t){r(null,o(t))})}"function"==typeof n&&(r=n,n=null);var s=h(t);return n&&s?k&&!g?e.encodePayloadAsBlob(t,r):e.encodePayloadAsArrayBuffer(t,r):t.length?void c(t,i,function(t,e){return r(e.join(""))}):r("0:")},e.decodePayload=function(t,n,r){if("string"!=typeof t)return e.decodePayloadAsBinary(t,n,r);"function"==typeof n&&(r=n,n=null);var o;if(""===t)return r(w,0,1);for(var i,s,a="",c=0,p=t.length;c0;){for(var s=new Uint8Array(o),a=0===s[0],c="",p=1;255!==s[p];p++){if(c.length>310)return r(w,0,1);c+=s[p]}o=f(o,2+c.length),c=parseInt(c);var u=f(o,0,c);if(a)try{u=String.fromCharCode.apply(null,new Uint8Array(u))}catch(h){var l=new Uint8Array(u);u="";for(var p=0;pr&&(n=r),e>=r||e>=n||0===r)return new ArrayBuffer(0);for(var o=new Uint8Array(t),i=new Uint8Array(n-e),s=e,a=0;s=55296&&e<=56319&&o65535&&(e-=65536,o+=w(e>>>10&1023|55296),e=56320|1023&e),o+=w(e);return o}function c(t,e){if(t>=55296&&t<=57343){if(e)throw Error("Lone surrogate U+"+t.toString(16).toUpperCase()+" is not a scalar value");return!1}return!0}function p(t,e){return w(t>>e&63|128)}function u(t,e){if(0==(4294967168&t))return w(t);var n="";return 0==(4294965248&t)?n=w(t>>6&31|192):0==(4294901760&t)?(c(t,e)||(t=65533),n=w(t>>12&15|224),n+=p(t,6)):0==(4292870144&t)&&(n=w(t>>18&7|240),n+=p(t,12),n+=p(t,6)),n+=w(63&t|128)}function h(t,e){e=e||{};for(var n,r=!1!==e.strict,o=s(t),i=o.length,a=-1,c="";++a=v)throw Error("Invalid byte index");var t=255&g[b];if(b++,128==(192&t))return 63&t;throw Error("Invalid continuation byte")}function l(t){var e,n,r,o,i;if(b>v)throw Error("Invalid byte index");if(b==v)return!1;if(e=255&g[b],b++,0==(128&e))return e;if(192==(224&e)){if(n=f(),i=(31&e)<<6|n,i>=128)return i;throw Error("Invalid continuation byte")}if(224==(240&e)){if(n=f(),r=f(),i=(15&e)<<12|n<<6|r,i>=2048)return c(i,t)?i:65533;throw Error("Invalid continuation byte")}if(240==(248&e)&&(n=f(),r=f(),o=f(),i=(7&e)<<18|n<<12|r<<6|o,i>=65536&&i<=1114111))return i;throw Error("Invalid UTF-8 detected")}function d(t,e){e=e||{};var n=!1!==e.strict;g=s(t),v=g.length,b=0;for(var r,o=[];(r=l(n))!==!1;)o.push(r);return a(o)}var y="object"==typeof e&&e,m=("object"==typeof t&&t&&t.exports==y&&t,"object"==typeof o&&o);m.global!==m&&m.window!==m||(i=m);var g,v,b,w=String.fromCharCode,k={version:"2.1.2",encode:h,decode:d};r=function(){return k}.call(e,n,e,t),!(void 0!==r&&(t.exports=r))}(this)}).call(e,n(27)(t),function(){return this}())},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}},function(t,e){!function(){"use strict";for(var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",n=new Uint8Array(256),r=0;r>2],i+=t[(3&r[n])<<4|r[n+1]>>4],i+=t[(15&r[n+1])<<2|r[n+2]>>6],i+=t[63&r[n+2]];return o%3===2?i=i.substring(0,i.length-1)+"=":o%3===1&&(i=i.substring(0,i.length-2)+"=="),i},e.decode=function(t){var e,r,o,i,s,a=.75*t.length,c=t.length,p=0;"="===t[t.length-1]&&(a--,"="===t[t.length-2]&&a--);var u=new ArrayBuffer(a),h=new Uint8Array(u);for(e=0;e>4,h[p++]=(15&o)<<4|i>>2,h[p++]=(3&i)<<6|63&s;return u}}()},function(t,e){(function(e){function n(t){for(var e=0;e0);return e}function r(t){var e=0;for(u=0;u';i=document.createElement(e)}catch(t){i=document.createElement("iframe"),i.name=o.iframeId,i.src="javascript:0"}i.id=o.iframeId,o.form.appendChild(i),o.iframe=i}var o=this;if(!this.form){var i,s=document.createElement("form"),a=document.createElement("textarea"),u=this.iframeId="eio_iframe_"+this.index;s.className="socketio",s.style.position="absolute",s.style.top="-1000px",s.style.left="-1000px",s.target=u,s.method="POST",s.setAttribute("accept-charset","utf-8"),a.name="d",s.appendChild(a),document.body.appendChild(s),this.form=s,this.area=a}this.form.action=this.uri(),r(),t=t.replace(p,"\\\n"),this.area.value=t.replace(c,"\\n");try{this.form.submit()}catch(h){}this.iframe.attachEvent?this.iframe.onreadystatechange=function(){"complete"===o.iframe.readyState&&n()}:this.iframe.onload=n}}).call(e,function(){return this}())},function(t,e,n){(function(e){function r(t){var e=t&&t.forceBase64;e&&(this.supportsBinary=!1),this.perMessageDeflate=t.perMessageDeflate,this.usingBrowserWebSocket=h&&!t.forceNode,this.protocols=t.protocols,this.usingBrowserWebSocket||(l=o),i.call(this,t)}var o,i=n(21),s=n(22),a=n(30),c=n(31),p=n(32),u=n(3)("engine.io-client:websocket"),h=e.WebSocket||e.MozWebSocket;if("undefined"==typeof window)try{o=n(35)}catch(f){}var l=h;l||"undefined"!=typeof window||(l=o),t.exports=r,c(r,i),r.prototype.name="websocket",r.prototype.supportsBinary=!0,r.prototype.doOpen=function(){if(this.check()){var t=this.uri(),e=this.protocols,n={agent:this.agent,perMessageDeflate:this.perMessageDeflate};n.pfx=this.pfx,n.key=this.key,n.passphrase=this.passphrase,n.cert=this.cert,n.ca=this.ca,n.ciphers=this.ciphers,n.rejectUnauthorized=this.rejectUnauthorized,this.extraHeaders&&(n.headers=this.extraHeaders),this.localAddress&&(n.localAddress=this.localAddress);try{this.ws=this.usingBrowserWebSocket?e?new l(t,e):new l(t):new l(t,e,n)}catch(r){return this.emit("error",r)}void 0===this.ws.binaryType&&(this.supportsBinary=!1),this.ws.supports&&this.ws.supports.binary?(this.supportsBinary=!0,this.ws.binaryType="nodebuffer"):this.ws.binaryType="arraybuffer",this.addEventListeners()}},r.prototype.addEventListeners=function(){var t=this;this.ws.onopen=function(){t.onOpen()},this.ws.onclose=function(){t.onClose()},this.ws.onmessage=function(e){t.onData(e.data)},this.ws.onerror=function(e){t.onError("websocket error",e)}},r.prototype.write=function(t){function n(){r.emit("flush"),setTimeout(function(){r.writable=!0,r.emit("drain")},0)}var r=this;this.writable=!1;for(var o=t.length,i=0,a=o;i0&&t.jitter<=1?t.jitter:0,this.attempts=0}t.exports=n,n.prototype.duration=function(){var t=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var e=Math.random(),n=Math.floor(e*this.jitter*t);t=0==(1&Math.floor(10*e))?t-n:t+n}return 0|Math.min(t,this.max)},n.prototype.reset=function(){this.attempts=0},n.prototype.setMin=function(t){this.ms=t},n.prototype.setMax=function(t){this.max=t},n.prototype.setJitter=function(t){this.jitter=t}}])}); //# sourceMappingURL=socket.io.js.map ================================================ FILE: static/radar.js ================================================ function Radar(canvas) { this.canvas = canvas; this.ctx = canvas.getContext('2d'); trackTransforms(this.ctx); this.scaledFactor = 1; this.mapImage = new Image; this.focusOffset = { X: this.canvas.width / 2, Y: this.canvas.height / 2 }; this.viewPortOffset = { X: 0, Y: 0 }; var self = this; window.addEventListener('resize', function () { self.focusOffset = { X: self.canvas.width / 2, Y: self.canvas.height / 2 }; }); // Adds ctx.getTransform() - returns an SVGMatrix // Adds ctx.transformedPoint(x,y) - returns an SVGPoint function trackTransforms(ctx) { var svg = document.createElementNS("http://www.w3.org/2000/svg", 'svg'); var xform = svg.createSVGMatrix(); ctx.getTransform = function () { return xform; }; var savedTransforms = []; var save = ctx.save; ctx.save = function () { savedTransforms.push(xform.translate(0, 0)); return save.call(ctx); }; var restore = ctx.restore; ctx.restore = function () { xform = savedTransforms.pop(); return restore.call(ctx); }; var scale = ctx.scale; ctx.scale = function (sx, sy) { xform = xform.scaleNonUniform(sx, sy); return scale.call(ctx, sx, sy); }; var rotate = ctx.rotate; ctx.rotate = function (radians) { xform = xform.rotate(radians * 180 / Math.PI); return rotate.call(ctx, radians); }; var translate = ctx.translate; ctx.translate = function (dx, dy) { xform = xform.translate(dx, dy); return translate.call(ctx, dx, dy); }; var transform = ctx.transform; ctx.transform = function (a, b, c, d, e, f) { var m2 = svg.createSVGMatrix(); m2.a = a; m2.b = b; m2.c = c; m2.d = d; m2.e = e; m2.f = f; xform = xform.multiply(m2); return transform.call(ctx, a, b, c, d, e, f); }; var setTransform = ctx.setTransform; ctx.setTransform = function (a, b, c, d, e, f) { xform.a = a; xform.b = b; xform.c = c; xform.d = d; xform.e = e; xform.f = f; return setTransform.call(ctx, a, b, c, d, e, f); }; var pt = svg.createSVGPoint(); ctx.transformedPoint = function (x, y) { pt.x = x; pt.y = y; return pt.matrixTransform(xform.inverse()); } } } Radar.prototype.setMap = function (map) { this.mapImage.src = map; } Radar.prototype.map = function () { this.ctx.drawImage(this.mapImage, 0, 0); } Radar.prototype.clear = function () { var p1 = this.ctx.transformedPoint(0, 0); var p2 = this.ctx.transformedPoint(this.canvas.width, this.canvas.height); this.ctx.clearRect(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y); } Radar.prototype.translate = function (offsetX, offsetY) { this.ctx.translate(offsetX, offsetY); this.viewPortOffset.X += offsetX; this.viewPortOffset.Y += offsetY; } Radar.prototype.setZoom = function (scale) { var pt = this.ctx.transformedPoint(this.canvas.width / 2, this.canvas.height / 2); this.scaledFactor *= scale; this.ctx.translate(pt.x, pt.y); this.ctx.scale(scale, scale); this.ctx.translate(-pt.x, -pt.y); } Radar.prototype.setMove = function (offsetX, offsetY) { offsetX = offsetX / this.scaledFactor; offsetY = offsetY / this.scaledFactor; this.translate(offsetX, offsetY); } Radar.prototype.setFocus = function (x, y) { var pos = this.coords2Pos(x, y); this.translate(this.focusOffset.X - pos.X, this.focusOffset.Y - pos.Y); this.focusOffset = pos; } // translates game coords to overlay coords Radar.prototype.game2Pix = function (p) { return p * (8130 / 813000) } Radar.prototype.coords2Pos = function (x, y) { return { X: this.game2Pix(x), Y: this.game2Pix(y) } } Radar.prototype.dot = function (x, y, color, width) { var pos = this.coords2Pos(x, y); var radius = 7 / this.scaledFactor; this.ctx.beginPath(); this.ctx.arc(pos.X, pos.Y, radius, 0, 2 * Math.PI, false); this.ctx.lineWidth = width || 5; this.ctx.fillStyle = color || 'red'; this.ctx.fill(); } Radar.prototype.pieChart = function (x, y, percent, color) { var pos = this.coords2Pos(x, y); var radius = 7 / this.scaledFactor; var startAngle = 1.5 * Math.PI; var endAngle = (percent * 2 * Math.PI) + 1.5 * Math.PI; // 扇形 this.ctx.fillStyle = color || 'gray'; this.ctx.beginPath(); this.ctx.moveTo(pos.X, pos.Y); this.ctx.arc(pos.X, pos.Y, radius, startAngle, endAngle, false); this.ctx.closePath(); this.ctx.fill(); } Radar.prototype.text = function (x, y, content, color) { var pos = this.coords2Pos(x, y); this.ctx.font = '' + 8 / this.scaledFactor + 'pt Calibri'; this.ctx.fillStyle = color || 'white'; this.ctx.textAlign = 'center'; this.ctx.fillText(content, pos.X, pos.Y + (3 / this.scaledFactor)); } // useless // Radar.prototype.floatText = function (posX, posY, content, color) { // this.ctx.font = '' + 8 / this.scaledFactor + 'pt Calibri'; // this.ctx.fillStyle = color || 'lightgreen'; // this.ctx.textAlign = 'left'; // this.ctx.fillText(content, posX - this.viewPortOffset.X, posY - this.viewPortOffset.Y); // } // from https://github.com/jerrytang67/helloworld Radar.prototype.lineWithAngle = function (x, y, length, width, angle, color) { var pos = this.coords2Pos(x, y); var anX = 5 * Math.cos(Math.PI * angle / 180.0); var anY = 5 * Math.sin(Math.PI * angle / 180.0); var x1 = pos.X + anX; var y1 = pos.Y + anY; var circle1 = { x: pos.X, y: pos.Y, r: 5 }; var circle2 = { x: x1, y: y1, r: 0 }; var arrow = { h: width / this.scaledFactor, w: length / this.scaledFactor }; drawArrow(this.ctx, arrow, circle1, circle2, color); //draw arrow -- uuaing function drawArrow(canvasContext, arrow, ptArrow, endPt, color) { var angleInDegrees = getAngleBetweenPoints(ptArrow, endPt); var endPt = getPointOnCircle(endPt.r, ptArrow, endPt); // first save the untranslated/unrotated context canvasContext.save(); // move the rotation point to the center of the rect canvasContext.translate(endPt.x, endPt.y); // rotate the rect canvasContext.rotate(angleInDegrees * Math.PI / 180); canvasContext.beginPath(); canvasContext.moveTo(0, 0); canvasContext.lineTo(0, -arrow.h); canvasContext.lineTo(arrow.w, 0); canvasContext.lineTo(0, +arrow.h); canvasContext.closePath(); canvasContext.fillStyle = color; canvasContext.lineWidth = 0; //canvasContext.stroke(); canvasContext.fill(); // restore the context to its untranslated/unrotated state canvasContext.restore(); } function getPointOnCircle(radius, originPt, endPt) { var angleInDegrees = getAngleBetweenPoints(originPt, endPt); // Convert from degrees to radians via multiplication by PI/180 var x = radius * Math.cos(angleInDegrees * Math.PI / 180) + originPt.x; var y = radius * Math.sin(angleInDegrees * Math.PI / 180) + originPt.y; return { x: x, y: y }; } function getAngleBetweenPoints(originPt, endPt) { var interPt = { x: endPt.x - originPt.x, y: endPt.y - originPt.y }; return Math.atan2(interPt.y, interPt.x) * 180 / Math.PI; } } ================================================ FILE: static/utils.js ================================================ var Utils = { getParameterByName: function (name) { var url = window.location.href; name = name.replace(/[\[\]]/g, "\\$&"); var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), results = regex.exec(url); if (!results) return null; if (!results[2]) return ''; return decodeURIComponent(results[2].replace(/\+/g, " ")); }, MinsCounter: function () { var Counter = function () { this.count = 0; this.last = 0; function clear() { this.last = this.count; this.count = 0; } setInterval(clear.bind(this), 1000 * 1); } Counter.prototype.getPerSec = function () { return this.last; } Counter.prototype.update = function () { this.count++; } return new Counter(); } }