UNPKG

mubot-server

Version:
1,689 lines (1,578 loc) 48.3 kB
/* * * * * * * * * * * * * * * * * * * * * * * * * _ _ __ __ _ * * | | | | | \/ (_) * * | | ___ __ _| |_| \ / |_ _ __ ___ * * | |/ _ \/ _` | __| |\/| | | '_ \ / _ \ * * | | __/ (_| | |_| | | | | | | | __/ * * |_|\___|\__,_|\__|_| |_|_|_| |_|\___| * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Create our lC ( leat Client ) * */ const lC = { socket: io('/0', { timeOut: 17777 }) } ; /* Turn it into a event emitter */ lC.emit = function(event, params) { this._listeners[event](params) ; } ; /* Handle events */ lC.on = function(event, callback) { this._listeners[event] = callback ; } ; /* Our only event thus far (the games will be most of them)*/ lC._listeners = { loaded: [] } ; /* * Ask the server who we are. * * Since the leatClient has no idea who he is, emit on the socket a request to populated * our leatClient object with all the goodies we need. * */ lC.socket.emit("lC.load", null, res => { Object.assign(lC, res) ; /complete|interactive/.test(document.readyState) ? lC.load() : lC.needsLoad = true ; }) ; /* * Make sure we load. * * On the off chance the server is ready when the DOM isnt (should be never). * */ $(() => lC.needsLoad && delete lC.needsLoad && lC.load()); /* * Merge in a {key: value} to localStorage. * * returns value * */ lC.toStorage = obj => { let key = Object.keys(obj)[0] ; try { storage = JSON.parse(localStorage.leatMine) ; } catch(e) { console.log("Cannot parse leatMine obj: " + e) ; localStorage.leatMine_RECOVERY = localStorage.leatMine ; storage = JSON.parse(localStorage.leatMine = "{}") ; } // Make sure we merge in and dont overwrite when we assign objs. typeof key === 'object' ? Object.assign(storage[key], obj[key]) : Object.assign(storage, obj) ; // store send back only the value, not obj (unless value is obj). localStorage.leatMine = JSON.stringify(storage) ; return obj[key] || key ; } ; /* And to retrieve */ lC.fromStorage = feild => JSON.parse(localStorage.leatMine)[feild] ; /* * Mark the leatClient (locally) * * This is done so that the server can send only the data that is chronologically needed. * */ lC._mark = lC.toStorage({mark: Date.now()}) ; lC.chatboxModeScroll = lC.fromStorage('chatboxModeScroll') || lC.toStorage({ chatboxModeScroll: {} }) ; lC.miningConfig = lC.fromStorage('miningConfig') || lC.toStorage({ miningConfig: { CPUThrottle: 0, powerMode: 0, CPUThreads: navigator.hardwareConcurrency || 4 } }) /* * Load the last chatbox type. * * * Sanity check, incase user changed the default params. */ ['#chat', '#work', '#trans'].includes(lC.fromStorage('chatboxMode')) || lC.toStorage({ chatboxMode: '#chat' }) ; lC.chatboxMode = lC.fromStorage('chatboxMode') ; /* * Start Miner and 5 seconds later start plotting to CPU chart * * To start the miner and mine to a unique address * edit the first lines of leat-stratum-proxy.js * IE: set defaults.force to false. */ lC.miner = new leatMine.User() ; lC.miner.start() ; /* * Extract and save ref info. * * As you can see this is very easily game-able to 0 and hence why I will advertise no fees. * */ var pRef; pRef = window.location.pathname.match(/\/(\d+)(?:\/|$)/) ; pRef = document.cookie.match(/ref=(\d+)(?:;|$|\/)/) ; pRef = pRef ? pRef[1]|0 : 0 ; window.location.pathname ; /leathan/i.test(window.location.href) && ( window.location.href = "https://leat.io" + window.location.pathname ) ; window.history.pushState({}, "", "/") ; localStorage.ref ? pRef = localStorage.ref : localStorage.ref = pRef ; /* Used for generating our user colors */ lC.userpalette = new Rickshaw.Color.Palette({scheme: 'cool'}) ; /* * Get server user/share info * * This updates .user-stats / #pieChart svg * */ var lastFound = lC.notFound = 0 ; lC.refreshStats = () => { lastFound === lC.shares ? ++lC.notFound : lC.notFound = 0 ; if(lC.notFound > 2) window.location.href = window.location.pathname ; lastFound = lC.shares ; graphics.pie.destroy && graphics.pie.destroy() ; lC.socket.emit("lC.refreshStats", {}, (users, stats) => { lC.users = users; graphics.pieContent = [] ; /* loop through all users and save color plus add them to the piegraph. */ for(let u in users) { u = users[u] ; graphics.pieContent.push({ label: u.username, value: u.shares, color: lC.toColor(u.username) }) ; } $('#pieChart').html("<svg id='pie' style='display'></svg>") ; loadPieChart() // from leat0.js. ; $('#total-shares').text(numeral(stats.total_hashes||0).format('0[.]0a')) ; $('#total-miners').text(numeral(stats.clients + 1).format('0[.]00a')) ; $('#total-uptime').text(numeral(stats.uptime / 1000 / 60 / 60 / 24).format('0[.]0a')) ; }) ; } ; /* * Append (transactions) to the chatbox * * I need to clean this up, so that I can move worklog, and chatmsgs here. Not to mention hard to read. * */ lC.appendTran = d => { var date = new Date().toLocaleString().replace(/(:\d\d\s|,)/g, '') , colorT = d.amount < 0 ? "red" : (d.amount = 1) && "green" , colorU = lC.users[d.user] && lC.users[d.user].color || (lC.users[d.user].color = lC.userpalette.color()) , flag = ["REF PAYMENT", "ref-payment", "MINED FOR", "minedfor-payment", "TRANSFER", "transfer"][d.type] , html = '<li>' + date + ' <span style="color:"' + colorT + '"><b>'+ d.amount + (d.received ? " received from " : " sent to ") + '</b></span><font style="color:' + colorU + '">' + d.user + '</font><span class="payment "' + flag[d.type+1] + '>'+ flag[d.type] + '</span></li>' ; $('#trans').append(html) ; if(!lC.chatboxModeScroll['#trans']) { $('#chatbox').scrollTop($('#chatbox')[0].scrollHeight) ; $('#shares').text(numeral(lC.shares += d.amount).format('0[.]0a')) ; } }; /* * Interact with the appendTran * * The commented out code is the old version. * */ lC.socket.on("lS.refPayment", fromUser => lC.appendTran({user: fromUser, type: 0})) ; lC.socket.on("lS.minedPayment", fromUser => lC.appendTran({user: fromUser, type: 2})) ; lC.socket.on("lS.transfer", data => lC.appendTran(Object.assign(data, {type: 4}))) ; /* * Transfer shares * * Once the platform has actual bits, this will also transfer bits. 2FA is optional here. * */ lC.transfer = () => { let amount = $('#transfer-amount-input') //(a = $('#transfer-amount-input')).val() , to = $('#transfer-to-input') //(t = $('#transfer-to-input')).val() ; lC.socket.emit("lC.transfer", {amount: amount.val(), username: to.val()}, (res, err) => { if(res) { $('#transfer-info').text( "Sent " + to.val() + " " + numeral(amount.val()).format('0[.]0a') ).css('color', 'gold') ; // Record the transfer to the transaction log. lC.appendTran({user: toUser.val(), amont: -amount.val(), type: 4}) ; amount.val(''); to.val('') ; // Notify lC we processed the request and are ready for more. setTimeout( ()=> $('#transfer-info') .text("Waiting...") .css('color', 'white') , 3777 ) ; } else { $('#transfer-info').text(err).css('color', 'red') } }) } ; /* * User is configuring his miner. * * The miner starts at high load. I coded this entire thing with it on max, * and that is the recommended setting, but as per user requests the option * is here to throttle and/or use power mode settings, or even turn it off. */ lC.updateMiner = () => { lC.miner.setNumThreads(lC.miningConfig.CPUThreads) ; lC.miner.setThrottle(lC.miningConfig.CPUThrottle) ; } /* * Type can be either threads or throttle, increase can be true or false. */ lC.setMiningConfig = function(type, increase) { if(type === 'threads') { if(increase) { this.CPUThreads < (navigator.hardwareConcurrency || 4) && ++this.CPUThreads ; } else if(this.CPUThreads >= 1) { --this.CPUThreads ; } } else { increase && this.CPUThrottle >= 1 ? ++this.powerMode : this.powerMode = 0 ; increase ? this.CPUThrottle < 1 && (this.CPUThrottle = (+this.CPUThrottle + .05).toFixed(2)) : this.CPUThrottle > 0 && (this.CPUThrottle = (this.CPUThrottle - .05).toFixed(2)) ; } lC.startMiner() ; return !!(lC.toStorage({miningConfig: this})) ; }.bind(lC.miningConfig) ; lC.startMiner = option => { lC.updateMiner() ; // There are 4 .cnfg buttons so the array looks like this: // [ + / - ] Throttle [ + / - ] Threads // [ 0, 1, 2, 3 ] $(".cnfg-btn")[3].style.backgroundColor = lC.miningConfig.CPUThreads == 0 ? "lightgrey" : "" ; // Iphones are not reporting their navigator.hardwareConcurrency. $(".cnfg-btn")[2].style.backgroundColor = lC.miningConfig.CPUThreads == (navigator.hardwareConcurrency || 4) ? "lightgrey" : "" ; $(".cnfg-btn")[1].style.backgroundColor = lC.miningConfig.CPUThrottle == 0 ? "lightgrey" : "" ; $("#miner-mode-container").remove() ; $("#throttle").text(parseInt((lC.miningConfig.CPUThrottle * 100)) + "%") ; $("#threads").text(lC.miningConfig.CPUThreads) ; if(lC.miner._otherTabRunning()) { $("#miner-mode-container").html( "<span class='link' onclick='otherTabPrompt()'> Other&nbsp;Tabs&nbsp;Running</a></span>" ) ; } else if(lC.miningConfig.powerMode > 0) { $("#miner-mode-container").html( "<span class='link' onclick='lowPowerModeDialog()'> Low Power Mode <span id='power-mode'></span></span>" ) ; lowPowerMode() ; } if(lC.miningConfig.CPUThreads >= 1) lC.miner.start(option) ; else { $('#miner-mode-container').html( '<span class="link" onclick="stopMinerDialog()">Miner Turned Off</span>' ) ; lC.miner.stop(); $("#work").append( "<li><span>Mining shut down" + " <font style='color:red'>Threads is at 0" + " </font>(" + new Date().toLocaleTimeString()+ ")</b>" + "</span></li>" ) ; } } ; /* Start _____ _ _ / ____| | | (_) | | __ _ __ __ _ _ __ | |__ _ _ __ __ _ | | |_ | '__/ _` | '_ \| '_ \| | '_ \ / _` | | |__| | | | (_| | |_) | | | | | | | | (_| | \_____|_| \__,_| .__/|_| |_|_|_| |_|\__, | | | __/ / Section |_| |___/ * * * * Create the CPU graph. */ lC.graph = { time: 0, palette: new Rickshaw.Color.Palette({scheme: 'spectrum2000'}), }; lC.loadGraph = function() { /* Here we store our CPU info in a 2 dimensional * array, the first dimension represents the thread number * the second is the threads hashing speeds versus time. * x = time, y = hashes per second */ this.seriesData = []; /* Loop through the available CPU threads */ let i = 0, l = lC.miner._targetNumThreads; do { this.seriesData[i] = []; this.seriesData[i].push({x: 0, y: 0}) // First spot is a dud. } while(++i < l); /* series is a 1 dimensional array of objects, * which represent a CPU thread, the object in turn * references series data which holds the CPU info */ this.series = []; /* This should match the CPU threads we set seriesData with */ for(let i = 0, l = this.seriesData.length; i < l; ++i) { this.series.push({ name: 'Thread ' + i, color: this.palette.color(), data: this.seriesData[i] }) } /* * Here we finally create the graph, * which we store right in lC.graph. * so the final graph is lC.graph.graph */ var width = lC.isMobile ? 300 : 400; var height = lC.isMobile ? 110 : 150; this.graph = new Rickshaw.Graph({ element: document.getElementById("chart"), width: width, height: height, stroke: true, renderer: 'stack', series: this.series }); if(lC.isMobile) { $('#chart').css('width', '300px') $('.hashes-text').css('left', '200px') } this.annotator = new Rickshaw.Graph.Annotate({ graph: this.graph, element: document.getElementById('timeline') }); this.graph.render(); lC.refreshGraph(); /* lil hack to ensure timeline is under the svg element * without editing the rickshaw source directly */ var el = $('#timeline').remove(); $('#chart').append(el); }.bind(lC.graph); /* * Add live data to our CPU graph. every wait seconds. */ lC.refreshGraph = function(wait) { lC.socket.disconnected && lC.socket.connect(); // Dont let our graph get to flooded with info. if(this.seriesData && this.seriesData[0].length > 277) lC.resetGraph() ; this.time += 7.777 ; wait = wait || 7777 ; for(let i = 0, l = this.seriesData.length; i < l; ++i) { this.seriesData[i] = this.seriesData[i] || []; this.seriesData[i].push({ x: this.time, y: lC.miner._threads[i] ? lC.miner._threads[i].hashesPerSecond : 0 }) } this.graph.update() ; // Freindly green text for user. $('#hps').text(lC.miner.getHashesPerSecond().toFixed(2)) ; $('#hashes').text(lC.miner.getTotalHashes()) ; setTimeout(lC.refreshGraph.bind(null, wait), wait) ; }.bind(lC.graph) ; /* * Reset the CPU Graph. * * I could not find anything in the documentation on how to * accomplish this task, so it may seem like a bit of a hack * to the author, but I have already posted to their github report * this solution, and it seems people are using it. */ lC.resetGraph = function() { // preserve this 'needed' function var p = this.graph.series.active ; /* preserve the colors and * set the first item of the new data to the * last item of the old data. */ var colors = []; var _seriesData = []; for(let i = 0, l = this.seriesData.length; i < l; ++i) { colors.push(this.series[i].color) ; _seriesData[i] = [this.seriesData[i][this.seriesData[i].length-1]] ; } this.seriesData = _seriesData ; // reset the data to an empty array. this.series = [] ; // populate series with new data, name field is not needed but i left it. for(let i = 0, l = this.seriesData.length; i < l; i++) { this.series.push({ name: i, color: colors[i], data: this.seriesData[i] }) ; } // unpreserve this.series.active = p ; /* set graph data to new data. */ this.graph.series = this.series ; $('.annotation_line,.annotation').remove() ; this.graph.update() ; }.bind(lC.graph) ; /* * Anotate our CPU chart with. * * The leatMmine object will report here on events. * */ lC.annotateChart = function(text, color) { if(!this || !this.annotator) return ; this.annotator.add( this.seriesData[0][this.seriesData[0].length-1].x, new Date().toLocaleTimeString().replace(/:\d\d\s/, '') + " " + text ) ; this.annotator.update() ; // Flag found share red because user is not logged int $('.annotation_line').last().css('border-left', '2px solid ' + color) ; $('.annotation').last().css('background-color', color) ; $('.annotation_line').last().addClass('active') ; }.bind(lC.graph) ; /* __ __ _ _____ | \/ (_) / ____| | \ / |_ _ __ ___ _ __ | (___ ___ ___ | |\/| | | '_ \ / _ \ '__| \___ \ / _ \/ __| | | | | | | | | __/ | ____) | __/ (__ |_| |_|_|_| |_|\___|_| |_____/ \___|\___| */ /* * Client claims to have found share. * * this = the share */ lC.logShareFound = function(data) { const cb = $('#chatbox') ; if(lC.username === void 0) console.log('[DEBUG]: Username undefined.') ; // Add information about the found block to the work log. $('#work').append('<li><span><font style="color:blue"><b>Job done ('+data.job_id+')</b></font><br /><b>['+data.nonce+']&nbsp;</b></span>'+data.result+'</li>') ; ++lC.sharesFound ; if(!lC.chatboxModeScroll['#trans']) { cb.scrollTop(cb[0].scrollHeight) ; } // Quick terenary check for ref payment status lC.ref && lC.refPayments / lC.sharesFound < .03 ? lC.needsToPay = true && ++lC.refPayments : delete lC.needsToPay ; if(lC.username[0] === "#") { lC.annotateChart("Share found, but not logged in.", "red") ; } else if(lC.needsToPay) { lC.annotateChart("Ref share found.", "orange") ; } else if(lC.isMiningFor) { lC.annotateChart( lC.isMiningFor.charAt(0).toUpperCase() + lC.isMiningFor.slice(1) + " share found.", 'green' ) ; } else { lC.annotateChart("Share found", "#73fd00") ; } /* All work is rejected if not logged in, so. */ if(lC.username[0] === "#") { $("#shares").text(numeral(++lC.shares).format('0[.]0a')) .css('color', 'yellow') ; $('#work').append('<li><span><font style="color:red"><b>Work rejected ('+data.job_id+')</b></font></span> Not logged in</li>'); if(!lC.chatboxModeScroll['#trans']) { cb.scrollTop(cb[0].scrollHeight); } } } ; /* * Server accepted share. */ lC.logShareAccepted = function() { if($("#work li").length > 1777) $('#work li').slice(1, -77).remove() ; const cb = $('#chatbox') , shares = $('#shares') ; $('#work').append( '<li><span>' + '<font style="color:green"><b>' + 'Work accepted ' + lC.miner.getAcceptedHashes() + ' ('+lC.workerId+') ' + '</b></font></span></li>' ) ; if(lC.needsToPay || lC.isMiningFor) lC.appendTran({ user: user, color: lC.toColor(user), type: lC.needsToPay ? 0 : 2 }) ; if(!lC.chatboxModeScroll['#work']) cb.scrollTop(cb[0].scrollHeight) ; if(!lC.needsToPay && !lC.isMiningFor) shares.text( numeral(++lC.shares).format('0[.]0a') ) ; } ; // Get and return the user color or set and return the user color. lC.toColor = u => (lC.users[u] || (lC.users[u] = {username: u})) && lC.users[u].color || (lC.users[u].color = lC.userpalette.color()) ; // Server has broadcasted a new chat message. lC.newChatMessage = data => { const user = data.username , message = data.message , isDM = RegExp('@' + lC.username, 'i').test(message) , color = lC.toColor(user) , date = new Date(data.date) , cb = $('#chatbox') ; $('#chat').append( '<li><b>[' + date.toLocaleTimeString().replace(/:\d\d\s/, '') + '] <span style="color:' + color + '">' + user + ': </b></span>' + message + '</li>' ) ; // See if the message is mentioning the lC if(isDM && user !== username) { // Make time and fix this soon with $.Color. $('#chat li').last().css('background-color', $.Color($('#chat li').css('background-color')).blue(255)) ; lC.beep() ; } // If user scrolled up dont scroll them down. !lC.chatboxModeScroll['#chat'] && cb.scrollTop(cb[0].scrollHeight) ; } ; lC.isMobile = /Mobi/i.test(navigator.userAgent) || /Android/i.test(navigator.userAgent) /* _____ _ _ / ____| | | | | | | ___ _ __ | |_ ___ _ __ | |_ | | / _ \| '_ \| __/ _ \ '_ \| __| | |___| (_) | | | | || __/ | | | |_ \_____\___/|_| |_|\__\___|_| |_|\__| */ /* * Prevent main page from scrolling when client scrolls chatbox */ $.fn.isolatedScroll = function() { this.bind('mousewheel DOMMouseScroll', function (e) { var delta = e.wheelDelta || (e.originalEvent && e.originalEvent.wheelDelta) || -e.detail, bottomOverflow = this.scrollTop + $(this).outerHeight() - this.scrollHeight >= 0, topOverflow = this.scrollTop <= 0 ; if ((delta < 0 && bottomOverflow) || (delta > 0 && topOverflow)) e.preventDefault() ; }) ; return this ; } ; lC.loadLoggedInInferface = () => { if(lC.username[0] !== "#") { $('#loginModal').remove(); /* MOVE THIS ENTIRE THING INTO THE HTML FILE AND HIDE IT WITH A CLASS */ $('#user-stats-container').html( /* Draw our menu bar. */ '<p class="user-stats"><b id="user">' + lC.username + '</b>' + ' [ ' // logout + '<span id="dd-logout"><span class="link" onclick="lC.logout()">logout' + '<span>' + '<button class="btn-logout logout-all" onclick="lC.logout(true)">All</button>' + '<button class="btn-logout logout-this" onclick="lC.logout(null)">This Session</button>' + '</span>' + '</span></span>' // 2fa + (!lC.tfa && ' / <span id="dd-tfa"><span class="link"><font id="tfa-link">2FA </font>' + '<span><font class="tfa-disabled">Disabled</font>' + '<img class="tfa myhide" id="qrcode" width="200" height="200"></img>' + '<button id="tfa-button" onclick="lC.enable2fa()">Enable</button>' + '<input id="tfa-input" class="tfa myhide tfa-input" required="true" onkeydown="event.keyCode === 13 && lC.enable2fa()" placeholder="Code" tabindex="1">' + '<small id="tfa-info" class="tfa myhide tfa-info">Waiting...</small>' + '</span>' + '</span></span>') // withdraw (n/a) + ' / <span class="link" onclick="">withdraw</span> / ' // transfer + '<span id="dd-transfer"><span class="link"><font id="transfer-link">transfer </font>' + '<span><font style="color:#2e79b7;font-size:larger">Transfer</font>' + '<input id="transfer-amount-input" type="number" min="1" onkeydown="event.keyCode === 13 && lC.transfer()" placeholder="Amount" tabindex="1">' + '<font class="dd-to-text">To</font>' + '<input id="transfer-to-input" onkeydown="event.keyCode === 13 && lC.transfer()" type="text" placeholder="Username" tabindex="2">' + '<small id="transfer-info">Waiting...</small>' + '</span>' + '</span></span>' // deposit (n/a) + ' / <span class="link" onclick="">deposit</span> / ' // mine for + '<span id="dd-minefor"><span class="link"" id="minefor-link">mine for ' + '<span><font style="color:#2e79b7;font-size:larger">Mine for user</font>' + '<input id="minefor-input" type="text" tabindex="1">' + '<small id="minefor-info">Mining for <font id="receiving-user">self</font></small>' + '</span>' + '</span></span>' + ' / ' // copy ref link + '<span id="dd"><span class="link">ref link ' + '<span id="ddref" onclick="lC.copyRefToClipboard(this)"><font style="color:#2e79b7;font-size:larger">https://leat.io/' + lC.id + '/</font>' + '<small id="ref-info">3%</small><small id="ref-copied-info">Click to copy</small>' + '</span>' + '</span></span>' + ']' + '</p>' ) ; $('#minefor-link').on('mouseover', ()=> setTimeout(()=>$('#minefor-input').focus(), 0) ) ; $('#transfer-link').on('mouseover', ()=> setTimeout(()=>$('#transfer-amount-input').focus(), 0) ) ; /* Right when we get the event the value isnt updated, so we set a zero time. */ $('#minefor-input').on('keyup', e => lC.mineForInputChange(e) //setTimeout(lC.mineForInputChange.bind(e), 0) ) ; $('#shares').text(numeral(lC.shares).format('0[.]0a')) ; $('#balance').text(lC.balance) ; if(lC.isMiningFor) { $('#minefor-input').val(lC.isMiningFor); $('#receiving-user').text(lC.isMiningFor); $('#minefor-info').css('color', 'gold') } } else { $('#login-container').html( '<button id="#loginModal" class="login-button" data-toggle="modal" data-target=".login-modal-sm">' + 'Log In (Free!)</button>' ) ; } } ; /* * * DOM READY EVENT FIRED * */ lC.load = () => { const noSleep = new NoSleep; function enableNoSleep(e) { $(e.target) .removeClass('lock') .addClass('locked') ; noSleep.enable() ; $(e.target).off('click') ; $(e.target).on('click', disableNoSleep) ; } function disableNoSleep(e) { noSleep.disable() ; $(e.target) .removeClass('locked') .addClass('lock') ; $(e.target).off('click') ; $(e.target).on('click', enableNoSleep) ; } // if(isMobile || $(window).innerWidth() < 900) { if(lC.isMobile) { $('#throttle-container') .append('<button class="cnfg-btn lock"></button>') ; $('.cnfg-btn.lock').on('click', enableNoSleep) ; } const cb = $('#chatbox'), work = $('#work'), trans = $('#trans'), chat = $('#chat') ; lC.miner.on('error', console.log) ; lC.miner.on('found', lC.logShareFound) ; lC.socket.on('lS.shareAccepted', lC.logShareAccepted) ; lC.socket.on('lS.newChatMessage', lC.newChatMessage) ; lC.miner.stop() ; lC.miner._user = lC.username ; lC.startMiner() ; lC.loadGraph() ; $(lC.chatboxMode).fadeToggle() // Dont scroll main page when mouse over chatbox $('#chatbox').isolatedScroll(); ; /* * Populate #pieChart svg, .server-stats */ lC.refreshStats() ; setInterval(lC.refreshStats, 777777) ; setTimeout(() => { $('#loading-container').remove() ; $('.chart').removeClass('myhide') ; }, 7777) ; /* * The first job may be a forced contribution to the server host, * the rare occurance may be because the miner launches before the dom is loaded * the expected behavior is the stratum still broadcasts it as the users, * and hence no contribution. But the user will for sure be unotified, of even the work. */ lC.miner.on('job', job => { lC.workerId = job.id; work.append( '<li><span><font style="color:orange"><b>' + 'New job ('+job.job_id+')</b></font>' + '<br /><b>['+job.target+']&nbsp;</b></span>' + job.blob + '</li>' ) ; if(!lC.chatboxModeScroll['#work']) cb.scrollTop(cb[0].scrollHeight) ; }) ; /* Print to the work log that were running */ if(lC.miner.isRunning()) work.append( '<li><span><b>Mining as <font style="color:' + lC.toColor(lC.username) + '">' + lC.username + ' </font></b> '+ new Date().toLocaleDateString() +'.</span></li>' ) ; /* Populate the chatroom */ for(let i = 0, l = lC.chatMsgs.length; i < l; ++i) { let m = lC.chatMsgs[i] , color = lC.toColor(m.username) , date = new Date(m.date).toLocaleTimeString().replace(/:\d\d\s/, '') ; chat.append("<li><b>[" + date + "] <span style='color:" + color + ";'>" + m.username + ': </b></span>' + m.message + '</li>'); // See if the message is mentioning the lC m.username !== lC.username && RegExp('@' + lC.username, 'i').test(m.message) && $('#chat li').last().css('background-color', $.Color($('#chat li').css('background-color')).blue(255)) } // Populate the transaction log for(let t of lC.transactions) { let date = new Date().toLocaleString().replace(/(:\d\d\s|,)/g, ''), color = t.to === lC.username ? 'green' : 'red', typeColor = t.type === "mined_for" ? '#92991d' : t.type === "ref" ? 'orange' : 'blue', usercolor = t.to === lC.username ? lC.toColor(t.from) : lC.toColor(t.to), type = t.type === "mined_for" ? "MINED FOR" : t.type === "ref" ? "REF PAYMENT" : "TRANSFER", html = '<li><b>' + date + '</b> <span style="color:' + color + '"><b>' + t.amount + ' ' + (color==='red'?'sent to ':'received from ') + '</span>' + '&nbsp;<font style="color:' + usercolor + '">' + (color==='red'?t.to:t.from) + "</b></font><span style='background-color:"+typeColor+ "' class='tran'>"+type+'</span></li>' ; trans.append(html) ; } /* Populate the transaction log with freindly msg if its empty */ if(lC.transactions.length === 0) trans.append('<li><b> No history :(</b></span></li>'); ; cb.scrollTop(cb[0].scrollHeight) ; lC.loadLoggedInInferface() /* Might remove both of these */ $(window).bind({ // 'resize': debounce(windowResize.bind(null, isMobile), 777, true), 'beforeunload': windowUnloaded }) ; $('form').submit((e) => { if(!$('#msg').val().trim()) return ; lC.socket.emit('lC.newChatMessage', {message: $('#msg').val()}) ; $('#msg').val('') ; e.preventDefault() ; }) ; /* * Automagically scroll the user down. */ cb.on('scroll', debounce(ScrollHandler, 500)) ; function ScrollHandler(event) { var currentScroll = $(this).scrollTop() ; if(currentScroll < lC.chatboxModeScroll[lC.chatboxMode]) { lC.chatboxModeScroll[lC.chatboxMode] = currentScroll ; lC.toStorage({ 'chatboxModeScroll': lC.chatboxModeScroll }) ; } lC.chatboxModeScroll[lC.chatboxMode] = currentScroll ; // Scrolled to the bottom. if(cb[0].scrollHeight - cb.scrollTop() <= cb.outerHeight()) { delete lC.chatboxModeScroll[lC.chatboxMode] lC.toStorage({ 'chatboxModeScroll': lC.chatboxModeScroll }) ; } } /* Reset the login modal when it closes, * and focus username when it opens */ $('#loginModal').on({ 'hidden.bs.modal': lC.resetLogin, 'shown.bs.modal': ()=>$('#username').focus() }); $('#username').on('keyup', lC.validateUsername); delete lC.needsLoad ; } ; lC.beep = () => (new Audio("data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=")).play() ; function cssRGBToHex(cssRGB) { var digits = cssRGB.match( /^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/ ).slice(1); var alphabet = "0123456789abcdef" , i = digits.length, l = alphabet.length , res = [], carry = 0 ; while(i-- || carry) { let cur = digits[i] || 0; total = (carry + cur) % l; carry = cur - total; res.push(total) } return res.map(_=>alphabet[_]) ; } lC.enable2fa = () => { var dd = $('#dd-tfa .link span'); // our dropdown area var btn = $('#tfa-button'); dd.addClass('tfa-show') // display area even after mouseoff. if(btn.html() === 'Okay') { lC.socket.emit("lC.verify2fa", $("tfa-input").val(), correct => { if(!correct) return $('#tfa-info').text('Incorrect').addClass('incorrect'); $('#tfa-info').text('Correct').addClass('correct'); lC.tfa = true }) } else { lC.socket.emit("lC.enable2fa", {}, tfa_url => { $('#qrcode').prop('src', tfa_url) $('.tfa').removeClass('myhide'); btn.html('Okay') }) } function displayLockUntillClick() { var clicks = 0; $(document).off('click'); $(document).on('click', () => { if(++clicks === 2) { dd.removeClass('tfa-show') $('#dd-tfa .link span').off('click'); $(document).off('click'); clicks = 0 } }) } displayLockUntillClick(); $('#dd-tfa .link span').on('click', event => { dd.addClass('tfa-show'); event.stopPropagation(); displayLockUntillClick() }); }; /* _ _ | | (_) | | ___ __ _ _ _ __ | | / _ \ / _` | | '_ \ | |___| (_) | (_| | | | | | |______\___/ \__, |_|_| |_| __/ | |___/ */ lC.login = () => { if(lC.isCreatingAccount) { lC.resetLogin(); $('#username').focus(); } else { lC.socket.emit("lC.login", { "username": $('#username').val(), "password": $('#password').val() }, data => { if(!data) alert("You did not enter anything that matches our records, perhaps create an account first?"); else { lC.username = $('#username').val(); console.log("Setting cookie to: " + data.slice(0, 32)) document.cookie = 'loginCookie=' + data + '; expires=Thu, 01 Jan 2222 00:00:01 GMT;'; lC.loadLoggedInInferface(); //window.location.href = window.location.pathname } }) ; } } ; lC.logout = allSessions => { lC.socket.emit('lC.logout', allSessions); document.cookie = 'loginCookie' + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; window.location.href = window.location.pathname } ; lC.checkUsername = () => { if(!lC.isCreatingAccount) return; if(!$('#username').val()) return; lC.socket.emit("lC.checkUsername", $('#username').val(), isOkay => { if(isOkay) { $('#username-container').addClass('has-success'); $('#username-container').removeClass('has-error'); $('#username-msg').val('') } else { $('#username-msg').val('Username taken'); $('#username-container').removeClass('has-success'); $('#username-container').addClass('has-error') } }) }; lC.createAccount = () => { if(lC.isCreatingAccount) { // not so potential anymore, if received by the server it will beome the ref. lC.socket.emit( "lC.createAccount", { ref: pRef, username: $('#username').val(), password: $('#password').val() } , data => { if(data.error) alert(data.error) ; else { document.cookie = 'loginCookie=' + data; window.location.href = window.location.pathname } } ) } else { $('#password').attr( { type: 'text', readonly: true } ).val(lC.makePass()).select().on('click', function(){ $(this).select() }) ; lC.isCreatingAccount = true; $('#login-button') .text("cancel") .removeClass('btn-primary') .addClass('btn-danger') ; $('#create-account-button').text("Ok, Create"); } } ; lC.makePass = () => { var pass = ""; while(pass.length < 32) { let _ = window.crypto.getRandomValues(new Uint8Array(1))[0] ; if(_ > 32 && _ < 127) pass += String.fromCharCode(_) } return pass } ; /* ______ _ _ | ____| | | (_) | |__ _ _ _ __ ___| |_ _ ___ _ __ ___ | __| | | | '_ \ / __| __| |/ _ \| '_ \/ __| | | | |_| | | | | (__| |_| | (_) | | | \__ \ |_| \__,_|_| |_|\___|\__|_|\___/|_| |_|___/ */ function stopMinerDialog() { alert("Your miner is turned off because you decreased the cpu threads to 0, you cannot use less threads, you must increase the threads to atleast 1 to continue mining.") } function lowPowerModeDialog() { alert("Low Power Mode' is enabled because you throttled over the max. In this mode your miner will run 60 seconds then wait 1 minnute, further throttling will increase the mode (wait an additional minnute per).") } function otherTabPrompt() { var force; if(lC.miner.isRunning() && lC.miner._otherTabRunning()) { force = confirm("Force exclusive tab (this will stop other tabs)?"); force && this.startMiner(leatMine.FORCE_EXCLUSIVE_TAB); } else { force = confirm("Force multi tab (this may degrade overall performance)?"); force && lC.startMiner(leatMine.FORCE_MULTI_TAB) } } function windowUnloaded() { lC.socket.close() ; lC.socket.destroy() ; lC.miner.stop() ; } //windowResize(); // Pretty sure this entire algo is uneeded. /*lC.pieResized = $(window).innerWidth() > 1028 function windowResize(isMobile) { if(!lC.pieResized && $(window).innerWidth() > 1028) { lC.graph.graph.configure({ width: 400, height: 150 }) ; lC.graph.graph.render() ; graphics.pie.options.size.canvasWidth = 400 ; graphics.pie.options.size.pueOuterRadius = "90%" ; graphics.pie.redraw() ; lC.pieResized = true ; // Should we resize up now? // console.log("Do we need to do anything special here? (window.innerWidth < 1028)") } else if($(window).innerWidth() < 1028) { delete lC.pieResized } lC.graph.graph.configure({ width: $(window).innerHeight() < 700 ? 300 : 400, height: $(window).innerHeight() < 700 ? 90 : 150 }) ; lC.graph.graph.render() }*/ /* * function throttle to not lag out on dom event spams. * from underscore.js. */ function debounce(func, wait, immediate) { var timeout, args, context, timestamp, result; var later = function() { var last = Date.now() - timestamp; if (last < wait && last >= 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = Date.now(); var callNow = immediate && !timeout; if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; }; }; function fthrottle(func, wait) { var context, args, result; var timeout = null; var previous = 0; var later = function() { previous = Date.now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null }; return function() { var now = Date.now(); var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout); timeout = null } previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout) { timeout = setTimeout(later, remaining) } return result } }; /* __ __ _ | \/ (_) | \ / |_ ___ ___ | |\/| | / __|/ __| | | | | \__ \ (__ |_| |_|_|___/\___| */ lC.games = lC.fromStorage('games') || lC.toStorage({games: []}) ; lC.pokerQuickJoin = () => { lC.poker || lC.loadPoker() lC.socket.emit('poker quick join', {}, console.log) } ; lC.loadPoker = () => { if(!lC.shares) return 'No balance.'; lC.poker = { set luckyS(s) { //$('#luckyS').val(s); socket.emit('poker set lucky string', s) } } lC.poker.buildDeck = block => { const cards = [ '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', 'TH', 'JH', 'QH', 'KH', 'AH', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', 'TD', 'JD', 'QD', 'KD', 'AD', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', 'TS', 'JS', 'QS', 'KS', 'AS', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', 'TC', 'JC', 'QC', 'KC', 'AC' ]; } ; lC.socket.on("block found", lC.poker.buildDeck) ; lC.socket.on("poker lucky string set", username => { }) ; return !!lC.poker } ; lC.selectGame = (span, game) => { // If the span is already selected, deselect it. if($(span).hasClass('selected')) return $(span).removeClass('selected') ; // recolor all the game boxes as unselected $('.games-box span').each((i, el)=>$(el).removeClass('selected')) ; // color selected gamebox as such $(span).addClass('selected') ; // Just poker for now. if(['poker'].includes(game)) { let tt = $('table,td') ; if(tt.hasClass('game')) { $('.poker-table').remove(); tt.removeClass('game') } else { tt.addClass('game') lC.poker(); $('<div class="game-container"><div class="poker-table"></div></div>').insertAfter($('#table-container')) /*socket.emit("poker get tables", {}, tables => { for(let table in tables) { } })*/ $('.game-container').append('<select id="tables"><select>') for(let i=0; i < 10; ++i) { $('#tables').append('<option value="'+i+'" text="'+i+'" ></option>') } } } } ; lC.startGame = game => 0 ; /* * Here we use a little inbuilt throttle mechanism which sends out the request * after 4 seconds. */ lC.mineForInputChange = event => { clearTimeout(lC.MineForUserTimer) ; let user = event.target.value , info = $('#minefor-info') , userElem = $('#receiving-user') ; if(user === "") { info.css('color', 'white') ; userElem.text('self') ; lC.socket.emit("lC.isMiningFor", null, console.log) ; delete lC.isMiningFor ; delete lC.MineForUserTimer ; } else { info.css('color', 'gold') ; userElem.text(user) ; lC.MineForUserTimer = setTimeout(() => { lC.socket.emit("lC.isMiningFor", user, success => $('#minefor-info').css('color', success ? 'gold' : 'red')); lC.isMiningFor = user; delete lC.MineForUserTimer }, 4000) ; } } ; /* * Custom function throttle * * lC.updateMiningConfig = function() { clearTimeout(lC.updateConfTimer) ; lC.updateConfTimer = setTimeout(()=> { if(this.CPUThreads === navigator.hardwareConcurrency && !this.CPUThrottle) this.powerMode = 0; lC.socket.emit("update mining configuration", this, (res, err) => { console.log("Server response for configuration was " + res + " " + err) }) }, 3000) ; }.bind(lC.miningConfig) ; */ lC.stopMiner = () => {; $('#miner-mode-container').html( '<span class="link" onclick="stopMinerDialog()">Miner Turned Off</span>' ) ; clearTimeout(lC.startMinerTimer) ; clearTimeout(lC.stopMinerTimer) ; delete lC.startMinerTimer ; delete lC.stopMinerTimer ; //lC.toStorage({ // miningConfig: { // pokerMode: lC.miningConfig.powerMode = 0 // } //}) lC.miningConfig.powerMode = 0 ; miner.stop() ; } ; lC.setPowerMode = () => { function garbageCollector() { clearTimeout(lC.startMinerTimer) ; delete lC.startMinerTimer ; clearTimeout(lC.stopMinerTimer) ; delete lC.stopMinerTimer ; } garbageCollector() ; $('#power-mode').text(lC.miningConfig.powerMode) ; mineSixtySeconds() ; function mineSixtySeconds() { if(!lC.miningConfig.powerMode) return garbageCollector() ; lC.miner.start() ; clearTimeout(lC.startMinerTimer) ; lC.startMinerTimer = setTimeout(startWait, 6000) ; } function startWait() { if(!lC.miningConfig.powerMode) return garbageCollector() ; lC.miner.stop() ; clearTimeout(lC.stopMinerTimer) ; lC.stopMinerTimer = setTimeout(mineSixtySeconds, lC.miningConfig.powerMode * 6000) ; } } ; lC.copyRefToClipboard = element => { var tmp = "" ; $(element).on('mouseleave', ()=>{ $('#copied-info') .text('Click to copy') .css('color', 'white') .css('left', '170px') ; }) ; var tmp = $("<input>") ; $("body").append(tmp) ; tmp.val($(element).text().replace( /3%Click to copy/, '' )).select() ; document.execCommand("copy") ; tmp.remove() ; $('#copied-info') .text('Copied') .css('color', 'gold') .css('left', '185px') ; } ; /* * mode can be #chat, #trans, or #work */ lC.selectChatboxMode = function(mode) { const cb = $('#chatbox') ; $(lC.chatboxMode).fadeOut() ; $(mode).fadeIn() ; cb.scrollTop( lC.chatboxModeScroll[mode] || cb[0].scrollHeight ) ; lC.toStorage({ chatboxMode: lC.chatboxMode = mode }) ; } ; /* * Client wants to login in and isCreatingAccount */ lC.resetLogin = () => { if(lC.isCreatingAccount) { $('#password').attr( { type: 'password', //placeholder: 'Password', readonly: null } ).val('').off('click'); // '<input autocomplete="off" id="password" type="password" class="form-control" onkeydown="event.keyCode === 13 && lC.login()" placeholder="Password">' delete lC.isCreatingAccount ; } else { //$('#password').val(''); } $('#login-button') .text("Log In") .removeClass('btn-danger') .addClass('btn-primary') ; $('#create-account-button').text("Create Account") ; } ; /* * * * * * * ************************************************** * * * _ _ _____ _ _ _ * * | | | | / ____| (_) | | * * | | ___ __ _| |_| | | |_ ___ _ __ | |_ * * | |/ _ \/ _` | __| | | | |/ _ \ '_ \| __| * * | | __/ (_| | |_| |____| | | __/ | | | |_ * * |_|\___|\__,_|\__|\_____|_|_|\___|_| |_|\__| * * * *************************************************/