Merge with master

pull/2514/head
Victor Baranov 5 years ago
commit 992e243049
  1. 2
      .credo.exs
  2. 6
      CHANGELOG.md
  3. 637
      apps/block_scout_web/assets/js/lib/awesomplete-util.js
  4. 2
      apps/block_scout_web/assets/js/lib/awesomplete.js
  5. 63
      apps/block_scout_web/assets/js/non-critical.js
  6. 3099
      apps/block_scout_web/assets/package-lock.json
  7. 5
      apps/block_scout_web/assets/package.json
  8. 44
      apps/block_scout_web/assets/webpack.config.js
  9. 4
      apps/block_scout_web/lib/block_scout_web/csp_header.ex
  10. 10
      apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex
  11. 4
      apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex
  12. 4
      apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex
  13. 10
      apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex
  14. 3
      apps/block_scout_web/package-lock.json
  15. 90
      apps/block_scout_web/priv/gettext/default.pot
  16. 90
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  17. 2
      apps/explorer/lib/explorer/chain.ex

@ -89,7 +89,7 @@
# or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just
# set the `excluded_macros` parameter to `[:schema, :setup, :test]`.
#
{Credo.Check.Design.DuplicatedCode, false},
{Credo.Check.Design.DuplicatedCode, excluded_macros: [], mass_threshold: 80},
# You can also customize the exit_status of each check.
# If you don't want TODO comments to cause `mix credo` to fail, just

@ -6,6 +6,8 @@
- [#2456](https://github.com/poanetwork/blockscout/pull/2456) - fetch pending transactions for geth
### Fixes
- [#2503](https://github.com/poanetwork/blockscout/pull/2503) - Mitigate autocompletion library influence to page loading performance
- [#2502](https://github.com/poanetwork/blockscout/pull/2502) - increase reward task timeout
- [#2463](https://github.com/poanetwork/blockscout/pull/2463) - dark theme fixes
- [#2496](https://github.com/poanetwork/blockscout/pull/2496) - fix docker build
- [#2495](https://github.com/poanetwork/blockscout/pull/2495) - fix logs for indexed chain
@ -13,12 +15,14 @@
- [#2425](https://github.com/poanetwork/blockscout/pull/2425) - Force to show address view for checksummed address even if it is not in DB
### Chore
- [#2402](https://github.com/poanetwork/blockscout/pull/2402) - bump otp version to 22.0
- [#2492](https://github.com/poanetwork/blockscout/pull/2492) - hide decoded row if event is not decoded
- [#2490](https://github.com/poanetwork/blockscout/pull/2490) - enable credo duplicated code check
- [#2432](https://github.com/poanetwork/blockscout/pull/2432) - bump credo version
- [#2457](https://github.com/poanetwork/blockscout/pull/2457) - update mix.lock
- [#2435](https://github.com/poanetwork/blockscout/pull/2435) - Replace deprecated extract-text-webpack-plugin with mini-css-extract-plugin
- [#2450](https://github.com/poanetwork/blockscout/pull/2450) - Fix clearance of logs and node_modules folders in clearing script
- [#2434](https://github.com/poanetwork/blockscout/pull/2434) - get rid of timex warnings
- [#2402](https://github.com/poanetwork/blockscout/pull/2402) - bump otp version to 22.0
## 2.0.2-beta

@ -0,0 +1,637 @@
/* eslint-env browser */
/* global Awesomplete */
/* exported AwesompleteUtil */
/*
* Library endorsing Lea Verou's Awesomplete widget, providing:
* - dynamic remote data loading
* - labels with HTML markup
* - events and styling for exact matches
* - events and styling for mismatches
* - select item when TAB key is used
*
* (c) Nico Hoogervorst
* License: MIT
*
*/
window.AwesompleteUtil = (function () {
//
// event names and css classes
//
var _AWE = 'awesomplete-'
var _AWE_LOAD = _AWE + 'loadcomplete'
var _AWE_CLOSE = _AWE + 'close'
var _AWE_MATCH = _AWE + 'match'
var _AWE_PREPOP = _AWE + 'prepop'
var _AWE_SELECT = _AWE + 'select'
var _CLS_FOUND = 'awe-found'
var _CLS_NOT_FOUND = 'awe-not-found'
var $ = Awesomplete.$ /* shortcut for document.querySelector */
//
// private functions
//
// Some parts are shamelessly copied from Awesomplete.js like the logic inside this _suggestion function.
// Returns an object with label and value properties. Data parameter is plain text or Object/Array with label and value.
function _suggestion (data) {
var lv = Array.isArray(data)
? { label: data[0], value: data[1] }
: typeof data === 'object' && 'label' in data && 'value' in data ? data : { label: data, value: data }
return {label: lv.label || lv.value, value: lv.value}
}
// Helper to send events with detail property.
function _fire (target, name, detail) {
// $.fire uses deprecated methods but other methods don't work in IE11.
return $.fire(target, name, {detail: detail})
}
// Look if there is an exact match or a mismatch, set awe-found, awe-not-found css class and send match events.
function _matchValue (awe, prepop) {
var input = awe.input /* the input field */
var classList = input.classList
var utilprops = awe.utilprops /* extra properties piggybacked on Awesomplete object */
var selected = utilprops.selected /* the exact selected Suggestion with label and value */
var val = utilprops.convertInput.call(awe, input.value) /* trimmed lowercased value */
var opened = awe.opened /* is the suggestion list opened? */
var result = [] /* matches with value */
var list = awe._list /* current list of suggestions */
var suggestion, fake, rec, j /* function scoped variables */
utilprops.prepop = false /* after the first call it's not a prepopulation phase anymore */
if (list) { /* if there is a suggestion list */
for (j = 0; j < list.length; j++) { /* loop all suggestions */
rec = list[j]
suggestion = _suggestion(awe.data(rec, val)) /* call data convert function */
// with maxItems = 0 cannot look if suggestion list is opened to determine if there are still matches,
// instead call the filter method to see if there are still some options.
if (awe.maxItems === 0) {
// Awesomplete.FILTER_CONTAINS and Awesomplete.FILTER_STARTSWITH use the toString method.
suggestion.toString = function () { return '' + this.label }
if (awe.filter(suggestion, val)) {
// filter returns true, so there is at least one partial match.
opened = true
}
}
// Don't want to change the real input field, emulate a fake one.
fake = {input: {value: ''}}
// Determine how this suggestion would look like if it is replaced in the input field,
// it is an exact match if somebody types exactly that.
// Use the fake input here. fake.input.value will contain the result of the replace function.
awe.replace.call(fake, suggestion)
// Trim and lowercase also the fake input and compare that with the currently typed-in value.
if (utilprops.convertInput.call(awe, fake.input.value) === val) {
// This is an exact match. However there might more suggestions with the same value.
// If the user selected a suggestion from the list, check if this one matches, assuming that
// value + label is unique (if not it will be difficult for the user to make an informed decision).
if (selected && selected.value === suggestion.value && selected.label === suggestion.label) {
// this surely is the selected one
result = [rec]
break
}
// add the matching record to the result set.
result.push(rec)
} // end if
} // end loop
// if the result differs from the previous result
if (utilprops.prevSelected !== result) {
// if there is an exact match
if (result.length > 0) {
// if prepopulation phase (initial/autofill value); not triggered by user input
if (prepop) {
_fire(input, _AWE_PREPOP, result)
} else if (utilprops.changed) { /* if input is changed */
utilprops.prevSelected = result /* new result */
classList.remove(_CLS_NOT_FOUND) /* remove class */
classList.add(_CLS_FOUND) /* add css class */
_fire(input, _AWE_MATCH, result) /* fire event */
}
} else if (prepop) { /* no exact match, if in prepopulation phase */
_fire(input, _AWE_PREPOP, [])
} else if (utilprops.changed) { /* no exact match, if input is changed */
utilprops.prevSelected = []
classList.remove(_CLS_FOUND)
// Mark as not-found if there are no suggestions anymore or if another field is now active
if (!opened || (input !== document.activeElement)) {
if (val.length > 0) {
classList.add(_CLS_NOT_FOUND)
_fire(input, _AWE_MATCH, [])
}
} else {
classList.remove(_CLS_NOT_FOUND)
}
}
}
}
}
// Listen to certain events of THIS awesomplete object to trigger input validation.
function _match (ev) {
var awe = this
if ((ev.type === _AWE_CLOSE || ev.type === _AWE_LOAD || ev.type === 'blur') && ev.target === awe.input) {
_matchValue(awe, awe.utilprops.prepop && ev.type === _AWE_LOAD)
}
}
// Select currently selected item if tab or shift-tab key is used.
function _onKeydown (ev) {
var awe = this
if (ev.target === awe.input && ev.keyCode === 9) { // TAB key
awe.select() // take current selected item
}
}
// Handle selection event. State changes when an item is selected.
function _select (ev) {
var awe = this
awe.utilprops.changed = true // yes, user made a change
awe.utilprops.selected = ev.text // Suggestion object
}
// check if the object is empty {} object
function _isEmpty (val) {
return Object.keys(val).length === 0 && val.constructor === Object
}
// Need an updated suggestion list if:
// - There is no result yet, or there is a result but not for the characters we entered
// - or there might be more specific results because the limit was reached.
function _ifNeedListUpdate (awe, val, queryVal) {
var utilprops = awe.utilprops
return (!utilprops.listQuery ||
(!utilprops.loadall && /* with loadall, if there is a result, there is no need for new lists */
val.lastIndexOf(queryVal, 0) === 0 &&
(val.lastIndexOf(utilprops.listQuery, 0) !== 0 ||
(typeof utilprops.limit === 'number' && awe._list.length >= utilprops.limit))))
}
// Set a new suggestion list. Trigger loadcomplete event.
function _loadComplete (awe, list, queryVal) {
awe.list = list
awe.utilprops.listQuery = queryVal
_fire(awe.input, _AWE_LOAD, queryVal)
}
// Handle ajax response. Expects HTTP OK (200) response with JSON object with suggestion(s) (array).
function _onLoad () {
var t = this
var awe = t.awe
var xhr = t.xhr
var queryVal = t.queryVal
var val = awe.utilprops.val
var data
var prop
if (xhr.status === 200) {
data = JSON.parse(xhr.responseText)
if (awe.utilprops.convertResponse) data = awe.utilprops.convertResponse(data)
if (!Array.isArray(data)) {
if (awe.utilprops.limit === 0 || awe.utilprops.limit === 1) {
// if there is max 1 result expected, the array is not needed.
// Fur further processing, take the whole result and put it as one element in an array.
data = _isEmpty(data) ? [] : [data]
} else {
// search for the first property that contains an array
for (prop in data) {
if (Array.isArray(data[prop])) {
data = data[prop]
break
}
}
}
}
// can only handle arrays
if (Array.isArray(data)) {
// are we still interested in this response?
if (_ifNeedListUpdate(awe, val, queryVal)) {
// accept the new suggestion list
_loadComplete(awe, data, queryVal || awe.utilprops.loadall)
}
}
}
}
// Perform suggestion list lookup for the current value and validate. Use ajax when there is an url specified.
function _lookup (awe, val) {
var xhr
if (awe.utilprops.url) {
// are we still interested in this response?
if (_ifNeedListUpdate(awe, val, val)) {
xhr = new XMLHttpRequest()
awe.utilprops.ajax.call(awe,
awe.utilprops.url,
awe.utilprops.urlEnd,
awe.utilprops.loadall ? '' : val,
_onLoad.bind({awe: awe, xhr: xhr, queryVal: val}),
xhr
)
} else {
_matchValue(awe, awe.utilprops.prepop)
}
} else {
_matchValue(awe, awe.utilprops.prepop)
}
}
// Restart autocomplete search: clear css classes and send match-event with empty list.
function _restart (awe) {
var elem = awe.input
var classList = elem.classList
// IE11 only handles the first parameter of the remove method.
classList.remove(_CLS_NOT_FOUND)
classList.remove(_CLS_FOUND)
_fire(elem, _AWE_MATCH, [])
}
// handle new input value
function _update (awe, val, prepop) {
// prepop parameter is optional. Default value is false.
awe.utilprops.prepop = prepop || false
// if value changed
if (awe.utilprops.val !== val) {
// new value, clear previous selection
awe.utilprops.selected = null
// yes, user made a change
awe.utilprops.changed = true
awe.utilprops.val = val
// value is empty or smaller than minChars
if (val.length < awe.minChars || val.length === 0) {
// restart autocomplete search
_restart(awe)
}
if (val.length >= awe.minChars) {
// lookup suggestions and validate input
_lookup(awe, val)
}
}
return awe
}
// handle input changed event for THIS awesomplete object
function _onInput (e) {
var awe = this
var val
if (e.target === awe.input) {
// lowercase and trim input value
val = awe.utilprops.convertInput.call(awe, awe.input.value)
_update(awe, val)
}
}
// item function (as specified in Awesomplete) which just creates the 'li' HTML tag.
function _item (html /* , input */) {
return $.create('li', {
innerHTML: html,
'aria-selected': 'false'
})
}
// Escape HTML characters in text.
function _htmlEscape (text) {
return text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
}
// Function to copy a field from the selected autocomplete item to another DOM element.
function _copyFun (e) {
var t = this
var sourceId = t.sourceId
var dataField = t.dataField
var targetId = t.targetId
var elem
var val
if (e.target === $(sourceId)) {
if (typeof targetId === 'function') {
targetId(e, dataField)
} else {
// lookup target element if it isn't resolved yet
elem = $(targetId)
// don't override target inputs if user is currently editing it.
if (elem && elem !== document.activeElement) {
// event must contain 1 item from suggestion list
val = Array.isArray(e.detail) && e.detail.length === 1 ? e.detail[0] : null
// if a datafield is specified, take that value
val = (dataField && val ? val[dataField] : val) || ''
// if it is an input control
if (typeof elem.value !== 'undefined') {
// set new value
elem.value = val
// not really sure if it is an input control, check if it has a classList
if (elem.classList && elem.classList.remove) {
// it might be another awesomplete control, if so the input is not wrong anymore because it's changed now
elem.classList.remove(_CLS_NOT_FOUND)
}
} else if (typeof elem.src !== 'undefined') { /* is it an image tag? */
elem.src = val
} else {
// use innerHTML to set the new value, because value might intentionally contain HTML markup
elem.innerHTML = val
}
}
}
}
}
// click function for the combobox button
function _clickFun (e) {
var t = this
var awe
var minChars
if (e.target === $(t.btnId)) {
e.preventDefault()
awe = t.awe
// toggle open/close
if (awe.ul.childNodes.length === 0 || awe.ul.hasAttribute('hidden')) {
minChars = awe.minChars
// ignore that the input value is empty
awe.minChars = 0
// show the suggestion list
awe.evaluate()
awe.minChars = minChars
} else {
awe.close()
}
}
}
// Return text with mark tags arround matching input. Don't replace inside <HTML> tags.
// When startsWith is true, mark only the matching begin text.
function _mark (text, input, startsWith) {
var searchText = $.regExpEscape(_htmlEscape(input).trim())
var regExp = searchText.length <= 0 ? null : startsWith ? RegExp('^' + searchText, 'i') : RegExp('(?!<[^>]+?>)' + searchText + '(?![^<]*?>)', 'gi')
return text.replace(regExp, '<mark>$&</mark>')
}
// Recursive jsonFlatten function
function _jsonFlatten (result, cur, prop, level, opts) {
var root = opts.root /* filter resulting json tree on root property (optional) */
var value = opts.value /* search for this property and copy it's value to a new 'value' property
(optional, do not specify it if the json array contains plain strings) */
var label = opts.label || opts.value /* search this property and copy it's value to a new 'label' property.
If there is a 'opts.value' field but no 'opts.label', assume label is the same. */
var isEmpty = true
var arrayResult = []
var j
// at top level, look if there is a property which starts with root (if specified)
if (level === 0 && root && prop && (prop + '.').lastIndexOf(root + '.', 0) !== 0 && (root + '.').lastIndexOf(prop + '.', 0) !== 0) {
return result
}
// handle current part of the json tree
if (Object(cur) !== cur) {
if (prop) {
result[prop] = cur
} else {
result = cur
}
} else if (Array.isArray(cur)) {
for (j = 0; j < cur.length; j++) {
arrayResult.push(_jsonFlatten({}, cur[j], '', level + 1, opts))
}
if (prop) {
result[prop] = arrayResult
} else {
result = arrayResult
}
} else {
for (j in cur) {
isEmpty = false
_jsonFlatten(result, cur[j], prop ? prop + '.' + j : j, level, opts)
}
if (isEmpty && prop) result[prop] = {}
}
// for arrays at top and subtop level
if (level < 2 && prop) {
// if a 'value' is specified and found a mathing property, create extra 'value' property.
if (value && (prop + '.').lastIndexOf(value + '.', 0) === 0) { result['value'] = result[prop] }
// if a 'label' is specified and found a mathing property, create extra 'label' property.
if (label && (prop + '.').lastIndexOf(label + '.', 0) === 0) { result['label'] = result[prop] }
}
if (level === 0) {
// Make sure that both value and label properties exist, even if they are nil.
// This is handy with limit 0 or 1 when the result doesn't have to contain an array.
if (value && !('value' in result)) { result['value'] = null }
if (label && !('label' in result)) { result['label'] = null }
}
return result
}
// Stop AwesompleteUtil; detach event handlers from the Awesomplete object.
function _detach () {
var t = this
var elem = t.awe.input
var boundMatch = t.boundMatch
var boundOnInput = t.boundOnInput
var boundOnKeydown = t.boundOnKeydown
var boundSelect = t.boundSelect
elem.removeEventListener(_AWE_SELECT, boundSelect)
elem.removeEventListener(_AWE_LOAD, boundMatch)
elem.removeEventListener(_AWE_CLOSE, boundMatch)
elem.removeEventListener('blur', boundMatch)
elem.removeEventListener('input', boundOnInput)
elem.removeEventListener('keydown', boundOnKeydown)
}
//
// public methods
//
return {
// ajax call for url + val + urlEnd. fn is the callback function. xhr parameter is optional.
ajax: function (url, urlEnd, val, fn, xhr) {
xhr = xhr || new XMLHttpRequest()
xhr.open('GET', url + encodeURIComponent(val) + (urlEnd || ''))
xhr.onload = fn
xhr.send()
return xhr
},
// Convert input before comparing it with suggestion. lowercase and trim the text
convertInput: function (text) {
return typeof text === 'string' ? text.trim().toLowerCase() : ''
},
// item function as defined in Awesomplete.
// item(html, input). input is optional and ignored in this implementation
item: _item,
// Set a new suggestion list. Trigger loadcomplete event.
// load(awesomplete, list, queryVal)
load: _loadComplete,
// Return text with mark tags arround matching input. Don't replace inside <HTML> tags.
// When startsWith is true, mark only the matching begin text.
// mark(text, input, startsWith)
mark: _mark,
// highlight items: Marks input in the first line, not in the optional description
itemContains: function (text, input) {
var arr
if (input.trim().length > 0) {
arr = ('' + text).split(/<p>/)
arr[0] = _mark(arr[0], input)
text = arr.join('<p>')
}
return _item(text, input)
},
// highlight items: mark all occurrences of the input text
itemMarkAll: function (text, input) {
return _item(input.trim() === '' ? '' + text : _mark('' + text, input), input)
},
// highlight items: mark input in the begin text
itemStartsWith: function (text, input) {
return _item(input.trim() === '' ? '' + text : _mark('' + text, input, true), input)
},
// create Awesomplete object for input control elemId. opts are passed unchanged to Awesomplete.
create: function (elemId, utilOpts, opts) {
opts.item = opts.item || this.itemContains /* by default uses itemContains, can be overriden */
var awe = new Awesomplete(elemId, opts)
awe.utilprops = utilOpts || {}
// loadall is true if there is no url (there is a static data-list)
if (!awe.utilprops.url && typeof awe.utilprops.loadall === 'undefined') {
awe.utilprops.loadall = true
}
awe.utilprops.ajax = awe.utilprops.ajax || this.ajax /* default ajax function can be overriden */
awe.utilprops.convertInput = awe.utilprops.convertInput || this.convertInput /* the same applies for convertInput */
return awe
},
// attach Awesomplete object to event listeners
attach: function (awe) {
var elem = awe.input
var boundMatch = _match.bind(awe)
var boundOnKeydown = _onKeydown.bind(awe)
var boundOnInput = _onInput.bind(awe)
var boundSelect = _select.bind(awe)
var boundDetach = _detach.bind({awe: awe,
boundMatch: boundMatch,
boundOnInput: boundOnInput,
boundOnKeydown: boundOnKeydown,
boundSelect: boundSelect
})
var events = {
'keydown': boundOnKeydown,
'input': boundOnInput
}
events['blur'] = events[_AWE_CLOSE] = events[_AWE_LOAD] = boundMatch
events[_AWE_SELECT] = boundSelect
$.bind(elem, events)
awe.utilprops.detach = boundDetach
// Perform ajax call if prepop is true and there is an initial input value, or when all values must be loaded (loadall)
if (awe.utilprops.prepop && (awe.utilprops.loadall || elem.value.length > 0)) {
awe.utilprops.val = awe.utilprops.convertInput.call(awe, elem.value)
_lookup(awe, awe.utilprops.val)
}
return awe
},
// update input value via javascript. Use prepop=true when this is an initial/prepopulation value.
update: function (awe, value, prepop) {
awe.input.value = value
return _update(awe, value, prepop)
},
// create and attach Awesomplete object for input control elemId. opts are passed unchanged to Awesomplete.
start: function (elemId, utilOpts, opts) {
return this.attach(this.create(elemId, utilOpts, opts))
},
// Stop AwesompleteUtil; detach event handlers from the Awesomplete object.
detach: function (awe) {
if (awe.utilprops.detach) {
awe.utilprops.detach()
delete awe.utilprops.detach
}
return awe
},
// Create function to copy a field from the selected autocomplete item to another DOM element.
// dataField can be null.
createCopyFun: function (sourceId, dataField, targetId) {
return _copyFun.bind({sourceId: sourceId, dataField: dataField, targetId: $(targetId) || targetId})
},
// attach copy function to event listeners. prepop is optional and by default true.
// if true the copy function will also listen to awesomplete-prepop events.
// The optional listenEl is the element that listens, defaults to document.body.
attachCopyFun: function (fun, prepop, listenEl) {
// prepop parameter defaults to true
prepop = typeof prepop === 'boolean' ? prepop : true
listenEl = listenEl || document.body
listenEl.addEventListener(_AWE_MATCH, fun)
if (prepop) listenEl.addEventListener(_AWE_PREPOP, fun)
return fun
},
// Create and attach copy function.
startCopy: function (sourceId, dataField, targetId, prepop) {
var sourceEl = $(sourceId)
return this.attachCopyFun(this.createCopyFun(sourceEl || sourceId, dataField, targetId), prepop, sourceEl)
},
// Stop copy function. Detach it from event listeners.
// The optional listenEl must be the same element that was used during startCopy/attachCopyFun;
// in general: Awesomplete.$(sourceId). listenEl defaults to document.body.
detachCopyFun: function (fun, listenEl) {
listenEl = listenEl || document.body
listenEl.removeEventListener(_AWE_PREPOP, fun)
listenEl.removeEventListener(_AWE_MATCH, fun)
return fun
},
// Create function for combobox button (btnId) to toggle dropdown list.
createClickFun: function (btnId, awe) {
return _clickFun.bind({btnId: btnId, awe: awe})
},
// Attach click function for combobox to click event.
// The optional listenEl is the element that listens, defaults to document.body.
attachClickFun: function (fun, listenEl) {
listenEl = listenEl || document.body
listenEl.addEventListener('click', fun)
return fun
},
// Create and attach click function for combobox button. Toggles open/close of suggestion list.
startClick: function (btnId, awe) {
var btnEl = $(btnId)
return this.attachClickFun(this.createClickFun(btnEl || btnId, awe), btnEl)
},
// Stop click function. Detach it from event listeners.
// The optional listenEl must be the same element that was used during startClick/attachClickFun;
// in general: Awesomplete.$(btnId). listenEl defaults to document.body.
detachClickFun: function (fun, listenEl) {
listenEl = listenEl || document.body
listenEl.removeEventListener('click', fun)
return fun
},
// filter function as specified in Awesomplete. Filters suggestion list on items containing input value.
// Awesomplete.FILTER_CONTAINS filters on data.label, however
// this function filters on value and not on the shown label which may contain markup.
filterContains: function (data, input) {
return Awesomplete.FILTER_CONTAINS(data.value, input)
},
// filter function as specified in Awesomplete. Filters suggestion list on matching begin text.
// Awesomplete.FILTER_STARTSWITH filters on data.label, however
// this function filters on value and not on the shown label which may contain markup.
filterStartsWith: function (data, input) {
return Awesomplete.FILTER_STARTSWITH(data.value, input)
},
// Flatten JSON.
// { "a":{"b":{"c":[{"d":{"e":1}}]}}} becomes {"a.b.c":[{"d.e":1}]}.
// This function can be bind to configure it with extra options;
// bind({root: '<root path>', value: '<value property>', label: '<label property>'})
jsonFlatten: function (data) {
// start json tree recursion
return _jsonFlatten({}, data, '', 0, this)
}
}
}())

@ -0,0 +1,2 @@
import 'awesomplete/awesomplete.css'
import 'awesomplete'

@ -1,63 +0,0 @@
// We need to import the CSS so that webpack will load it.
// The ExtractTextPlugin is used to separate it out into
// its own CSS file.
import '../css/app.scss'
// webpack automatically concatenates all files in your
// watched paths. Those paths can be configured as
// endpoints in "webpack.config.js".
//
// Import dependencies
//
import '@babel/polyfill'
import 'phoenix_html'
import 'bootstrap'
// Import local files
//
// Local files can be imported directly using relative
// paths "./socket" or full ones "web/static/js/socket".
import './locale'
import './pages/address'
import './pages/address/coin_balances'
import './pages/address/transactions'
import './pages/address/logs'
import './pages/address/validations'
import './pages/address/internal_transactions'
import './pages/blocks'
import './pages/chain'
import './pages/pending_transactions'
import './pages/transaction'
import './pages/transactions'
import './pages/favorites'
import './pages/network-search'
import './pages/layout'
import './pages/verification_form'
import './pages/dark-mode-switcher'
import './pages/admin/tasks.js'
import './lib/clipboard_buttons'
import './lib/currency'
import './lib/from_now'
import './lib/indexing'
import './lib/loading_element'
import './lib/market_history_chart'
import './lib/pending_transactions_toggle'
import './lib/pretty_json'
import './lib/reload_button'
import './lib/smart_contract/read_only_functions'
import './lib/smart_contract/wei_ether_converter'
import './lib/stop_propagation'
import './lib/token_balance_dropdown'
import './lib/token_balance_dropdown_search'
import './lib/token_transfers_toggle'
import './lib/transaction_input_dropdown'
import './lib/async_listing_load'
import './lib/tooltip'
import './lib/modals'
import './lib/try_api'
import './lib/card_tabs'
import './lib/network_selector'

File diff suppressed because it is too large Load Diff

@ -20,6 +20,7 @@
},
"dependencies": {
"@fortawesome/fontawesome-free": "^5.1.0-4",
"awesomplete": "1.1.2",
"bignumber.js": "^7.2.1",
"bootstrap": "^4.1.3",
"chart.js": "^2.7.2",
@ -47,7 +48,7 @@
"babel-loader": "^7.1.4",
"babel-preset-env": "^1.6.1",
"copy-webpack-plugin": "^4.5.1",
"css-loader": "^0.28.11",
"css-loader": "^3.1.0",
"eslint": "^4.15.0",
"eslint-config-standard": "^11.0.0-beta.0",
"eslint-plugin-import": "^2.8.0",
@ -60,7 +61,7 @@
"node-sass": "^4.9.3",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^2.1.4",
"sass-loader": "^7.0.1",
"sass-loader": "^7.1.0",
"style-loader": "^0.21.0",
"terser-webpack-plugin": "^1.3.0",
"webpack": "^4.6.0",

@ -27,6 +27,46 @@ function transpileViewScript(file) {
}
};
const jsOptimizationParams = {
cache: true,
parallel: true,
sourceMap: true,
}
const awesompleteJs = {
entry: {
awesomplete: './js/lib/awesomplete.js',
'awesomplete-util': './js/lib/awesomplete-util.js',
},
output: {
filename: '[name].min.js',
path: path.resolve(__dirname, '../priv/static/js')
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
}
],
},
],
},
optimization: {
minimizer: [
new TerserJSPlugin(jsOptimizationParams),
],
},
plugins: [
new MiniCssExtractPlugin({
filename: '../css/awesomplete.css'
}),
]
}
const appJs =
{
entry: {
@ -39,7 +79,7 @@ const appJs =
path: path.resolve(__dirname, '../priv/static/js')
},
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
minimizer: [new TerserJSPlugin(jsOptimizationParams), new OptimizeCSSAssetsPlugin({})],
},
module: {
rules: [
@ -93,4 +133,4 @@ const appJs =
const viewScripts = glob.sync('./js/view_specific/**/*.js').map(transpileViewScript);
module.exports = viewScripts.concat(appJs);
module.exports = viewScripts.concat(appJs, awesompleteJs);

@ -13,8 +13,8 @@ defmodule BlockScoutWeb.CSPHeader do
"content-security-policy" => "\
connect-src 'self' #{websocket_endpoints(conn)}; \
default-src 'self';\
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://nico-amsterdam.github.io;\
style-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com https://nico-amsterdam.github.io;\
script-src 'self' 'unsafe-inline' 'unsafe-eval';\
style-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com;\
img-src 'self' 'unsafe-inline' 'unsafe-eval' data:;\
font-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.gstatic.com data:;\
"

@ -11,10 +11,10 @@
) %>
</h3>
</dd>
<dt class="col-md-2"><%= gettext "Decoded" %></dt>
<dd class="col-md-10">
<%= case decode(@log, @log.transaction) do %>
<% {:error, :contract_not_verified} -> %>
<dt class="col-md-2"><%= gettext "Decoded" %></dt>
<dd class="col-md-10">
<div class="alert alert-info">
<%= gettext "To see decoded input data, the contract must be verified." %>
<%= case @log.transaction do %>
@ -25,10 +25,14 @@
<% end %>
</div>
<% {:error, :could_not_decode} -> %>
<dt class="col-md-2"><%= gettext "Decoded" %></dt>
<dd class="col-md-10">
<div class="alert alert-danger">
<%= gettext "Failed to decode log data." %>
</div>
<% {:ok, method_id, text, mapping} -> %>
<dt class="col-md-2"><%= gettext "Decoded" %></dt>
<dd class="col-md-10">
<table summary="Transaction Info" class="table thead-light table-bordered transaction-input-table">
<tr>
<td>Method Id</td>
@ -78,7 +82,7 @@
<% end %>
</table>
</div>
<% _ -> %>
<% _ -> %>
<%= nil %>
<% end %>
<dt class="col-md-2"><%= gettext "Topics" %></dt>

@ -1,3 +1,7 @@
<link rel="preload" href="/css/awesomplete.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/css/awesomplete.css"></noscript>
<script src="/js/awesomplete.min.js"></script>
<script src="/js/awesomplete-util.min.js"></script>
<nav class="navbar navbar-dark navbar-expand-lg navbar-primary" data-selector="navbar" id="top-navbar">
<script>
if (localStorage.getItem("current-color-mode") === "dark") {

@ -18,10 +18,6 @@
<meta name="msapplication-config" content="<%= static_path(@conn, "/browserconfig.xml") %>">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="https://nico-amsterdam.github.io/awesomplete-util/css/awesomplete.css">
<script src="https://nico-amsterdam.github.io/awesomplete-util/js/awesomplete.min.js"></script>
<script src="https://nico-amsterdam.github.io/awesomplete-util/js/awesomplete-util.min.js"></script>
<%= render_existing(@view_module, "_metatags.html", assigns) || render("_default_title.html") %>
</head>

@ -11,10 +11,10 @@
) %>
</h3>
</dd>
<dt class="col-md-1"><%= gettext "Decoded" %></dt>
<dd class="col-md-11">
<%= case decode(@log, @transaction) do %>
<% {:error, :contract_not_verified} -> %>
<dt class="col-md-1"><%= gettext "Decoded" %></dt>
<dd class="col-md-11">
<div class="alert alert-info">
<%= gettext "To see decoded input data, the contract must be verified." %>
<%= case @transaction do %>
@ -25,10 +25,16 @@
<% end %>
</div>
<% {:error, :could_not_decode} -> %>
<dt class="col-md-1"><%= gettext "Decoded" %></dt>
<dd class="col-md-11">
<div class="alert alert-danger">
<%= gettext "Failed to decode log data." %>
</div>
<% {:error, :no_matching_function} -> %>
<%= nil %>
<% {:ok, method_id, text, mapping} -> %>
<dt class="col-md-1"><%= gettext "Decoded" %></dt>
<dd class="col-md-11">
<table summary="Transaction Info" class="table thead-light table-bordered transaction-input-table">
<tr>
<td>Method Id</td>

@ -1,3 +0,0 @@
{
"lockfileVersion": 1
}

@ -62,7 +62,7 @@ msgid "(query)"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:40
#: lib/block_scout_web/templates/layout/app.html.eex:36
msgid "- We're indexing this chain right now. Some of the counts may be inaccurate."
msgstr ""
@ -87,7 +87,7 @@ msgid "API for the %{subnetwork} - BlockScout"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:66
#: lib/block_scout_web/templates/layout/_topnav.html.eex:70
msgid "Accounts"
msgstr ""
@ -157,7 +157,7 @@ msgid "Block Height: %{height}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:55
#: lib/block_scout_web/templates/layout/app.html.eex:51
msgid "Block Mined, awaiting import..."
msgstr ""
@ -178,13 +178,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:87
#: lib/block_scout_web/templates/layout/_topnav.html.eex:26
#: lib/block_scout_web/templates/layout/_topnav.html.eex:30
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
msgid "Blocks"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:54
#: lib/block_scout_web/templates/layout/app.html.eex:50
msgid "Blocks Indexed"
msgstr ""
@ -368,7 +368,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_balance_card.html.eex:15
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:21
#: lib/block_scout_web/templates/layout/app.html.eex:60
#: lib/block_scout_web/templates/layout/app.html.eex:56
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20
#: lib/block_scout_web/templates/transaction/_tile.html.eex:30
#: lib/block_scout_web/templates/transaction/overview.html.eex:196
@ -392,7 +392,7 @@ msgid "Fetching tokens..."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:36
#: lib/block_scout_web/templates/layout/_topnav.html.eex:40
msgid "Forked Blocks (Reorgs)"
msgstr ""
@ -446,7 +446,7 @@ msgid "IN"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:56
#: lib/block_scout_web/templates/layout/app.html.eex:52
msgid "Indexing Tokens"
msgstr ""
@ -478,7 +478,7 @@ msgid "Inventory"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:57
#: lib/block_scout_web/templates/layout/app.html.eex:53
msgid "Less than"
msgstr ""
@ -499,7 +499,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:31
#: lib/block_scout_web/templates/layout/app.html.eex:58
#: lib/block_scout_web/templates/layout/app.html.eex:54
#: lib/block_scout_web/views/address_view.ex:121
#: lib/block_scout_web/views/address_view.ex:121
msgid "Market Cap"
@ -547,10 +547,10 @@ msgid "Must be set to:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:46
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:50
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:52
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:19
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:46
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:52
msgid "Name"
msgstr ""
@ -597,7 +597,7 @@ msgid "Parent Hash"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:54
#: lib/block_scout_web/templates/layout/_topnav.html.eex:58
#: lib/block_scout_web/views/transaction_view.ex:162
#: lib/block_scout_web/views/transaction_view.ex:196
msgid "Pending"
@ -615,7 +615,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:24
#: lib/block_scout_web/templates/layout/app.html.eex:59
#: lib/block_scout_web/templates/layout/app.html.eex:55
msgid "Price"
msgstr ""
@ -662,8 +662,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/index.html.eex:14
#: lib/block_scout_web/templates/layout/_topnav.html.eex:118
#: lib/block_scout_web/templates/layout/_topnav.html.eex:135
#: lib/block_scout_web/templates/layout/_topnav.html.eex:122
#: lib/block_scout_web/templates/layout/_topnav.html.eex:139
msgid "Search"
msgstr ""
@ -767,7 +767,7 @@ msgid "To"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:16
#: lib/block_scout_web/templates/layout/_topnav.html.eex:20
msgid "Toggle navigation"
msgstr ""
@ -860,7 +860,7 @@ msgstr ""
#: lib/block_scout_web/templates/block_transaction/index.html.eex:10
#: lib/block_scout_web/templates/block_transaction/index.html.eex:18
#: lib/block_scout_web/templates/chain/show.html.eex:108
#: lib/block_scout_web/templates/layout/_topnav.html.eex:45
#: lib/block_scout_web/templates/layout/_topnav.html.eex:49
#: lib/block_scout_web/views/address_view.ex:305
msgid "Transactions"
msgstr ""
@ -887,7 +887,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:80
#: lib/block_scout_web/templates/layout/_topnav.html.eex:33
#: lib/block_scout_web/templates/layout/_topnav.html.eex:37
msgid "Uncles"
msgstr ""
@ -902,7 +902,7 @@ msgid "Used"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:49
#: lib/block_scout_web/templates/layout/_topnav.html.eex:53
msgid "Validated"
msgstr ""
@ -1048,17 +1048,17 @@ msgid "Loading...."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:74
#: lib/block_scout_web/templates/layout/_topnav.html.eex:78
msgid "APIs"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:78
#: lib/block_scout_web/templates/layout/_topnav.html.eex:82
msgid "GraphQL"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:83
#: lib/block_scout_web/templates/layout/_topnav.html.eex:87
msgid "RPC"
msgstr ""
@ -1108,8 +1108,12 @@ msgid "Static Call"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:14
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:14
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:28
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:34
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:28
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:36
msgid "Decoded"
msgstr ""
@ -1430,8 +1434,8 @@ msgid "Error: Could not determine contract creator."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:112
#: lib/block_scout_web/templates/layout/_topnav.html.eex:116
#: lib/block_scout_web/templates/layout/_topnav.html.eex:120
msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr ""
@ -1657,7 +1661,7 @@ msgid "ETH RPC API Documentation"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:88
#: lib/block_scout_web/templates/layout/_topnav.html.eex:92
msgid "Eth RPC"
msgstr ""
@ -1753,9 +1757,9 @@ msgid "Constructor Arguments"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:59
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:63
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:31
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:59
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:65
msgid "Copy Value"
msgstr ""
@ -1765,29 +1769,29 @@ msgid "Create2"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:49
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:114
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:53
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:118
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:49
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:115
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:55
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:121
msgid "Data"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:29
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:29
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:31
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:31
msgid "Failed to decode log data."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:48
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:48
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:52
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:54
msgid "Indexed?"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:43
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:43
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:47
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:49
msgid "Log Data"
msgstr ""
@ -1799,14 +1803,14 @@ msgid "Reward"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:84
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:85
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:88
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:91
msgid "Topics"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:47
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:51
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:20
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:47
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:53
msgid "Type"
msgstr ""

@ -62,7 +62,7 @@ msgid "(query)"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:40
#: lib/block_scout_web/templates/layout/app.html.eex:36
msgid "- We're indexing this chain right now. Some of the counts may be inaccurate."
msgstr ""
@ -87,7 +87,7 @@ msgid "API for the %{subnetwork} - BlockScout"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:66
#: lib/block_scout_web/templates/layout/_topnav.html.eex:70
msgid "Accounts"
msgstr ""
@ -157,7 +157,7 @@ msgid "Block Height: %{height}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:55
#: lib/block_scout_web/templates/layout/app.html.eex:51
msgid "Block Mined, awaiting import..."
msgstr ""
@ -178,13 +178,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:87
#: lib/block_scout_web/templates/layout/_topnav.html.eex:26
#: lib/block_scout_web/templates/layout/_topnav.html.eex:30
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
msgid "Blocks"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:54
#: lib/block_scout_web/templates/layout/app.html.eex:50
msgid "Blocks Indexed"
msgstr ""
@ -368,7 +368,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_balance_card.html.eex:15
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:21
#: lib/block_scout_web/templates/layout/app.html.eex:60
#: lib/block_scout_web/templates/layout/app.html.eex:56
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20
#: lib/block_scout_web/templates/transaction/_tile.html.eex:30
#: lib/block_scout_web/templates/transaction/overview.html.eex:196
@ -392,7 +392,7 @@ msgid "Fetching tokens..."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:36
#: lib/block_scout_web/templates/layout/_topnav.html.eex:40
msgid "Forked Blocks (Reorgs)"
msgstr ""
@ -446,7 +446,7 @@ msgid "IN"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:56
#: lib/block_scout_web/templates/layout/app.html.eex:52
msgid "Indexing Tokens"
msgstr ""
@ -478,7 +478,7 @@ msgid "Inventory"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:57
#: lib/block_scout_web/templates/layout/app.html.eex:53
msgid "Less than"
msgstr ""
@ -499,7 +499,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:31
#: lib/block_scout_web/templates/layout/app.html.eex:58
#: lib/block_scout_web/templates/layout/app.html.eex:54
#: lib/block_scout_web/views/address_view.ex:121
#: lib/block_scout_web/views/address_view.ex:121
msgid "Market Cap"
@ -547,10 +547,10 @@ msgid "Must be set to:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:46
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:50
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:52
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:19
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:46
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:52
msgid "Name"
msgstr ""
@ -597,7 +597,7 @@ msgid "Parent Hash"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:54
#: lib/block_scout_web/templates/layout/_topnav.html.eex:58
#: lib/block_scout_web/views/transaction_view.ex:162
#: lib/block_scout_web/views/transaction_view.ex:196
msgid "Pending"
@ -615,7 +615,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:24
#: lib/block_scout_web/templates/layout/app.html.eex:59
#: lib/block_scout_web/templates/layout/app.html.eex:55
msgid "Price"
msgstr ""
@ -662,8 +662,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/index.html.eex:14
#: lib/block_scout_web/templates/layout/_topnav.html.eex:118
#: lib/block_scout_web/templates/layout/_topnav.html.eex:135
#: lib/block_scout_web/templates/layout/_topnav.html.eex:122
#: lib/block_scout_web/templates/layout/_topnav.html.eex:139
msgid "Search"
msgstr ""
@ -767,7 +767,7 @@ msgid "To"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:16
#: lib/block_scout_web/templates/layout/_topnav.html.eex:20
msgid "Toggle navigation"
msgstr ""
@ -860,7 +860,7 @@ msgstr ""
#: lib/block_scout_web/templates/block_transaction/index.html.eex:10
#: lib/block_scout_web/templates/block_transaction/index.html.eex:18
#: lib/block_scout_web/templates/chain/show.html.eex:108
#: lib/block_scout_web/templates/layout/_topnav.html.eex:45
#: lib/block_scout_web/templates/layout/_topnav.html.eex:49
#: lib/block_scout_web/views/address_view.ex:305
msgid "Transactions"
msgstr ""
@ -887,7 +887,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:80
#: lib/block_scout_web/templates/layout/_topnav.html.eex:33
#: lib/block_scout_web/templates/layout/_topnav.html.eex:37
msgid "Uncles"
msgstr ""
@ -902,7 +902,7 @@ msgid "Used"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:49
#: lib/block_scout_web/templates/layout/_topnav.html.eex:53
msgid "Validated"
msgstr ""
@ -1048,17 +1048,17 @@ msgid "Loading...."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:74
#: lib/block_scout_web/templates/layout/_topnav.html.eex:78
msgid "APIs"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:78
#: lib/block_scout_web/templates/layout/_topnav.html.eex:82
msgid "GraphQL"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:83
#: lib/block_scout_web/templates/layout/_topnav.html.eex:87
msgid "RPC"
msgstr ""
@ -1108,8 +1108,12 @@ msgid "Static Call"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:14
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:14
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:28
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:34
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:28
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:36
msgid "Decoded"
msgstr ""
@ -1431,8 +1435,8 @@ msgid "Error: Could not determine contract creator."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:112
#: lib/block_scout_web/templates/layout/_topnav.html.eex:116
#: lib/block_scout_web/templates/layout/_topnav.html.eex:120
msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr ""
@ -1658,7 +1662,7 @@ msgid "ETH RPC API Documentation"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:88
#: lib/block_scout_web/templates/layout/_topnav.html.eex:92
msgid "Eth RPC"
msgstr ""
@ -1754,9 +1758,9 @@ msgid "Constructor Arguments"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:59
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:63
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:31
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:59
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:65
msgid "Copy Value"
msgstr ""
@ -1766,29 +1770,29 @@ msgid "Create2"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:49
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:114
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:53
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:118
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:49
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:115
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:55
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:121
msgid "Data"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:29
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:29
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:31
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:31
msgid "Failed to decode log data."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:48
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:48
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:52
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:54
msgid "Indexed?"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:43
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:43
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:47
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:49
msgid "Log Data"
msgstr ""
@ -1800,14 +1804,14 @@ msgid "Reward"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:84
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:85
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:88
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:91
msgid "Topics"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:47
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:51
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:20
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:47
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:53
msgid "Type"
msgstr ""

@ -232,7 +232,7 @@ defmodule Explorer.Chain do
address_hash
|> address_to_transactions_without_rewards(paging_options, options)
|> Enum.concat(Task.await(rewards_task))
|> Enum.concat(Task.await(rewards_task, :timer.seconds(20)))
|> Enum.sort_by(fn item ->
case item do
{%Reward{} = emission_reward, _} ->

Loading…
Cancel
Save