From 454ae98fa6e87da247f5a8cc4d3e24eb53cf73d6 Mon Sep 17 00:00:00 2001 From: pk910 Date: Thu, 7 Sep 2023 06:04:59 +0200 Subject: [PATCH 01/10] prepare live front page --- cmd/explorer/main.go | 2 ++ handlers/index.go | 11 +++++++++ static/css/layout.css | 4 ++++ templates/index/recentBlocks.html | 4 ++-- templates/index/recentEpochs.html | 37 ++++++++++++++++++++++--------- 5 files changed, 46 insertions(+), 12 deletions(-) diff --git a/cmd/explorer/main.go b/cmd/explorer/main.go index 8830e2cd..b3f0ed5f 100644 --- a/cmd/explorer/main.go +++ b/cmd/explorer/main.go @@ -68,6 +68,8 @@ func startFrontend() { router := mux.NewRouter() router.HandleFunc("/", handlers.Index).Methods("GET") + router.HandleFunc("/index", handlers.Index).Methods("GET") + router.HandleFunc("/index/data", handlers.IndexData).Methods("GET") router.HandleFunc("/clients", handlers.Clients).Methods("GET") router.HandleFunc("/epochs", handlers.Epochs).Methods("GET") router.HandleFunc("/epoch/{epoch}", handlers.Epoch).Methods("GET") diff --git a/handlers/index.go b/handlers/index.go index 14343dd5..fef69c65 100644 --- a/handlers/index.go +++ b/handlers/index.go @@ -2,6 +2,7 @@ package handlers import ( "bytes" + "encoding/json" "fmt" "math" "net/http" @@ -39,6 +40,16 @@ func Index(w http.ResponseWriter, r *http.Request) { } } +func IndexData(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + pageData := getIndexPageData() + err := json.NewEncoder(w).Encode(pageData) + if err != nil { + logrus.WithError(err).Error("error encoding index data") + http.Error(w, "Internal server error", http.StatusServiceUnavailable) + } +} + func getIndexPageData() *models.IndexPageData { pageData := &models.IndexPageData{} pageCacheKey := "index" diff --git a/static/css/layout.css b/static/css/layout.css index 43a7cc72..405ca043 100644 --- a/static/css/layout.css +++ b/static/css/layout.css @@ -229,6 +229,10 @@ header nav .nav-item:focus-within .dropdown-menu { white-space: nowrap; } +.table .template-row { + display: none; +} + span.validator-label { white-space: nowrap; } diff --git a/templates/index/recentBlocks.html b/templates/index/recentBlocks.html index 6eb38624..347a8df5 100644 --- a/templates/index/recentBlocks.html +++ b/templates/index/recentBlocks.html @@ -22,7 +22,7 @@
{{ formatAddCommas $block.Epoch }} {{ if eq .Status 2 }} {{ formatAddCommas $block.Slot }} @@ -50,7 +50,7 @@
no blocks found diff --git a/templates/index/recentEpochs.html b/templates/index/recentEpochs.html index 491344f9..2eca1754 100644 --- a/templates/index/recentEpochs.html +++ b/templates/index/recentEpochs.html @@ -18,10 +18,29 @@
+ + + + + + + Yes + No + + + +
+ +
+
+
+
+ + + {{ if gt .RecentEpochCount 0 }} {{ range $i, $epoch := .RecentEpochs }} - + {{ formatAddCommas $epoch.Epoch }} {{ formatRecentTimeShort $epoch.Ts }} @@ -36,22 +55,20 @@
{{ formatEthAddCommasFromGwei $epoch.TargetVoted }} ({{ formatFloat $epoch.VoteParticipation 2 }}%)
-
+
{{ end }} - - {{ else }} - - + {{ else }} + no epochs found - - {{ end }} + {{ end }} + From cba4a30ae3d9109cd10576a503541f4e47b06d46 Mon Sep 17 00:00:00 2001 From: pk910 Date: Fri, 8 Sep 2023 02:09:50 +0200 Subject: [PATCH 02/10] implemented background refresh for startpage --- handlers/index.go | 6 ++ static/js/explorer.js | 41 ++++---- static/js/knockout.min.js | 139 +++++++++++++++++++++++++++ static/js/page-index.js | 133 +++++++++++++++++++++++++ templates/_layout/layout.html | 5 +- templates/index/index.html | 16 ++- templates/index/networkOverview.html | 61 ++++++------ templates/index/recentBlocks.html | 54 +++++++++-- templates/index/recentEpochs.html | 27 ++++-- templates/index/recentSlots.html | 55 +++++++++-- types/models/indexPage.go | 6 +- utils/templateFucs.go | 1 + 12 files changed, 464 insertions(+), 80 deletions(-) create mode 100644 static/js/knockout.min.js create mode 100644 static/js/page-index.js diff --git a/handlers/index.go b/handlers/index.go index fef69c65..9170e055 100644 --- a/handlers/index.go +++ b/handlers/index.go @@ -6,6 +6,8 @@ import ( "fmt" "math" "net/http" + "net/url" + "strconv" "strings" "time" @@ -92,6 +94,7 @@ func buildIndexPageData() (*models.IndexPageData, time.Duration) { NetworkName: utils.Config.Chain.Name, DepositContract: utils.Config.Chain.Config.DepositContractAddress, ShowSyncingMessage: !isSynced, + SlotsPerEpoch: utils.Config.Chain.Config.SlotsPerEpoch, CurrentEpoch: uint64(currentEpoch), CurrentFinalizedEpoch: finalizedEpoch, CurrentSlot: currentSlot, @@ -234,6 +237,9 @@ func buildIndexPageRecentBlocksData(pageData *models.IndexPageData, currentSlot if blockData.EthBlockNumber != nil { blockModel.WithEthBlock = true blockModel.EthBlock = *blockData.EthBlockNumber + if utils.Config.Frontend.EthExplorerLink != "" { + blockModel.EthBlockLink, _ = url.JoinPath(utils.Config.Frontend.EthExplorerLink, "block", strconv.FormatUint(blockModel.EthBlock, 10)) + } } pageData.RecentBlocks = append(pageData.RecentBlocks, blockModel) } diff --git a/static/js/explorer.js b/static/js/explorer.js index b10cc065..af091da9 100644 --- a/static/js/explorer.js +++ b/static/js/explorer.js @@ -7,6 +7,7 @@ }); window.explorer = { initControls: initControls, + renderRecentTime: renderRecentTime, }; function initControls() { @@ -18,24 +19,28 @@ // init clipboard buttons var clipboard = new ClipboardJS("[data-clipboard-text]"); - clipboard.on("success", function (e) { - var title = e.trigger.getAttribute("data-bs-original-title"); - var tooltip = bootstrap.Tooltip.getInstance(e.trigger); - tooltip.setContent({ '.tooltip-inner': 'Copied!' }); - tooltip.show(); - setTimeout(function () { - tooltip.setContent({ '.tooltip-inner': title }); - }, 1000); - }); - clipboard.on("error", function (e) { - var title = e.trigger.getAttribute("data-bs-original-title"); - var tooltip = bootstrap.Tooltip.getInstance(e.trigger); - tooltip.setContent({ '.tooltip-inner': 'Failed to Copy!' }); - tooltip.show(); - setTimeout(function () { - tooltip.setContent({ '.tooltip-inner': title }); - }, 1000); - }); + clipboard.on("success", onClipboardSuccess); + clipboard.on("error", onClipboardError); + } + + function onClipboardSuccess(e) { + var title = e.trigger.getAttribute("data-bs-original-title"); + var tooltip = bootstrap.Tooltip.getInstance(e.trigger); + tooltip.setContent({ '.tooltip-inner': 'Copied!' }); + tooltip.show(); + setTimeout(function () { + tooltip.setContent({ '.tooltip-inner': title }); + }, 1000); + } + + function onClipboardError(e) { + var title = e.trigger.getAttribute("data-bs-original-title"); + var tooltip = bootstrap.Tooltip.getInstance(e.trigger); + tooltip.setContent({ '.tooltip-inner': 'Failed to Copy!' }); + tooltip.show(); + setTimeout(function () { + tooltip.setContent({ '.tooltip-inner': title }); + }, 1000); } function updateTimers() { diff --git a/static/js/knockout.min.js b/static/js/knockout.min.js new file mode 100644 index 00000000..d7520e3b --- /dev/null +++ b/static/js/knockout.min.js @@ -0,0 +1,139 @@ +/*! + * Knockout JavaScript library v3.5.1 + * (c) The Knockout.js team - http://knockoutjs.com/ + * License: MIT (http://www.opensource.org/licenses/mit-license.php) + */ + +(function() {(function(n){var A=this||(0,eval)("this"),w=A.document,R=A.navigator,v=A.jQuery,H=A.JSON;v||"undefined"===typeof jQuery||(v=jQuery);(function(n){"function"===typeof define&&define.amd?define(["exports","require"],n):"object"===typeof exports&&"object"===typeof module?n(module.exports||exports):n(A.ko={})})(function(S,T){function K(a,c){return null===a||typeof a in W?a===c:!1}function X(b,c){var d;return function(){d||(d=a.a.setTimeout(function(){d=n;b()},c))}}function Y(b,c){var d;return function(){clearTimeout(d); +d=a.a.setTimeout(b,c)}}function Z(a,c){c&&"change"!==c?"beforeChange"===c?this.pc(a):this.gb(a,c):this.qc(a)}function aa(a,c){null!==c&&c.s&&c.s()}function ba(a,c){var d=this.qd,e=d[r];e.ra||(this.Qb&&this.mb[c]?(d.uc(c,a,this.mb[c]),this.mb[c]=null,--this.Qb):e.I[c]||d.uc(c,a,e.J?{da:a}:d.$c(a)),a.Ja&&a.gd())}var a="undefined"!==typeof S?S:{};a.b=function(b,c){for(var d=b.split("."),e=a,f=0;fa.a.A(c,b)&&c.push(b)});return c},Mb:function(a, +b,c){var d=[];if(a)for(var e=0,l=a.length;ee?d&&b.push(c):d||b.splice(e,1)},Ba:g,extend:c,setPrototypeOf:d,Ab:g?d:c,P:b,Ga:function(a,b,c){if(!a)return a;var d={},e;for(e in a)f.call(a,e)&&(d[e]= +b.call(c,a[e],e,a));return d},Tb:function(b){for(;b.firstChild;)a.removeNode(b.firstChild)},Yb:function(b){b=a.a.la(b);for(var c=(b[0]&&b[0].ownerDocument||w).createElement("div"),d=0,e=b.length;dp?a.setAttribute("selected",b):a.selected=b},Db:function(a){return null===a||a===n?"":a.trim? +a.trim():a.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},Ud:function(a,b){a=a||"";return b.length>a.length?!1:a.substring(0,b.length)===b},vd:function(a,b){if(a===b)return!0;if(11===a.nodeType)return!1;if(b.contains)return b.contains(1!==a.nodeType?a.parentNode:a);if(b.compareDocumentPosition)return 16==(b.compareDocumentPosition(a)&16);for(;a&&a!=b;)a=a.parentNode;return!!a},Sb:function(b){return a.a.vd(b,b.ownerDocument.documentElement)},kd:function(b){return!!a.a.Lb(b,a.a.Sb)},R:function(a){return a&& +a.tagName&&a.tagName.toLowerCase()},Ac:function(b){return a.onError?function(){try{return b.apply(this,arguments)}catch(c){throw a.onError&&a.onError(c),c;}}:b},setTimeout:function(b,c){return setTimeout(a.a.Ac(b),c)},Gc:function(b){setTimeout(function(){a.onError&&a.onError(b);throw b;},0)},B:function(b,c,d){var e=a.a.Ac(d);d=l[c];if(a.options.useOnlyNativeEvents||d||!v)if(d||"function"!=typeof b.addEventListener)if("undefined"!=typeof b.attachEvent){var k=function(a){e.call(b,a)},f="on"+c;b.attachEvent(f, +k);a.a.K.za(b,function(){b.detachEvent(f,k)})}else throw Error("Browser doesn't support addEventListener or attachEvent");else b.addEventListener(c,e,!1);else t||(t="function"==typeof v(b).on?"on":"bind"),v(b)[t](c,e)},Fb:function(b,c){if(!b||!b.nodeType)throw Error("element must be a DOM node when calling triggerEvent");var d;"input"===a.a.R(b)&&b.type&&"click"==c.toLowerCase()?(d=b.type,d="checkbox"==d||"radio"==d):d=!1;if(a.options.useOnlyNativeEvents||!v||d)if("function"==typeof w.createEvent)if("function"== +typeof b.dispatchEvent)d=w.createEvent(k[c]||"HTMLEvents"),d.initEvent(c,!0,!0,A,0,0,0,0,0,!1,!1,!1,!1,0,b),b.dispatchEvent(d);else throw Error("The supplied element doesn't support dispatchEvent");else if(d&&b.click)b.click();else if("undefined"!=typeof b.fireEvent)b.fireEvent("on"+c);else throw Error("Browser doesn't support triggering events");else v(b).trigger(c)},f:function(b){return a.O(b)?b():b},bc:function(b){return a.O(b)?b.v():b},Eb:function(b,c,d){var l;c&&("object"===typeof b.classList? +(l=b.classList[d?"add":"remove"],a.a.D(c.match(q),function(a){l.call(b.classList,a)})):"string"===typeof b.className.baseVal?e(b.className,"baseVal",c,d):e(b,"className",c,d))},Bb:function(b,c){var d=a.a.f(c);if(null===d||d===n)d="";var e=a.h.firstChild(b);!e||3!=e.nodeType||a.h.nextSibling(e)?a.h.va(b,[b.ownerDocument.createTextNode(d)]):e.data=d;a.a.Ad(b)},Yc:function(a,b){a.name=b;if(7>=p)try{var c=a.name.replace(/[&<>'"]/g,function(a){return"&#"+a.charCodeAt(0)+";"});a.mergeAttributes(w.createElement(""),!1)}catch(d){}},Ad:function(a){9<=p&&(a=1==a.nodeType?a:a.parentNode,a.style&&(a.style.zoom=a.style.zoom))},wd:function(a){if(p){var b=a.style.width;a.style.width=0;a.style.width=b}},Pd:function(b,c){b=a.a.f(b);c=a.a.f(c);for(var d=[],e=b;e<=c;e++)d.push(e);return d},la:function(a){for(var b=[],c=0,d=a.length;c",""],d=[3,"","
"],e=[1,""],f={thead:c,tbody:c,tfoot:c,tr:[2,"","
"],td:d,th:d,option:e,optgroup:e},g=8>=a.a.W;a.a.ua=function(c,d){var e;if(v)if(v.parseHTML)e=v.parseHTML(c,d)||[];else{if((e=v.clean([c],d))&&e[0]){for(var l=e[0];l.parentNode&&11!==l.parentNode.nodeType;)l=l.parentNode; +l.parentNode&&l.parentNode.removeChild(l)}}else{(e=d)||(e=w);var l=e.parentWindow||e.defaultView||A,p=a.a.Db(c).toLowerCase(),q=e.createElement("div"),t;t=(p=p.match(/^(?:\x3c!--.*?--\x3e\s*?)*?<([a-z]+)[\s>]/))&&f[p[1]]||b;p=t[0];t="ignored
"+t[1]+c+t[2]+"
";"function"==typeof l.innerShiv?q.appendChild(l.innerShiv(t)):(g&&e.body.appendChild(q),q.innerHTML=t,g&&q.parentNode.removeChild(q));for(;p--;)q=q.lastChild;e=a.a.la(q.lastChild.childNodes)}return e};a.a.Md=function(b,c){var d=a.a.ua(b, +c);return d.length&&d[0].parentElement||a.a.Yb(d)};a.a.fc=function(b,c){a.a.Tb(b);c=a.a.f(c);if(null!==c&&c!==n)if("string"!=typeof c&&(c=c.toString()),v)v(b).html(c);else for(var d=a.a.ua(c,b.ownerDocument),e=0;eb){if(5E3<= +++c){h=f;a.a.Gc(Error("'Too much recursion' after processing "+c+" task groups."));break}b=f}try{d()}catch(p){a.a.Gc(p)}}}function c(){b();h=f=e.length=0}var d,e=[],f=0,g=1,h=0;A.MutationObserver?d=function(a){var b=w.createElement("div");(new MutationObserver(a)).observe(b,{attributes:!0});return function(){b.classList.toggle("foo")}}(c):d=w&&"onreadystatechange"in w.createElement("script")?function(a){var b=w.createElement("script");b.onreadystatechange=function(){b.onreadystatechange=null;w.documentElement.removeChild(b); +b=null;a()};w.documentElement.appendChild(b)}:function(a){setTimeout(a,0)};return{scheduler:d,zb:function(b){f||a.na.scheduler(c);e[f++]=b;return g++},cancel:function(a){a=a-(g-f);a>=h&&ad[0]?p+d[0]: +d[0]),p);for(var p=1===g?p:Math.min(c+(d[1]||0),p),g=c+g-2,h=Math.max(p,g),U=[],L=[],n=2;cc;c++)b=b();return b})};a.toJSON=function(b,c,d){b=a.ad(b);return a.a.hc(b,c,d)};d.prototype={constructor:d,save:function(b,c){var d=a.a.A(this.keys, +b);0<=d?this.values[d]=c:(this.keys.push(b),this.values.push(c))},get:function(b){b=a.a.A(this.keys,b);return 0<=b?this.values[b]:n}}})();a.b("toJS",a.ad);a.b("toJSON",a.toJSON);a.Wd=function(b,c,d){function e(c){var e=a.xb(b,d).extend({ma:"always"}),h=e.subscribe(function(a){a&&(h.s(),c(a))});e.notifySubscribers(e.v());return h}return"function"!==typeof Promise||c?e(c.bind(d)):new Promise(e)};a.b("when",a.Wd);(function(){a.w={M:function(b){switch(a.a.R(b)){case "option":return!0===b.__ko__hasDomDataOptionValue__? +a.a.g.get(b,a.c.options.$b):7>=a.a.W?b.getAttributeNode("value")&&b.getAttributeNode("value").specified?b.value:b.text:b.value;case "select":return 0<=b.selectedIndex?a.w.M(b.options[b.selectedIndex]):n;default:return b.value}},cb:function(b,c,d){switch(a.a.R(b)){case "option":"string"===typeof c?(a.a.g.set(b,a.c.options.$b,n),"__ko__hasDomDataOptionValue__"in b&&delete b.__ko__hasDomDataOptionValue__,b.value=c):(a.a.g.set(b,a.c.options.$b,c),b.__ko__hasDomDataOptionValue__=!0,b.value="number"=== +typeof c?c:"");break;case "select":if(""===c||null===c)c=n;for(var e=-1,f=0,g=b.options.length,h;f=h){c.push(p&&q.length?{key:p,value:q.join("")}:{unknown:p||q.join("")});p=h=0;q=[];continue}}else if(58===u){if(!h&&!p&&1===q.length){p=q.pop();continue}}else if(47===u&&1arguments.length){if(b=w.body,!b)throw Error("ko.applyBindings: could not find document.body; has the document been loaded?"); +}else if(!b||1!==b.nodeType&&8!==b.nodeType)throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");k(q(a,c),b)};a.Dc=function(b){return!b||1!==b.nodeType&&8!==b.nodeType?n:a.Td(b)};a.Ec=function(b){return(b=a.Dc(b))?b.$data:n};a.b("bindingHandlers",a.c);a.b("bindingEvent",a.i);a.b("bindingEvent.subscribe",a.i.subscribe);a.b("bindingEvent.startPossiblyAsyncContentBinding",a.i.Cb);a.b("applyBindings",a.vc);a.b("applyBindingsToDescendants",a.Oa); +a.b("applyBindingAccessorsToNode",a.ib);a.b("applyBindingsToNode",a.ld);a.b("contextFor",a.Dc);a.b("dataFor",a.Ec)})();(function(b){function c(c,e){var k=Object.prototype.hasOwnProperty.call(f,c)?f[c]:b,l;k?k.subscribe(e):(k=f[c]=new a.T,k.subscribe(e),d(c,function(b,d){var e=!(!d||!d.synchronous);g[c]={definition:b,Gd:e};delete f[c];l||e?k.notifySubscribers(b):a.na.zb(function(){k.notifySubscribers(b)})}),l=!0)}function d(a,b){e("getConfig",[a],function(c){c?e("loadComponent",[a,c],function(a){b(a, +c)}):b(null,null)})}function e(c,d,f,l){l||(l=a.j.loaders.slice(0));var g=l.shift();if(g){var q=g[c];if(q){var t=!1;if(q.apply(g,d.concat(function(a){t?f(null):null!==a?f(a):e(c,d,f,l)}))!==b&&(t=!0,!g.suppressLoaderExceptions))throw Error("Component loaders must supply values by invoking the callback, not by returning values synchronously.");}else e(c,d,f,l)}else f(null)}var f={},g={};a.j={get:function(d,e){var f=Object.prototype.hasOwnProperty.call(g,d)?g[d]:b;f?f.Gd?a.u.G(function(){e(f.definition)}): +a.na.zb(function(){e(f.definition)}):c(d,e)},Bc:function(a){delete g[a]},oc:e};a.j.loaders=[];a.b("components",a.j);a.b("components.get",a.j.get);a.b("components.clearCachedDefinition",a.j.Bc)})();(function(){function b(b,c,d,e){function g(){0===--B&&e(h)}var h={},B=2,u=d.template;d=d.viewModel;u?f(c,u,function(c){a.j.oc("loadTemplate",[b,c],function(a){h.template=a;g()})}):g();d?f(c,d,function(c){a.j.oc("loadViewModel",[b,c],function(a){h[m]=a;g()})}):g()}function c(a,b,d){if("function"===typeof b)d(function(a){return new b(a)}); +else if("function"===typeof b[m])d(b[m]);else if("instance"in b){var e=b.instance;d(function(){return e})}else"viewModel"in b?c(a,b.viewModel,d):a("Unknown viewModel value: "+b)}function d(b){switch(a.a.R(b)){case "script":return a.a.ua(b.text);case "textarea":return a.a.ua(b.value);case "template":if(e(b.content))return a.a.Ca(b.content.childNodes)}return a.a.Ca(b.childNodes)}function e(a){return A.DocumentFragment?a instanceof DocumentFragment:a&&11===a.nodeType}function f(a,b,c){"string"===typeof b.require? +T||A.require?(T||A.require)([b.require],function(a){a&&"object"===typeof a&&a.Xd&&a["default"]&&(a=a["default"]);c(a)}):a("Uses require, but no AMD loader is present"):c(b)}function g(a){return function(b){throw Error("Component '"+a+"': "+b);}}var h={};a.j.register=function(b,c){if(!c)throw Error("Invalid configuration for "+b);if(a.j.tb(b))throw Error("Component "+b+" is already registered");h[b]=c};a.j.tb=function(a){return Object.prototype.hasOwnProperty.call(h,a)};a.j.unregister=function(b){delete h[b]; +a.j.Bc(b)};a.j.Fc={getConfig:function(b,c){c(a.j.tb(b)?h[b]:null)},loadComponent:function(a,c,d){var e=g(a);f(e,c,function(c){b(a,e,c,d)})},loadTemplate:function(b,c,f){b=g(b);if("string"===typeof c)f(a.a.ua(c));else if(c instanceof Array)f(c);else if(e(c))f(a.a.la(c.childNodes));else if(c.element)if(c=c.element,A.HTMLElement?c instanceof HTMLElement:c&&c.tagName&&1===c.nodeType)f(d(c));else if("string"===typeof c){var h=w.getElementById(c);h?f(d(h)):b("Cannot find element with ID "+c)}else b("Unknown element type: "+ +c);else b("Unknown template value: "+c)},loadViewModel:function(a,b,d){c(g(a),b,d)}};var m="createViewModel";a.b("components.register",a.j.register);a.b("components.isRegistered",a.j.tb);a.b("components.unregister",a.j.unregister);a.b("components.defaultLoader",a.j.Fc);a.j.loaders.push(a.j.Fc);a.j.dd=h})();(function(){function b(b,e){var f=b.getAttribute("params");if(f){var f=c.parseBindingsString(f,e,b,{valueAccessors:!0,bindingParams:!0}),f=a.a.Ga(f,function(c){return a.o(c,null,{l:b})}),g=a.a.Ga(f, +function(c){var e=c.v();return c.ja()?a.o({read:function(){return a.a.f(c())},write:a.Za(e)&&function(a){c()(a)},l:b}):e});Object.prototype.hasOwnProperty.call(g,"$raw")||(g.$raw=f);return g}return{$raw:{}}}a.j.getComponentNameForNode=function(b){var c=a.a.R(b);if(a.j.tb(c)&&(-1!=c.indexOf("-")||"[object HTMLUnknownElement]"==""+b||8>=a.a.W&&b.tagName===c))return c};a.j.tc=function(c,e,f,g){if(1===e.nodeType){var h=a.j.getComponentNameForNode(e);if(h){c=c||{};if(c.component)throw Error('Cannot use the "component" binding on a custom element matching a component'); +var m={name:h,params:b(e,f)};c.component=g?function(){return m}:m}}return c};var c=new a.ga;9>a.a.W&&(a.j.register=function(a){return function(b){return a.apply(this,arguments)}}(a.j.register),w.createDocumentFragment=function(b){return function(){var c=b(),f=a.j.dd,g;for(g in f);return c}}(w.createDocumentFragment))})();(function(){function b(b,c,d){c=c.template;if(!c)throw Error("Component '"+b+"' has no template");b=a.a.Ca(c);a.h.va(d,b)}function c(a,b,c){var d=a.createViewModel;return d?d.call(a, +b,c):b}var d=0;a.c.component={init:function(e,f,g,h,m){function k(){var a=l&&l.dispose;"function"===typeof a&&a.call(l);q&&q.s();p=l=q=null}var l,p,q,t=a.a.la(a.h.childNodes(e));a.h.Ea(e);a.a.K.za(e,k);a.o(function(){var g=a.a.f(f()),h,u;"string"===typeof g?h=g:(h=a.a.f(g.name),u=a.a.f(g.params));if(!h)throw Error("No component name specified");var n=a.i.Cb(e,m),z=p=++d;a.j.get(h,function(d){if(p===z){k();if(!d)throw Error("Unknown component '"+h+"'");b(h,d,e);var f=c(d,u,{element:e,templateNodes:t}); +d=n.createChildContext(f,{extend:function(a){a.$component=f;a.$componentTemplateNodes=t}});f&&f.koDescendantsComplete&&(q=a.i.subscribe(e,a.i.pa,f.koDescendantsComplete,f));l=f;a.Oa(d,e)}})},null,{l:e});return{controlsDescendantBindings:!0}}};a.h.ea.component=!0})();var V={"class":"className","for":"htmlFor"};a.c.attr={update:function(b,c){var d=a.a.f(c())||{};a.a.P(d,function(c,d){d=a.a.f(d);var g=c.indexOf(":"),g="lookupNamespaceURI"in b&&0=a.a.W&&c in V?(c=V[c],h?b.removeAttribute(c):b[c]=d):h||(g?b.setAttributeNS(g,c,d):b.setAttribute(c,d));"name"===c&&a.a.Yc(b,h?"":d)})}};(function(){a.c.checked={after:["value","attr"],init:function(b,c,d){function e(){var e=b.checked,f=g();if(!a.S.Ya()&&(e||!m&&!a.S.qa())){var k=a.u.G(c);if(l){var q=p?k.v():k,z=t;t=f;z!==f?e&&(a.a.Na(q,f,!0),a.a.Na(q,z,!1)):a.a.Na(q,f,e);p&&a.Za(k)&&k(q)}else h&&(f===n?f=e:e||(f=n)),a.m.eb(k, +d,"checked",f,!0)}}function f(){var d=a.a.f(c()),e=g();l?(b.checked=0<=a.a.A(d,e),t=e):b.checked=h&&e===n?!!d:g()===d}var g=a.xb(function(){if(d.has("checkedValue"))return a.a.f(d.get("checkedValue"));if(q)return d.has("value")?a.a.f(d.get("value")):b.value}),h="checkbox"==b.type,m="radio"==b.type;if(h||m){var k=c(),l=h&&a.a.f(k)instanceof Array,p=!(l&&k.push&&k.splice),q=m||l,t=l?g():n;m&&!b.name&&a.c.uniqueName.init(b,function(){return!0});a.o(e,null,{l:b});a.a.B(b,"click",e);a.o(f,null,{l:b}); +k=n}}};a.m.wa.checked=!0;a.c.checkedValue={update:function(b,c){b.value=a.a.f(c())}}})();a.c["class"]={update:function(b,c){var d=a.a.Db(a.a.f(c()));a.a.Eb(b,b.__ko__cssValue,!1);b.__ko__cssValue=d;a.a.Eb(b,d,!0)}};a.c.css={update:function(b,c){var d=a.a.f(c());null!==d&&"object"==typeof d?a.a.P(d,function(c,d){d=a.a.f(d);a.a.Eb(b,c,d)}):a.c["class"].update(b,c)}};a.c.enable={update:function(b,c){var d=a.a.f(c());d&&b.disabled?b.removeAttribute("disabled"):d||b.disabled||(b.disabled=!0)}};a.c.disable= +{update:function(b,c){a.c.enable.update(b,function(){return!a.a.f(c())})}};a.c.event={init:function(b,c,d,e,f){var g=c()||{};a.a.P(g,function(g){"string"==typeof g&&a.a.B(b,g,function(b){var k,l=c()[g];if(l){try{var p=a.a.la(arguments);e=f.$data;p.unshift(e);k=l.apply(e,p)}finally{!0!==k&&(b.preventDefault?b.preventDefault():b.returnValue=!1)}!1===d.get(g+"Bubble")&&(b.cancelBubble=!0,b.stopPropagation&&b.stopPropagation())}})})}};a.c.foreach={Rc:function(b){return function(){var c=b(),d=a.a.bc(c); +if(!d||"number"==typeof d.length)return{foreach:c,templateEngine:a.ba.Ma};a.a.f(c);return{foreach:d.data,as:d.as,noChildContext:d.noChildContext,includeDestroyed:d.includeDestroyed,afterAdd:d.afterAdd,beforeRemove:d.beforeRemove,afterRender:d.afterRender,beforeMove:d.beforeMove,afterMove:d.afterMove,templateEngine:a.ba.Ma}}},init:function(b,c){return a.c.template.init(b,a.c.foreach.Rc(c))},update:function(b,c,d,e,f){return a.c.template.update(b,a.c.foreach.Rc(c),d,e,f)}};a.m.Ra.foreach=!1;a.h.ea.foreach= +!0;a.c.hasfocus={init:function(b,c,d){function e(e){b.__ko_hasfocusUpdating=!0;var f=b.ownerDocument;if("activeElement"in f){var g;try{g=f.activeElement}catch(l){g=f.body}e=g===b}f=c();a.m.eb(f,d,"hasfocus",e,!0);b.__ko_hasfocusLastValue=e;b.__ko_hasfocusUpdating=!1}var f=e.bind(null,!0),g=e.bind(null,!1);a.a.B(b,"focus",f);a.a.B(b,"focusin",f);a.a.B(b,"blur",g);a.a.B(b,"focusout",g);b.__ko_hasfocusLastValue=!1},update:function(b,c){var d=!!a.a.f(c());b.__ko_hasfocusUpdating||b.__ko_hasfocusLastValue=== +d||(d?b.focus():b.blur(),!d&&b.__ko_hasfocusLastValue&&b.ownerDocument.body.focus(),a.u.G(a.a.Fb,null,[b,d?"focusin":"focusout"]))}};a.m.wa.hasfocus=!0;a.c.hasFocus=a.c.hasfocus;a.m.wa.hasFocus="hasfocus";a.c.html={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.fc(b,c())}};(function(){function b(b,d,e){a.c[b]={init:function(b,c,h,m,k){var l,p,q={},t,x,n;if(d){m=h.get("as");var u=h.get("noChildContext");n=!(m&&u);q={as:m,noChildContext:u,exportDependencies:n}}x=(t= +"render"==h.get("completeOn"))||h.has(a.i.pa);a.o(function(){var h=a.a.f(c()),m=!e!==!h,u=!p,r;if(n||m!==l){x&&(k=a.i.Cb(b,k));if(m){if(!d||n)q.dataDependency=a.S.o();r=d?k.createChildContext("function"==typeof h?h:c,q):a.S.qa()?k.extend(null,q):k}u&&a.S.qa()&&(p=a.a.Ca(a.h.childNodes(b),!0));m?(u||a.h.va(b,a.a.Ca(p)),a.Oa(r,b)):(a.h.Ea(b),t||a.i.ma(b,a.i.H));l=m}},null,{l:b});return{controlsDescendantBindings:!0}}};a.m.Ra[b]=!1;a.h.ea[b]=!0}b("if");b("ifnot",!1,!0);b("with",!0)})();a.c.let={init:function(b, +c,d,e,f){c=f.extend(c);a.Oa(c,b);return{controlsDescendantBindings:!0}}};a.h.ea.let=!0;var Q={};a.c.options={init:function(b){if("select"!==a.a.R(b))throw Error("options binding applies only to SELECT elements");for(;0g)var m=a.a.g.Z(),k=a.a.g.Z(),l=function(b){var c=this.activeElement;(c=c&&a.a.g.get(c,k))&&c(b)},p=function(b,c){var d=b.ownerDocument;a.a.g.get(d,m)||(a.a.g.set(d,m,!0),a.a.B(d,"selectionchange",l));a.a.g.set(b,k,c)};a.c.textInput={init:function(b,c,k){function l(c,d){a.a.B(b,c,d)}function m(){var d=a.a.f(c());if(null===d||d===n)d="";L!==n&&d===L?a.a.setTimeout(m,4):b.value!==d&&(y=!0,b.value=d,y=!1,v=b.value)}function r(){w||(L=b.value,w=a.a.setTimeout(z, +4))}function z(){clearTimeout(w);L=w=n;var d=b.value;v!==d&&(v=d,a.m.eb(c(),k,"textInput",d))}var v=b.value,w,L,A=9==a.a.W?r:z,y=!1;g&&l("keypress",z);11>g&&l("propertychange",function(a){y||"value"!==a.propertyName||A(a)});8==g&&(l("keyup",z),l("keydown",z));p&&(p(b,A),l("dragend",r));(!g||9<=g)&&l("input",A);5>e&&"textarea"===a.a.R(b)?(l("keydown",r),l("paste",r),l("cut",r)):11>d?l("keydown",r):4>f?(l("DOMAutoComplete",z),l("dragdrop",z),l("drop",z)):h&&"number"===b.type&&l("keydown",r);l("change", +z);l("blur",z);a.o(m,null,{l:b})}};a.m.wa.textInput=!0;a.c.textinput={preprocess:function(a,b,c){c("textInput",a)}}})();a.c.uniqueName={init:function(b,c){if(c()){var d="ko_unique_"+ ++a.c.uniqueName.rd;a.a.Yc(b,d)}}};a.c.uniqueName.rd=0;a.c.using={init:function(b,c,d,e,f){var g;d.has("as")&&(g={as:d.get("as"),noChildContext:d.get("noChildContext")});c=f.createChildContext(c,g);a.Oa(c,b);return{controlsDescendantBindings:!0}}};a.h.ea.using=!0;a.c.value={init:function(b,c,d){var e=a.a.R(b),f="input"== +e;if(!f||"checkbox"!=b.type&&"radio"!=b.type){var g=[],h=d.get("valueUpdate"),m=!1,k=null;h&&("string"==typeof h?g=[h]:g=a.a.wc(h),a.a.Pa(g,"change"));var l=function(){k=null;m=!1;var e=c(),f=a.w.M(b);a.m.eb(e,d,"value",f)};!a.a.W||!f||"text"!=b.type||"off"==b.autocomplete||b.form&&"off"==b.form.autocomplete||-1!=a.a.A(g,"propertychange")||(a.a.B(b,"propertychange",function(){m=!0}),a.a.B(b,"focus",function(){m=!1}),a.a.B(b,"blur",function(){m&&l()}));a.a.D(g,function(c){var d=l;a.a.Ud(c,"after")&& +(d=function(){k=a.w.M(b);a.a.setTimeout(l,0)},c=c.substring(5));a.a.B(b,c,d)});var p;p=f&&"file"==b.type?function(){var d=a.a.f(c());null===d||d===n||""===d?b.value="":a.u.G(l)}:function(){var f=a.a.f(c()),g=a.w.M(b);if(null!==k&&f===k)a.a.setTimeout(p,0);else if(f!==g||g===n)"select"===e?(g=d.get("valueAllowUnset"),a.w.cb(b,f,g),g||f===a.w.M(b)||a.u.G(l)):a.w.cb(b,f)};if("select"===e){var q;a.i.subscribe(b,a.i.H,function(){q?d.get("valueAllowUnset")?p():l():(a.a.B(b,"change",l),q=a.o(p,null,{l:b}))}, +null,{notifyImmediately:!0})}else a.a.B(b,"change",l),a.o(p,null,{l:b})}else a.ib(b,{checkedValue:c})},update:function(){}};a.m.wa.value=!0;a.c.visible={update:function(b,c){var d=a.a.f(c()),e="none"!=b.style.display;d&&!e?b.style.display="":!d&&e&&(b.style.display="none")}};a.c.hidden={update:function(b,c){a.c.visible.update(b,function(){return!a.a.f(c())})}};(function(b){a.c[b]={init:function(c,d,e,f,g){return a.c.event.init.call(this,c,function(){var a={};a[b]=d();return a},e,f,g)}}})("click"); +a.ca=function(){};a.ca.prototype.renderTemplateSource=function(){throw Error("Override renderTemplateSource");};a.ca.prototype.createJavaScriptEvaluatorBlock=function(){throw Error("Override createJavaScriptEvaluatorBlock");};a.ca.prototype.makeTemplateSource=function(b,c){if("string"==typeof b){c=c||w;var d=c.getElementById(b);if(!d)throw Error("Cannot find template with ID "+b);return new a.C.F(d)}if(1==b.nodeType||8==b.nodeType)return new a.C.ia(b);throw Error("Unknown template type: "+b);};a.ca.prototype.renderTemplate= +function(a,c,d,e){a=this.makeTemplateSource(a,e);return this.renderTemplateSource(a,c,d,e)};a.ca.prototype.isTemplateRewritten=function(a,c){return!1===this.allowTemplateRewriting?!0:this.makeTemplateSource(a,c).data("isRewritten")};a.ca.prototype.rewriteTemplate=function(a,c,d){a=this.makeTemplateSource(a,d);c=c(a.text());a.text(c);a.data("isRewritten",!0)};a.b("templateEngine",a.ca);a.kc=function(){function b(b,c,d,h){b=a.m.ac(b);for(var m=a.m.Ra,k=0;k]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi, +d=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{xd:function(b,c,d){c.isTemplateRewritten(b,d)||c.rewriteTemplate(b,function(b){return a.kc.Ld(b,c)},d)},Ld:function(a,f){return a.replace(c,function(a,c,d,e,l){return b(l,c,d,f)}).replace(d,function(a,c){return b(c,"\x3c!-- ko --\x3e","#comment",f)})},md:function(b,c){return a.aa.Xb(function(d,h){var m=d.nextSibling;m&&m.nodeName.toLowerCase()===c&&a.ib(m,b,h)})}}}();a.b("__tr_ambtns",a.kc.md);(function(){a.C={};a.C.F=function(b){if(this.F=b){var c= +a.a.R(b);this.ab="script"===c?1:"textarea"===c?2:"template"==c&&b.content&&11===b.content.nodeType?3:4}};a.C.F.prototype.text=function(){var b=1===this.ab?"text":2===this.ab?"value":"innerHTML";if(0==arguments.length)return this.F[b];var c=arguments[0];"innerHTML"===b?a.a.fc(this.F,c):this.F[b]=c};var b=a.a.g.Z()+"_";a.C.F.prototype.data=function(c){if(1===arguments.length)return a.a.g.get(this.F,b+c);a.a.g.set(this.F,b+c,arguments[1])};var c=a.a.g.Z();a.C.F.prototype.nodes=function(){var b=this.F; +if(0==arguments.length){var e=a.a.g.get(b,c)||{},f=e.lb||(3===this.ab?b.content:4===this.ab?b:n);if(!f||e.jd){var g=this.text();g&&g!==e.bb&&(f=a.a.Md(g,b.ownerDocument),a.a.g.set(b,c,{lb:f,bb:g,jd:!0}))}return f}e=arguments[0];this.ab!==n&&this.text("");a.a.g.set(b,c,{lb:e})};a.C.ia=function(a){this.F=a};a.C.ia.prototype=new a.C.F;a.C.ia.prototype.constructor=a.C.ia;a.C.ia.prototype.text=function(){if(0==arguments.length){var b=a.a.g.get(this.F,c)||{};b.bb===n&&b.lb&&(b.bb=b.lb.innerHTML);return b.bb}a.a.g.set(this.F, +c,{bb:arguments[0]})};a.b("templateSources",a.C);a.b("templateSources.domElement",a.C.F);a.b("templateSources.anonymousTemplate",a.C.ia)})();(function(){function b(b,c,d){var e;for(c=a.h.nextSibling(c);b&&(e=b)!==c;)b=a.h.nextSibling(e),d(e,b)}function c(c,d){if(c.length){var e=c[0],f=c[c.length-1],g=e.parentNode,h=a.ga.instance,m=h.preprocessNode;if(m){b(e,f,function(a,b){var c=a.previousSibling,d=m.call(h,a);d&&(a===e&&(e=d[0]||b),a===f&&(f=d[d.length-1]||c))});c.length=0;if(!e)return;e===f?c.push(e): +(c.push(e,f),a.a.Ua(c,g))}b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.vc(d,b)});b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.aa.cd(b,[d])});a.a.Ua(c,g)}}function d(a){return a.nodeType?a:0a.a.W?0:b.nodes)?b.nodes():null)return a.a.la(c.cloneNode(!0).childNodes);b=b.text();return a.a.ua(b,e)};a.ba.Ma=new a.ba;a.gc(a.ba.Ma);a.b("nativeTemplateEngine",a.ba);(function(){a.$a=function(){var a=this.Hd=function(){if(!v||!v.tmpl)return 0;try{if(0<=v.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}(); +this.renderTemplateSource=function(b,e,f,g){g=g||w;f=f||{};if(2>a)throw Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");var h=b.data("precompiled");h||(h=b.text()||"",h=v.template(null,"{{ko_with $item.koBindingContext}}"+h+"{{/ko_with}}"),b.data("precompiled",h));b=[e.$data];e=v.extend({koBindingContext:e},f.templateOptions);e=v.tmpl(h,b,e);e.appendTo(g.createElement("div"));v.fragments={};return e};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+ +a+" })()) }}"};this.addTemplate=function(a,b){w.write(" @@ -54,7 +55,7 @@ - + {{ template "js" .Data }} diff --git a/templates/index/index.html b/templates/index/index.html index daa501f6..7699c779 100644 --- a/templates/index/index.html +++ b/templates/index/index.html @@ -1,11 +1,13 @@ {{ define "page" }} -
+
{{ template "networkOverview" . }} +
{{ template "recentEpochs" . }}
+
{{ template "recentBlocks" . }}
@@ -16,9 +18,16 @@
+
+
+ +
+
{{ end }} {{ define "js" }} + + {{ end }} {{ define "css" }} @@ -26,8 +35,9 @@ #recent-epochs, #recent-blocks, #recent-slots { margin-bottom: 0; } - .startpage-panel { - margin-bottom: 30px; + #update_timer { + display: inline-block; + min-width: 120px; } .startpage-panel .card-header { padding-top: 2px; diff --git a/templates/index/networkOverview.html b/templates/index/networkOverview.html index 2791cfc0..a3610ed3 100644 --- a/templates/index/networkOverview.html +++ b/templates/index/networkOverview.html @@ -1,12 +1,15 @@ {{ define "networkOverview" }}
-
-
{{- /**/ -}} - {{ if gt .CurrentScheduledCount 0 }} - {{ .CurrentScheduledCount }} / 32 slots left in epoch {{ .CurrentEpoch }} - {{ else }} - {{ .CurrentEpoch }} epoch complete - {{ end }} +
+
+ {{- /**/ -}} + + {{ if gt .CurrentScheduledCount 0 -}} + {{ .CurrentScheduledCount }} / 32 slots left in epoch {{ .CurrentEpoch }} + {{- else -}} + epoch {{ .CurrentEpoch }} complete + {{- end }} +
@@ -17,14 +20,14 @@
Epoch
- {{ .CurrentEpoch }} / - {{ .CurrentFinalizedEpoch }} + {{ .CurrentEpoch }} / + {{ .CurrentFinalizedEpoch }}
Current Slot
- {{ .CurrentSlot }} + {{ .CurrentSlot }}
@@ -34,18 +37,18 @@
Active Validators
- {{ .ActiveValidatorCount }} + {{ .ActiveValidatorCount }}
- {{ if eq .EnteringValidatorCount 0 }} -
Pending Validators
- {{ else }} -
Pending Validators
- {{ end }} +
+ + Pending Validators + +
- {{ .EnteringValidatorCount }} - / {{ .ExitingValidatorCount }} + {{ .EnteringValidatorCount }} + / {{ .ExitingValidatorCount }}
@@ -55,13 +58,13 @@
Staked Ether
- {{ formatEthAddCommasFromGwei .TotalEligibleEther }} ETH + {{ formatEthAddCommasFromGwei .TotalEligibleEther }} ETH
Average Balance
- {{ formatEthFromGwei .AverageValidatorBalance }} + {{ formatEthFromGwei .AverageValidatorBalance }}
@@ -71,32 +74,30 @@
Network Name:
-
- {{ .NetworkName }} -
+
{{ .NetworkName }}
Genesis Time:
- {{ .GenesisTime }} - ({{ formatRecentTimeShort .GenesisTime }}) - + {{ .GenesisTime }} + ({{ formatRecentTimeShort .GenesisTime }}) +
Genesis Validators Root:
- 0x{{ printf "%x" .GenesisValidatorsRoot }} - + 0x{{ printf "%x" .GenesisValidatorsRoot }} +
Genesis Fork Version:
- 0x{{ printf "%x" .GenesisForkVersion }} - + 0x{{ printf "%x" .GenesisForkVersion }} +
diff --git a/templates/index/recentBlocks.html b/templates/index/recentBlocks.html index 347a8df5..ec7ecf17 100644 --- a/templates/index/recentBlocks.html +++ b/templates/index/recentBlocks.html @@ -19,10 +19,48 @@
+ {{ html "" }} + + + +
+
+ + +
+
+ +
+
+ +
+
+
-
+ + + Genesis + Missed + Proposed + Missed (Orphaned) + Unknown + + + + + + + {{ html "" }} + {{ html "" }} + + + no blocks found + + + {{ html "" }} + {{ if gt .RecentBlockCount 0 }} {{ range $i, $block := .RecentBlocks }} - + {{ formatAddCommas $block.Epoch }} {{ if eq .Status 2 }} {{ formatAddCommas $block.Slot }} @@ -47,16 +85,14 @@
+ {{ else }} + no blocks found - - {{ end }} + {{ end }} +
diff --git a/templates/index/recentEpochs.html b/templates/index/recentEpochs.html index 2eca1754..ad4863df 100644 --- a/templates/index/recentEpochs.html +++ b/templates/index/recentEpochs.html @@ -18,29 +18,38 @@
+ + {{ html "" }} - - - + + + Yes No - +
- +
-
+
+ {{ html "" }} + {{ html "" }} + + + no epochs found + + + {{ html "" }} {{ if gt .RecentEpochCount 0 }} {{ range $i, $epoch := .RecentEpochs }} - + {{ formatAddCommas $epoch.Epoch }} {{ formatRecentTimeShort $epoch.Ts }} @@ -62,7 +71,7 @@
+ no epochs found diff --git a/templates/index/recentSlots.html b/templates/index/recentSlots.html index 3e1713a1..201d9e63 100644 --- a/templates/index/recentSlots.html +++ b/templates/index/recentSlots.html @@ -19,8 +19,51 @@
+ {{ html "" }} + + + {{ html "" }} +
+ {{ html "" }} +
+ {{ html "" }} +
+ +
+
+ {{ html "" }} + + + +
+
+ + + Genesis + Missed + Proposed + Missed (Orphaned) + Unknown + + + + + + + {{ html "" }} + {{ html "" }} + + + +
+ {{ template "timeline_svg" }} +
+ + + + {{ html "" }} + {{ if gt .RecentSlotCount 0 }} {{ $treeWidth := .ForkTreeWidth }} {{ range $i, $slot := .RecentSlots }} @@ -61,9 +104,7 @@
@@ -73,8 +114,8 @@
Date: Fri, 8 Sep 2023 02:19:54 +0200 Subject: [PATCH 03/10] reduce log level for page call --- handlers/clients.go | 2 +- handlers/epoch.go | 2 +- handlers/epochs.go | 2 +- handlers/index.go | 2 +- handlers/slot.go | 2 +- handlers/slots.go | 2 +- handlers/slots_filtered.go | 2 +- handlers/validator.go | 2 +- handlers/validator_slots.go | 2 +- handlers/validators.go | 2 +- services/frontendcache.go | 4 ++-- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/handlers/clients.go b/handlers/clients.go index 29abe634..2017c8bf 100644 --- a/handlers/clients.go +++ b/handlers/clients.go @@ -41,7 +41,7 @@ func getClientsPageData() *models.ClientsPageData { } func buildClientsPageData() (*models.ClientsPageData, time.Duration) { - logrus.Printf("clients page called") + logrus.Debugf("clients page called") pageData := &models.ClientsPageData{ Clients: []*models.ClientsPageDataClient{}, } diff --git a/handlers/epoch.go b/handlers/epoch.go index c02232a2..c734771f 100644 --- a/handlers/epoch.go +++ b/handlers/epoch.go @@ -66,7 +66,7 @@ func getEpochPageData(epoch uint64) *models.EpochPageData { } func buildEpochPageData(epoch uint64) (*models.EpochPageData, time.Duration) { - logrus.Printf("epoch page called: %v", epoch) + logrus.Debugf("epoch page called: %v", epoch) now := time.Now() currentSlot := utils.TimeToSlot(uint64(now.Unix())) diff --git a/handlers/epochs.go b/handlers/epochs.go index 672842b8..dc155573 100644 --- a/handlers/epochs.go +++ b/handlers/epochs.go @@ -54,7 +54,7 @@ func getEpochsPageData(firstEpoch uint64, pageSize uint64) *models.EpochsPageDat } func buildEpochsPageData(firstEpoch uint64, pageSize uint64) (*models.EpochsPageData, time.Duration) { - logrus.Printf("epochs page called: %v:%v", firstEpoch, pageSize) + logrus.Debugf("epochs page called: %v:%v", firstEpoch, pageSize) pageData := &models.EpochsPageData{} now := time.Now() diff --git a/handlers/index.go b/handlers/index.go index 9170e055..daec9f02 100644 --- a/handlers/index.go +++ b/handlers/index.go @@ -64,7 +64,7 @@ func getIndexPageData() *models.IndexPageData { } func buildIndexPageData() (*models.IndexPageData, time.Duration) { - logrus.Printf("index page called") + logrus.Debugf("index page called") recentEpochCount := 7 recentBlockCount := 7 diff --git a/handlers/slot.go b/handlers/slot.go index 582ea328..d22b87e3 100644 --- a/handlers/slot.go +++ b/handlers/slot.go @@ -198,7 +198,7 @@ func buildSlotPageData(blockSlot int64, blockRoot []byte, loadDuties bool) (*mod } else { return nil, -1 } - logrus.Printf("slot page called: %v", slot) + logrus.Debugf("slot page called: %v", slot) pageData := &models.SlotPageData{ Slot: slot, diff --git a/handlers/slots.go b/handlers/slots.go index 4e2fa197..73667dc2 100644 --- a/handlers/slots.go +++ b/handlers/slots.go @@ -55,7 +55,7 @@ func getSlotsPageData(firstSlot uint64, pageSize uint64) *models.SlotsPageData { } func buildSlotsPageData(firstSlot uint64, pageSize uint64) (*models.SlotsPageData, time.Duration) { - logrus.Printf("slots page called: %v:%v", firstSlot, pageSize) + logrus.Debugf("slots page called: %v:%v", firstSlot, pageSize) pageData := &models.SlotsPageData{} now := time.Now() diff --git a/handlers/slots_filtered.go b/handlers/slots_filtered.go index 0bb54127..556eed55 100644 --- a/handlers/slots_filtered.go +++ b/handlers/slots_filtered.go @@ -104,7 +104,7 @@ func buildFilteredSlotsPageData(pageIdx uint64, pageSize uint64, graffiti string FilterWithOrphaned: withOrphaned, FilterWithMissing: withMissing, } - logrus.Printf("slots_filtered page called: %v:%v [%v]", pageIdx, pageSize, graffiti) + logrus.Debugf("slots_filtered page called: %v:%v [%v]", pageIdx, pageSize, graffiti) if pageIdx == 0 { pageData.IsDefaultPage = true } diff --git a/handlers/validator.go b/handlers/validator.go index a1ca42ed..44edd66b 100644 --- a/handlers/validator.go +++ b/handlers/validator.go @@ -86,7 +86,7 @@ func getValidatorPageData(validatorIndex uint64) *models.ValidatorPageData { } func buildValidatorPageData(validatorIndex uint64) (*models.ValidatorPageData, time.Duration) { - logrus.Printf("validator page called: %v", validatorIndex) + logrus.Debugf("validator page called: %v", validatorIndex) validatorSetRsp := services.GlobalBeaconService.GetCachedValidatorSet() validator := validatorSetRsp.Data[validatorIndex] diff --git a/handlers/validator_slots.go b/handlers/validator_slots.go index c01cb042..f25df439 100644 --- a/handlers/validator_slots.go +++ b/handlers/validator_slots.go @@ -67,7 +67,7 @@ func buildValidatorSlotsPageData(validator uint64, pageIdx uint64, pageSize uint Index: validator, Name: services.GlobalBeaconService.GetValidatorName(validator), } - logrus.Printf("validator slots page called (%v): %v:%v", validator, pageIdx, pageSize) + logrus.Debugf("validator slots page called (%v): %v:%v", validator, pageIdx, pageSize) if pageIdx == 0 { pageData.IsDefaultPage = true } diff --git a/handlers/validators.go b/handlers/validators.go index f7d458c3..b5fc5054 100644 --- a/handlers/validators.go +++ b/handlers/validators.go @@ -81,7 +81,7 @@ func getValidatorsPageData(firstValIdx uint64, pageSize uint64, sortOrder string } func buildValidatorsPageData(firstValIdx uint64, pageSize uint64, sortOrder string, filterPubKey string, filterIndex string, filterName string, filterStatus string) (*models.ValidatorsPageData, time.Duration) { - logrus.Printf("validators page called: %v:%v:%v:%v:%v:%v:%v", firstValIdx, pageSize, sortOrder, filterPubKey, filterIndex, filterName, filterStatus) + logrus.Debugf("validators page called: %v:%v:%v:%v:%v:%v:%v", firstValIdx, pageSize, sortOrder, filterPubKey, filterIndex, filterName, filterStatus) pageData := &models.ValidatorsPageData{} cacheTime := 10 * time.Minute diff --git a/services/frontendcache.go b/services/frontendcache.go index ffcc6347..e3cb28a9 100644 --- a/services/frontendcache.go +++ b/services/frontendcache.go @@ -58,7 +58,7 @@ func (fc *FrontendCacheService) ProcessCachedPage(pageKey string, caching bool, processingPage := fc.processingDict[pageKey] if processingPage != nil { fc.processingMutex.Unlock() - logrus.Printf("page already processing: %v", pageKey) + logrus.Debugf("page already processing: %v", pageKey) processingPage.modelMutex.RLock() defer processingPage.modelMutex.RUnlock() @@ -88,7 +88,7 @@ func (fc *FrontendCacheService) completePageLoad(pageKey string, processingPage func (fc *FrontendCacheService) processCachedPageData(pageKey string, caching bool, pageData interface{}, buildFn func(pageCall *FrontendCacheProcessingPage) interface{}, pageCall *FrontendCacheProcessingPage) interface{} { // check cache if !utils.Config.Frontend.Debug && caching && fc.GetFrontendCache(pageKey, pageData) == nil { - logrus.Printf("page served from cache: %v", pageKey) + logrus.Debugf("page served from cache: %v", pageKey) return pageData } From ab80f4dbd891fd952c38adadb2b3dfad9f8f976b Mon Sep 17 00:00:00 2001 From: pk910 Date: Fri, 8 Sep 2023 02:21:08 +0200 Subject: [PATCH 04/10] small fix --- templates/index/recentSlots.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/index/recentSlots.html b/templates/index/recentSlots.html index 201d9e63..8a9bf020 100644 --- a/templates/index/recentSlots.html +++ b/templates/index/recentSlots.html @@ -52,7 +52,7 @@
{{ html "" }} - {{ html "" }} + {{ html "" }} From dd271a5e4434ec2011fe2507a9ed402933cbca22 Mon Sep 17 00:00:00 2001 From: pk910 Date: Fri, 8 Sep 2023 02:28:54 +0200 Subject: [PATCH 05/10] use dynamic timeouts instead of interval in frontpage js --- static/js/page-index.js | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/static/js/page-index.js b/static/js/page-index.js index 4a764657..e3542b88 100644 --- a/static/js/page-index.js +++ b/static/js/page-index.js @@ -1,10 +1,10 @@ (function() { window.addEventListener('DOMContentLoaded', function() { - window.setInterval(refreshLoop, 500); + window.setTimeout(refreshLoop, 500); }); - var refreshInterval = 15; + var refreshInterval = 15000; var lastRefresh = new Date().getTime(); var isRefreshing = false; var viewModel = null; @@ -20,16 +20,22 @@ }; function refreshLoop() { - var refreshTimeout = Math.ceil(refreshInterval - ((new Date().getTime() - lastRefresh) / 1000)); + var refreshTimeout = refreshInterval - ((new Date().getTime() - lastRefresh)); if(refreshTimeout < 0) refreshTimeout = 0; - document.getElementById("update_timer").innerText = "Next update in " + refreshTimeout + "s"; + document.getElementById("update_timer").innerText = "Next update in " + Math.ceil(refreshTimeout / 1000) + "s"; - if(refreshTimeout > 0) - return; + if(refreshTimeout <= 0) { + lastRefresh = new Date().getTime(); + refreshTimeout = refreshInterval; + refresh(); + } - lastRefresh = new Date().getTime(); - refresh(); + if(refreshTimeout > 1000) + refreshTimeout -= Math.floor(refreshTimeout/1000)*1000; + if(refreshTimeout == 0) + refreshTimeout = 1000; + window.setTimeout(refreshLoop, refreshTimeout); } async function refresh() { @@ -59,9 +65,12 @@ if(!viewModel) return createModel(data); for(var prop in data) { - if(typeof viewModel[prop] == "function") - viewModel[prop](data[prop]); - else + if(typeof viewModel[prop] == "function") { + if(viewModel[prop] instanceof ko.observableArray) + viewModel[prop](data[prop]||[]); + else + viewModel[prop](data[prop]); + } else viewModel[prop] = data[prop]; } } From 603fe4f27030ba09662cb7359486942d8cc7085f Mon Sep 17 00:00:00 2001 From: pk910 Date: Fri, 8 Sep 2023 03:45:40 +0200 Subject: [PATCH 06/10] made json model smaller by using shorter keys --- handlers/index.go | 3 -- templates/index/networkOverview.html | 26 ++++++------- templates/index/recentBlocks.html | 8 ++-- templates/index/recentEpochs.html | 10 ++--- templates/index/recentSlots.html | 4 +- types/models/indexPage.go | 57 +++++++++++++--------------- 6 files changed, 51 insertions(+), 57 deletions(-) diff --git a/handlers/index.go b/handlers/index.go index daec9f02..be95c34f 100644 --- a/handlers/index.go +++ b/handlers/index.go @@ -98,7 +98,6 @@ func buildIndexPageData() (*models.IndexPageData, time.Duration) { CurrentEpoch: uint64(currentEpoch), CurrentFinalizedEpoch: finalizedEpoch, CurrentSlot: currentSlot, - CurrentSlotIndex: currentSlotIndex, CurrentScheduledCount: utils.Config.Chain.Config.SlotsPerEpoch - currentSlotIndex, CurrentEpochProgress: float64(100) * float64(currentSlotIndex) / float64(utils.Config.Chain.Config.SlotsPerEpoch), } @@ -205,8 +204,6 @@ func buildIndexPageRecentEpochsData(pageData *models.IndexPageData, currentEpoch Finalized: finalizedEpoch >= int64(epochData.Epoch), EligibleEther: epochData.Eligible, TargetVoted: epochData.VotedTarget, - HeadVoted: epochData.VotedHead, - TotalVoted: epochData.VotedTotal, VoteParticipation: voteParticipation, }) } diff --git a/templates/index/networkOverview.html b/templates/index/networkOverview.html index a3610ed3..97932757 100644 --- a/templates/index/networkOverview.html +++ b/templates/index/networkOverview.html @@ -1,9 +1,9 @@ {{ define "networkOverview" }}
-
-
+
+
{{- /**/ -}} - + {{ if gt .CurrentScheduledCount 0 -}} {{ .CurrentScheduledCount }} / 32 slots left in epoch {{ .CurrentEpoch }} {{- else -}} @@ -20,14 +20,14 @@
Epoch
- {{ .CurrentEpoch }} / - {{ .CurrentFinalizedEpoch }} + {{ .CurrentEpoch }} / + {{ .CurrentFinalizedEpoch }}
Current Slot
- {{ .CurrentSlot }} + {{ .CurrentSlot }}
@@ -37,18 +37,18 @@
Active Validators
- {{ .ActiveValidatorCount }} + {{ .ActiveValidatorCount }}
- + Pending Validators
- {{ .EnteringValidatorCount }} - / {{ .ExitingValidatorCount }} + {{ .EnteringValidatorCount }} + / {{ .ExitingValidatorCount }}
@@ -58,13 +58,13 @@
Staked Ether
- {{ formatEthAddCommasFromGwei .TotalEligibleEther }} ETH + {{ formatEthAddCommasFromGwei .TotalEligibleEther }} ETH
Average Balance
- {{ formatEthFromGwei .AverageValidatorBalance }} + {{ formatEthFromGwei .AverageValidatorBalance }}
@@ -74,7 +74,7 @@
Network Name:
-
{{ .NetworkName }}
+
{{ .NetworkName }}
Genesis Time:
diff --git a/templates/index/recentBlocks.html b/templates/index/recentBlocks.html index ec7ecf17..ab59debc 100644 --- a/templates/index/recentBlocks.html +++ b/templates/index/recentBlocks.html @@ -20,7 +20,7 @@
- {{ html "" }} + {{ html "" }} @@ -28,7 +28,7 @@
-
+
@@ -36,7 +36,7 @@
-
-
+
-
Genesis @@ -51,7 +51,7 @@
{{ html "" }} - {{ html "" }} + {{ html "" }} no blocks found diff --git a/templates/index/recentEpochs.html b/templates/index/recentEpochs.html index ad4863df..ea9d6cfa 100644 --- a/templates/index/recentEpochs.html +++ b/templates/index/recentEpochs.html @@ -19,7 +19,7 @@
- {{ html "" }} + {{ html "" }} @@ -29,18 +29,18 @@
Yes No - +
- +
-
+
{{ html "" }} - {{ html "" }} + {{ html "" }} no epochs found diff --git a/templates/index/recentSlots.html b/templates/index/recentSlots.html index 8a9bf020..5106f99a 100644 --- a/templates/index/recentSlots.html +++ b/templates/index/recentSlots.html @@ -20,7 +20,7 @@
- {{ html "" }} + {{ html "" }} {{ html "" }} @@ -52,7 +52,7 @@
{{ html "" }} - {{ html "" }} + {{ html "" }} diff --git a/types/models/indexPage.go b/types/models/indexPage.go index ad45ca9d..c3f4e12b 100644 --- a/types/models/indexPage.go +++ b/types/models/indexPage.go @@ -6,35 +6,34 @@ import ( // IndexPageData is a struct to hold info for the main web page type IndexPageData struct { - NetworkName string `json:"network_name"` - DepositContract string `json:"deposit_contract"` - ShowSyncingMessage bool `json:"show_sync_message"` + NetworkName string `json:"netname"` + DepositContract string `json:"depaddr"` + ShowSyncingMessage bool `json:"show_sync"` SlotsPerEpoch uint64 `json:"slots_per_epoch"` - CurrentEpoch uint64 `json:"current_epoch"` - CurrentFinalizedEpoch int64 `json:"current_finalized_epoch"` - CurrentSlot uint64 `json:"current_slot"` - CurrentSlotIndex uint64 `json:"current_slot_index"` - CurrentScheduledCount uint64 `json:"current_scheduled_count"` - CurrentEpochProgress float64 `json:"current_epoch_progress"` - ActiveValidatorCount uint64 `json:"active_validator_count"` - EnteringValidatorCount uint64 `json:"entering_validator_count"` - ExitingValidatorCount uint64 `json:"exiting_validator_count"` - ValidatorsPerEpoch uint64 `json:"validators_per_epoch"` - ValidatorsPerDay uint64 `json:"validators_per_day"` - TotalEligibleEther uint64 `json:"total_eligible_ether"` - AverageValidatorBalance uint64 `json:"avg_validator_balance"` - NewDepositProcessAfter string `json:"deposit_queue_delay"` + CurrentEpoch uint64 `json:"cur_epoch"` + CurrentFinalizedEpoch int64 `json:"finalized_epoch"` + CurrentSlot uint64 `json:"cur_slot"` + CurrentScheduledCount uint64 `json:"cur_scheduled"` + CurrentEpochProgress float64 `json:"cur_epoch_prog"` + ActiveValidatorCount uint64 `json:"active_val"` + EnteringValidatorCount uint64 `json:"entering_val"` + ExitingValidatorCount uint64 `json:"exiting_val"` + ValidatorsPerEpoch uint64 `json:"churn_epoch"` + ValidatorsPerDay uint64 `json:"churn_day"` + TotalEligibleEther uint64 `json:"eligible"` + AverageValidatorBalance uint64 `json:"avg_balance"` + NewDepositProcessAfter string `json:"queue_delay"` GenesisTime time.Time `json:"genesis_time"` GenesisForkVersion []byte `json:"genesis_version"` GenesisValidatorsRoot []byte `json:"genesis_valroot"` NetworkForks []*IndexPageDataForks `json:"network_forks"` - RecentBlocks []*IndexPageDataBlocks `json:"recent_blocks"` - RecentBlockCount uint64 `json:"recent_block_count"` - RecentEpochs []*IndexPageDataEpochs `json:"recent_epochs"` - RecentEpochCount uint64 `json:"recent_epoch_count"` - RecentSlots []*IndexPageDataSlots `json:"recent_slots"` - RecentSlotCount uint64 `json:"recent_slot_count"` + RecentBlocks []*IndexPageDataBlocks `json:"blocks"` + RecentBlockCount uint64 `json:"block_count"` + RecentEpochs []*IndexPageDataEpochs `json:"epochs"` + RecentEpochCount uint64 `json:"epoch_count"` + RecentSlots []*IndexPageDataSlots `json:"slots"` + RecentSlotCount uint64 `json:"slot_count"` ForkTreeWidth int `json:"forktree_width"` } @@ -49,17 +48,15 @@ type IndexPageDataEpochs struct { Epoch uint64 `json:"epoch"` Ts time.Time `json:"ts"` Finalized bool `json:"finalized"` - EligibleEther uint64 `json:"eligibleether"` - TargetVoted uint64 `json:"target_voted"` - HeadVoted uint64 `json:"head_voted"` - TotalVoted uint64 `json:"total_voted"` - VoteParticipation float64 `json:"vote_participation"` + EligibleEther uint64 `json:"eligible"` + TargetVoted uint64 `json:"voted"` + VoteParticipation float64 `json:"votep"` } type IndexPageDataBlocks struct { Epoch uint64 `json:"epoch"` Slot uint64 `json:"slot"` - WithEthBlock bool `json:"with_eth_block"` + WithEthBlock bool `json:"has_block"` EthBlock uint64 `json:"eth_block"` EthBlockLink string `json:"eth_link"` Ts time.Time `json:"ts"` @@ -78,7 +75,7 @@ type IndexPageDataSlots struct { ProposerName string `json:"proposer_name"` Status uint64 `json:"status"` BlockRoot []byte `json:"block_root"` - ParentRoot []byte `json:"parent_root"` + ParentRoot []byte `json:"-"` ForkGraph []*IndexPageDataForkGraph `json:"fork_graph"` } From 52bfd04db6d4d7ebad37cb8193186cb5b2384066 Mon Sep 17 00:00:00 2001 From: pk910 Date: Fri, 8 Sep 2023 03:53:17 +0200 Subject: [PATCH 07/10] cache genesis rpc response (front page does not need any rpc calls now) --- indexer/cache.go | 9 +++++++++ indexer/client.go | 1 + indexer/indexer.go | 4 ++++ services/beaconservice.go | 2 +- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/indexer/cache.go b/indexer/cache.go index e54bd567..434903d1 100644 --- a/indexer/cache.go +++ b/indexer/cache.go @@ -28,6 +28,7 @@ type indexerCache struct { epochStatsMap map[uint64][]*EpochStats lastValidatorsEpoch int64 lastValidatorsResp *rpctypes.StandardV1StateValidatorsResponse + genesisResp *rpctypes.StandardV1GenesisResponse validatorLoadingLimiter chan int } @@ -79,6 +80,14 @@ func (cache *indexerCache) setFinalizedHead(epoch int64, root []byte) { } } +func (cache *indexerCache) setGenesis(genesis *rpctypes.StandardV1GenesisResponse) { + cache.cacheMutex.Lock() + defer cache.cacheMutex.Unlock() + if cache.genesisResp == nil { + cache.genesisResp = genesis + } +} + func (cache *indexerCache) getFinalizedHead() (int64, []byte) { cache.cacheMutex.RLock() defer cache.cacheMutex.RUnlock() diff --git a/indexer/client.go b/indexer/client.go index 1484178a..505c30e0 100644 --- a/indexer/client.go +++ b/indexer/client.go @@ -153,6 +153,7 @@ func (client *IndexerClient) checkIndexerClient() error { if genesis.Data.GenesisForkVersion.String() != utils.Config.Chain.Config.GenesisForkVersion { return fmt.Errorf("genesis fork version from RPC does not match the genesis fork version explorer configuration") } + client.indexerCache.setGenesis(genesis) // check syncronization state syncStatus, err := client.rpcClient.GetNodeSyncing() diff --git a/indexer/indexer.go b/indexer/indexer.go index a6f7714f..4aac1d4f 100644 --- a/indexer/indexer.go +++ b/indexer/indexer.go @@ -153,6 +153,10 @@ func (indexer *Indexer) GetRpcClient(archive bool, head []byte) *rpc.BeaconClien return readyClient.rpcClient } +func (indexer *Indexer) GetCachedGenesis() *rpctypes.StandardV1GenesisResponse { + return indexer.indexerCache.genesisResp +} + func (indexer *Indexer) GetFinalizedEpoch() (int64, []byte) { return indexer.indexerCache.getFinalizedHead() } diff --git a/services/beaconservice.go b/services/beaconservice.go index b528bc2a..50e21fe6 100644 --- a/services/beaconservice.go +++ b/services/beaconservice.go @@ -85,7 +85,7 @@ func (bs *BeaconService) GetCachedEpochStats(epoch uint64) *indexer.EpochStats { } func (bs *BeaconService) GetGenesis() (*rpctypes.StandardV1GenesisResponse, error) { - return bs.indexer.GetRpcClient(false, nil).GetGenesis() + return bs.indexer.GetCachedGenesis(), nil } func (bs *BeaconService) GetSlotDetailsByBlockroot(blockroot []byte) (*rpctypes.CombinedBlockResponse, error) { From 146725a6bd50200108f9bb8ce34bc1c227646bdc Mon Sep 17 00:00:00 2001 From: pk910 Date: Fri, 8 Sep 2023 04:29:12 +0200 Subject: [PATCH 08/10] fixed stale tooltips when underlying element gets removed --- static/js/explorer.js | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/static/js/explorer.js b/static/js/explorer.js index af091da9..9169b1ee 100644 --- a/static/js/explorer.js +++ b/static/js/explorer.js @@ -5,20 +5,51 @@ window.setInterval(updateTimers, 1000); initHeaderSearch(); }); + var tooltipDict = {}; + var tooltipIdx = 1; window.explorer = { initControls: initControls, renderRecentTime: renderRecentTime, + tooltipDict: tooltipDict, }; function initControls() { // init tooltips - var tooltipEls = document.querySelectorAll('[data-bs-toggle="tooltip"]'); - Array.prototype.forEach.call(tooltipEls, function(tooltipEl) { - new bootstrap.Tooltip(tooltipEl) - }); + document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(initTooltip); + cleanupTooltips(); // init clipboard buttons - var clipboard = new ClipboardJS("[data-clipboard-text]"); + document.querySelectorAll("[data-clipboard-text]").forEach(initCopyBtn); + } + + function initTooltip(el) { + if($(el).data("tooltip-init")) + return; + //console.log("init tooltip", el); + var idx = tooltipIdx++; + $(el).data("tooltip-init", idx).attr("data-tooltip-idx", idx.toString()); + $(el).tooltip(); + var tooltip = bootstrap.Tooltip.getInstance(el); + tooltipDict[idx] = { + element: el, + tooltip: tooltip, + }; + } + + function cleanupTooltips() { + Object.keys(explorer.tooltipDict).forEach(function(idx) { + var ref = explorer.tooltipDict[idx]; + if(document.body.contains(ref.element)) return; + ref.tooltip.dispose(); + delete explorer.tooltipDict[idx]; + }); + } + + function initCopyBtn(el) { + if($(el).data("clipboard-init")) + return; + $(el).data("clipboard-init", true); + var clipboard = new ClipboardJS(el); clipboard.on("success", onClipboardSuccess); clipboard.on("error", onClipboardError); } From ec0bbb6a8fa03f5995a789ad8a102e841cfbdb5f Mon Sep 17 00:00:00 2001 From: pk910 Date: Fri, 8 Sep 2023 04:36:57 +0200 Subject: [PATCH 09/10] fix high memory consumption due to recursive timer calls --- static/js/page-index.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/static/js/page-index.js b/static/js/page-index.js index e3542b88..b9ad8362 100644 --- a/static/js/page-index.js +++ b/static/js/page-index.js @@ -1,11 +1,12 @@ (function() { window.addEventListener('DOMContentLoaded', function() { - window.setTimeout(refreshLoop, 500); + window.setInterval(scheduleLoop, 500); }); var refreshInterval = 15000; var lastRefresh = new Date().getTime(); + var loopTimer = null; var isRefreshing = false; var viewModel = null; var baseModel = { @@ -19,7 +20,19 @@ hexstr: function(x) { return "0x" + base64ToHex(x); }, }; + function scheduleLoop() { + if(loopTimer) + return; + var refreshTimeout = refreshInterval - ((new Date().getTime() - lastRefresh)); + if(refreshTimeout < 0) + refreshTimeout = 0; + else if(refreshTimeout > 1000) + refreshTimeout -= Math.floor(refreshTimeout/1000)*1000; + loopTimer = setTimeout(refreshLoop, refreshTimeout); + } + function refreshLoop() { + loopTimer = null; var refreshTimeout = refreshInterval - ((new Date().getTime() - lastRefresh)); if(refreshTimeout < 0) refreshTimeout = 0; @@ -30,12 +43,6 @@ refreshTimeout = refreshInterval; refresh(); } - - if(refreshTimeout > 1000) - refreshTimeout -= Math.floor(refreshTimeout/1000)*1000; - if(refreshTimeout == 0) - refreshTimeout = 1000; - window.setTimeout(refreshLoop, refreshTimeout); } async function refresh() { From 461aa26326d7ad66cd44505990eaf2fba1b2466e Mon Sep 17 00:00:00 2001 From: pk910 Date: Fri, 8 Sep 2023 04:46:28 +0200 Subject: [PATCH 10/10] refresh fork badges --- templates/index/networkOverview.html | 5 ++++- types/models/indexPage.go | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/templates/index/networkOverview.html b/templates/index/networkOverview.html index 97932757..30317039 100644 --- a/templates/index/networkOverview.html +++ b/templates/index/networkOverview.html @@ -102,7 +102,10 @@
Network Forks:
-
+
+ {{ html "" }} + + {{ html "" }} {{ range $i, $fork := .NetworkForks }} {{ if $fork.Active }} {{ $fork.Name }} diff --git a/types/models/indexPage.go b/types/models/indexPage.go index c3f4e12b..c1aacf03 100644 --- a/types/models/indexPage.go +++ b/types/models/indexPage.go @@ -27,7 +27,7 @@ type IndexPageData struct { GenesisForkVersion []byte `json:"genesis_version"` GenesisValidatorsRoot []byte `json:"genesis_valroot"` - NetworkForks []*IndexPageDataForks `json:"network_forks"` + NetworkForks []*IndexPageDataForks `json:"forks"` RecentBlocks []*IndexPageDataBlocks `json:"blocks"` RecentBlockCount uint64 `json:"block_count"` RecentEpochs []*IndexPageDataEpochs `json:"epochs"`