diff --git a/.credo.exs b/.credo.exs
index 9f69aecd10..ba4d7feb49 100644
--- a/.credo.exs
+++ b/.credo.exs
@@ -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
diff --git a/.dialyzer-ignore b/.dialyzer-ignore
index de105bbd51..86584c2cb5 100644
--- a/.dialyzer-ignore
+++ b/.dialyzer-ignore
@@ -11,3 +11,5 @@ apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: The call 'Elixir.System'
apps/block_scout_web/lib/block_scout_web/views/layout_view.ex:175: The call 'Elixir.Poison.Parser':'parse!'(any(),#{'keys':='atoms!'}) will never return since the success typing is (binary() | maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | []),[{atom(),_}]) -> 'false' | 'nil' | 'true' | binary() | ['false' | 'nil' | 'true' | binary() | [any()] | number() | map()] | number() | map() and the contract is (iodata(),'Elixir.Keyword':t()) -> t()
apps/explorer/lib/explorer/smart_contract/publisher_worker.ex:6: The pattern 'false' can never match the type 'true'
apps/explorer/lib/explorer/smart_contract/publisher_worker.ex:6: The test 5 == 'infinity' can never evaluate to 'true'
+lib/block_scout_web/router.ex:1
+lib/phoenix/router.ex:324
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 74347d4e75..f006769dbf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,24 +1,83 @@
## Current
### Features
+- [#2561](https://github.com/poanetwork/blockscout/pull/2561) - Add token's type to the response of tokenlist method
+- [#2499](https://github.com/poanetwork/blockscout/pull/2499) - import emission reward ranges
+- [#2497](https://github.com/poanetwork/blockscout/pull/2497) - Add generic Ordered Cache behaviour and implementation
+
+### Fixes
+- [#2572](https://github.com/poanetwork/blockscout/pull/2572) - Ease non-critical css
+- [#2570](https://github.com/poanetwork/blockscout/pull/2570) - Network icons preload
+- [#2569](https://github.com/poanetwork/blockscout/pull/2569) - do not fetch emission rewards for transactions csv exporter
+- [#2568](https://github.com/poanetwork/blockscout/pull/2568) - filter pending token transfers
+- [#2564](https://github.com/poanetwork/blockscout/pull/2564) - fix first page button for uncles and reorgs
+- [#2563](https://github.com/poanetwork/blockscout/pull/2563) - Fix view less transfers button
+- [#2538](https://github.com/poanetwork/blockscout/pull/2538) - fetch the last not empty coin balance records
+
+### Chore
+- [#2566](https://github.com/poanetwork/blockscout/pull/2566) - upgrade absinthe phoenix
+
+
+## 2.0.3-beta
+
+### Features
+- [#2433](https://github.com/poanetwork/blockscout/pull/2433) - Add a functionality to try Eth RPC methods in the documentation
+- [#2529](https://github.com/poanetwork/blockscout/pull/2529) - show both eth value and token transfers on transaction overview page
+- [#2376](https://github.com/poanetwork/blockscout/pull/2376) - Split API and WebApp routes
- [#2477](https://github.com/poanetwork/blockscout/pull/2477) - aggregate token transfers on transaction page
- [#2458](https://github.com/poanetwork/blockscout/pull/2458) - Add LAST_BLOCK var to add ability indexing in the range of blocks
- [#2456](https://github.com/poanetwork/blockscout/pull/2456) - fetch pending transactions for geth
+- [#2403](https://github.com/poanetwork/blockscout/pull/2403) - Return gasPrice field at the result of gettxinfo method
### Fixes
+- [#2562](https://github.com/poanetwork/blockscout/pull/2562) - Fix dark theme flickering
+- [#2560](https://github.com/poanetwork/blockscout/pull/2560) - fix slash before not empty path in docs
+- [#2559](https://github.com/poanetwork/blockscout/pull/2559) - fix rsk total supply for empty exchange rate
+- [#2553](https://github.com/poanetwork/blockscout/pull/2553) - Dark theme import to the end of sass
+- [#2550](https://github.com/poanetwork/blockscout/pull/2550) - correctly encode decimal values for frontend
+- [#2549](https://github.com/poanetwork/blockscout/pull/2549) - Fix wrong colour of tooltip
+- [#2548](https://github.com/poanetwork/blockscout/pull/2548) - CSS preload support in Firefox
+- [#2547](https://github.com/poanetwork/blockscout/pull/2547) - do not show eth value if it's zero on the transaction overview page
+- [#2543](https://github.com/poanetwork/blockscout/pull/2543) - do not hide search input during logs search
+- [#2524](https://github.com/poanetwork/blockscout/pull/2524) - fix dark theme validator data styles
+- [#2532](https://github.com/poanetwork/blockscout/pull/2532) - don't show empty token transfers on the transaction overview page
+- [#2528](https://github.com/poanetwork/blockscout/pull/2528) - fix coin history chart data
+- [#2520](https://github.com/poanetwork/blockscout/pull/2520) - Hide loading message when fetching is failed
+- [#2523](https://github.com/poanetwork/blockscout/pull/2523) - Avoid importing internal_transactions of pending transactions
+- [#2519](https://github.com/poanetwork/blockscout/pull/2519) - enable `First` page button in pagination
+- [#2517](https://github.com/poanetwork/blockscout/pull/2517) - remove duplicate indexes
+- [#2515](https://github.com/poanetwork/blockscout/pull/2515) - do not aggregate NFT token transfers
+- [#2514](https://github.com/poanetwork/blockscout/pull/2514) - Isolating of staking dapp css && extracting of non-critical css
+- [#2512](https://github.com/poanetwork/blockscout/pull/2512) - alert link fix
+- [#2509](https://github.com/poanetwork/blockscout/pull/2509) - value-ticker gaps fix
+- [#2508](https://github.com/poanetwork/blockscout/pull/2508) - logs view columns fix
+- [#2506](https://github.com/poanetwork/blockscout/pull/2506) - fix two active tab in the top menu
+- [#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
- [#2459](https://github.com/poanetwork/blockscout/pull/2459) - fix top addresses query
- [#2425](https://github.com/poanetwork/blockscout/pull/2425) - Force to show address view for checksummed address even if it is not in DB
+- [#2551](https://github.com/poanetwork/blockscout/pull/2551) - Correctly handle dynamically created Bootstrap tooltips
### Chore
+- [#2554](https://github.com/poanetwork/blockscout/pull/2554) - remove extra slash for endpoint url in docs
+- [#2552](https://github.com/poanetwork/blockscout/pull/2552) - remove brackets for token holders percentage
+- [#2507](https://github.com/poanetwork/blockscout/pull/2507) - update minor version of ecto, ex_machina, phoenix_live_reload
+- [#2516](https://github.com/poanetwork/blockscout/pull/2516) - update absinthe plug from fork
+- [#2473](https://github.com/poanetwork/blockscout/pull/2473) - get rid of cldr warnings
- [#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
+- [#2373](https://github.com/poanetwork/blockscout/pull/2373) - Add script to validate internal_transactions constraint for large DBs
+
## 2.0.2-beta
@@ -164,6 +223,7 @@
- [#2255](https://github.com/poanetwork/blockscout/pull/2255) - upgrade elixir version to 1.9.0
- [#2256](https://github.com/poanetwork/blockscout/pull/2256) - use the latest version of chromedriver
+
## 2.0.0-beta
### Features
@@ -246,6 +306,7 @@
- [#2055](https://github.com/poanetwork/blockscout/pull/2055) - Increase timeout for geth indexers
- [#2069](https://github.com/poanetwork/blockscout/pull/2069) - Docsify integration: static docs page generation
+
## 1.3.15-beta
### Features
@@ -275,6 +336,7 @@
- [#1892](https://github.com/poanetwork/blockscout/pull/1892) - Remove temporary worker modules
+
## 1.3.13-beta
### Features
@@ -287,10 +349,12 @@
- [#1881](https://github.com/poanetwork/blockscout/pull/1881) - fix: store solc versions locally for performance
- [#1898](https://github.com/poanetwork/blockscout/pull/1898) - check if the constructor has arguments before verifying constructor arguments
+
## 1.3.12-beta
Reverting of synchronous block counter, implemented in #1848
+
## 1.3.11-beta
### Features
@@ -312,6 +376,7 @@ Reverting of synchronous block counter, implemented in #1848
- [#1814](https://github.com/poanetwork/blockscout/pull/1814) - Clear build artefacts script
- [#1837](https://github.com/poanetwork/blockscout/pull/1837) - Add -f flag to clear_build.sh script delete static folder
+
## 1.3.10-beta
### Features
@@ -422,6 +487,7 @@ Reverting of synchronous block counter, implemented in #1848
- [#1610](https://github.com/poanetwork/blockscout/pull/1610) - Add PIRL to Readme
+
## 1.3.6-beta
### Features
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
index 4de2ef6baf..3075397e8d 100644
--- a/ISSUE_TEMPLATE.md
+++ b/ISSUE_TEMPLATE.md
@@ -4,6 +4,7 @@
* Elixir & Erlang/OTP versions (`elixir -version`):
* Operating System:
+* Blockscout Version/branch:
### Steps to reproduce
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
index 09dbdd6b2f..4f889b7304 100644
--- a/PULL_REQUEST_TEMPLATE.md
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -35,4 +35,5 @@
- [ ] If I added new functionality, I added tests covering it.
- [ ] If I fixed a bug, I added a regression test to prevent the bug from silently reappearing again.
- [ ] I checked whether I should update the docs and did so if necessary
- - [ ] If I added/changed/removed ENV var, I should update the list of env vars in https://github.com/poanetwork/blockscout/blob/master/docs/env-variables.md to reflect changes in the table here https://poanetwork.github.io/blockscout/#/env-variables?id=blockscout-env-variables
+ - [ ] If I added/changed/removed ENV var, I should update the list of env vars in https://github.com/poanetwork/blockscout/blob/master/docs/env-variables.md to reflect changes in the table here https://poanetwork.github.io/blockscout/#/env-variables?id=blockscout-env-variables. I've set `master` in the `Version` column.
+ - [ ] If I add new indices into DB, I checked, that they don't redundant with PGHero or other tools
diff --git a/apps/block_scout_web/.sobelow-conf b/apps/block_scout_web/.sobelow-conf
index 73cbb5ae98..51d8b758cb 100644
--- a/apps/block_scout_web/.sobelow-conf
+++ b/apps/block_scout_web/.sobelow-conf
@@ -5,5 +5,5 @@
router: "lib/block_scout_web/router.ex",
exit: "low",
format: "compact",
- ignore: ["Config.Headers"]
+ ignore: ["Config.Headers", "Config.CSWH"]
]
diff --git a/apps/block_scout_web/assets/__tests__/lib/async_listing_load.js b/apps/block_scout_web/assets/__tests__/lib/async_listing_load.js
index 6e86d8a6b5..adae2d6033 100644
--- a/apps/block_scout_web/assets/__tests__/lib/async_listing_load.js
+++ b/apps/block_scout_web/assets/__tests__/lib/async_listing_load.js
@@ -46,14 +46,12 @@ describe('REQUEST_ERROR', () => {
describe('FINISH_REQUEST', () => {
test('sets loading status to false', () => {
const state = Object.assign({}, asyncInitialState, {
- loading: true,
- loadingFirstPage: true
+ loading: true
})
const action = { type: 'FINISH_REQUEST' }
const output = asyncReducer(state, action)
expect(output.loading).toEqual(false)
- expect(output.loadingFirstPage).toEqual(false)
})
})
diff --git a/apps/block_scout_web/assets/css/_images-preload.scss b/apps/block_scout_web/assets/css/_images-preload.scss
new file mode 100644
index 0000000000..8356a8f90e
--- /dev/null
+++ b/apps/block_scout_web/assets/css/_images-preload.scss
@@ -0,0 +1,15 @@
+body:after {
+ position:absolute; width:0; height:0; overflow:hidden; z-index:-1;
+ content:
+ url(/images/network-selector-icons/callisto-mainnet.png)
+ url(/images/network-selector-icons/ethereum-mainnet.png)
+ url(/images/network-selector-icons/ethereum-classic.png)
+ url(/images/network-selector-icons/goerli-testnet.png)
+ url(/images/network-selector-icons/kovan-testnet.png)
+ url(/images/network-selector-icons/poa-core.png)
+ url(/images/network-selector-icons/poa-sokol.png)
+ url(/images/network-selector-icons/rinkeby-testnet.png)
+ url(/images/network-selector-icons/rsk-mainnet.png)
+ url(/images/network-selector-icons/ropsten-testnet.png)
+ url(/images/network-selector-icons/xdai-chain.png)
+};
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/app.scss b/apps/block_scout_web/assets/css/app.scss
index e91df91740..508bb5335b 100644
--- a/apps/block_scout_web/assets/css/app.scss
+++ b/apps/block_scout_web/assets/css/app.scss
@@ -24,7 +24,6 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
@import "node_modules/bootstrap/scss/reboot";
@import "node_modules/bootstrap/scss/grid";
@import "node_modules/bootstrap/scss/code";
-@import "node_modules/bootstrap/scss/modal";
@import "node_modules/bootstrap/scss/close";
@import "node_modules/bootstrap/scss/buttons";
@import "node_modules/bootstrap/scss/forms";
@@ -50,7 +49,6 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
@import "node_modules/bootstrap/scss/navbar";
@import "node_modules/bootstrap/scss/pagination";
@import "node_modules/bootstrap/scss/tables";
-@import "node_modules/bootstrap/scss/tooltip";
@import "node_modules/bootstrap/scss/transitions";
// Code highlight
@@ -62,6 +60,7 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
// Custom SCSS
@import "layout";
@import "typography";
+@import "images-preload";
@import "code";
@import "helpers";
@import "elements";
@@ -75,8 +74,8 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
@import "components/filter";
@import "components/button";
@import "components/table";
-@import "components/qr-code";
@import "components/navbar";
+@import "components/alerts";
@import "components/animations";
@import "components/card";
@import "components/tile";
@@ -93,41 +92,28 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
@import "components/transaction-input";
@import "components/coin-balance-tile";
@import "components/highlight";
-@import "components/copy_icon";
@import "components/btn_full";
@import "components/btn_line";
-@import "components/stakes";
@import "components/check";
+@import "components/stakes_variables";
@import "components/stakes_table";
-@import "components/i_tooltip";
-@import "components/check_tooltip";
-@import "components/tooltip";
-@import "components/progress_from_to";
-@import "components/stakes_empty_content";
-@import "components/stakes_btn_remove_pool";
-@import "components/modal";
-@import "components/modal_validator_info";
@import "components/form";
-@import "components/stakes_progress";
-@import "components/modal_status";
-@import "components/modal_bottom_disclaimer";
-@import "components/modal_become_candidate";
-@import "components/modal_stake";
@import "components/btn_copy";
@import "components/btn_qr";
@import "components/btn_address_card";
@import "components/btn_dropdown_line";
@import "components/transaction";
@import "components/api";
-@import "components/alerts";
@import "components/verify_other_explorers";
@import "components/errors";
@import "components/log-search";
@import "components/radio";
+@import "components/modal_variables";
@import "components/network-selector";
@import "components/new_smart_contract";
@import "components/radio_big";
@import "components/btn_no_border";
+
@import "theme/dark-theme";
:export {
diff --git a/apps/block_scout_web/assets/css/components/_api.scss b/apps/block_scout_web/assets/css/components/_api.scss
index 820e1e8f01..6f46956780 100644
--- a/apps/block_scout_web/assets/css/components/_api.scss
+++ b/apps/block_scout_web/assets/css/components/_api.scss
@@ -116,6 +116,10 @@ $api-doc-list-item-view-more-color: $api-doc-list-item-title-color !default;
margin: 0;
}
+.api-doc-list-item-description {
+ width: 100%
+}
+
.api-doc-list-item-controls {
display: flex;
flex-direction: column;
diff --git a/apps/block_scout_web/assets/css/components/_button.scss b/apps/block_scout_web/assets/css/components/_button.scss
index d6a9e7b020..53bca7a83a 100644
--- a/apps/block_scout_web/assets/css/components/_button.scss
+++ b/apps/block_scout_web/assets/css/components/_button.scss
@@ -23,6 +23,7 @@ $button-secondary-color: $secondary !default;
background-color: darken($button-primary-color, 10%);
border-color: darken($button-primary-color, 10%);
color: #fff;
+ outline: none !important;
text-decoration: none;
}
diff --git a/apps/block_scout_web/assets/css/components/_form.scss b/apps/block_scout_web/assets/css/components/_form.scss
index 5346afcb7c..4b13065a05 100644
--- a/apps/block_scout_web/assets/css/components/_form.scss
+++ b/apps/block_scout_web/assets/css/components/_form.scss
@@ -9,6 +9,11 @@ $form-control-border-color: #e2e5ec !default;
border-radius: 4px;
}
+ &:focus {
+ border-color: $secondary;
+ box-shadow: none;
+ }
+
&.n-b-r {
border-right: none;
}
diff --git a/apps/block_scout_web/assets/css/components/_modal.scss b/apps/block_scout_web/assets/css/components/_modal.scss
index bc9e16c875..4e70be687c 100644
--- a/apps/block_scout_web/assets/css/components/_modal.scss
+++ b/apps/block_scout_web/assets/css/components/_modal.scss
@@ -1,9 +1,3 @@
-$modal-overlay-color: rgba($primary, 0.9) !default;
-$modal-horizontal-padding: 30px !default;
-$modal-vertical-padding: 25px !default;
-$modal-border-radius: 10px !default;
-$modal-gray-background: #f6f7f9 !default;
-
.modal-backdrop {
background-color: $modal-overlay-color;
diff --git a/apps/block_scout_web/assets/css/components/_modal_variables.scss b/apps/block_scout_web/assets/css/components/_modal_variables.scss
new file mode 100644
index 0000000000..92e2a94a33
--- /dev/null
+++ b/apps/block_scout_web/assets/css/components/_modal_variables.scss
@@ -0,0 +1,5 @@
+$modal-overlay-color: rgba($primary, 0.9) !default;
+$modal-horizontal-padding: 30px !default;
+$modal-vertical-padding: 25px !default;
+$modal-border-radius: 10px !default;
+$modal-gray-background: #f6f7f9 !default;
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/components/_network-selector.scss b/apps/block_scout_web/assets/css/components/_network-selector.scss
index e8b5f72e78..7f2a4c020d 100644
--- a/apps/block_scout_web/assets/css/components/_network-selector.scss
+++ b/apps/block_scout_web/assets/css/components/_network-selector.scss
@@ -243,6 +243,40 @@ $network-selector-item-icon-dimensions: 30px !default;
height: $network-selector-item-icon-dimensions;
margin: 0 15px 0 0;
width: $network-selector-item-icon-dimensions;
+
+ &-callisto-mainnet {
+ background-image: url(/images/network-selector-icons/callisto-mainnet.png)
+ }
+ &-ethereum-mainnet {
+ background-image: url(/images/network-selector-icons/ethereum-mainnet.png)
+ }
+ &-ethereum-classic {
+ background-image: url(/images/network-selector-icons/ethereum-classic.png)
+ }
+ &-goerli-testnet {
+ background-image: url(/images/network-selector-icons/goerli-testnet.png)
+ }
+ &-kovan-testnet {
+ background-image: url(/images/network-selector-icons/kovan-testnet.png)
+ }
+ &-poa-core {
+ background-image: url(/images/network-selector-icons/poa-core.png)
+ }
+ &-poa-sokol {
+ background-image: url(/images/network-selector-icons/poa-sokol.png)
+ }
+ &-rinkeby-testnet {
+ background-image: url(/images/network-selector-icons/rinkeby-testnet.png)
+ }
+ &-rsk-mainnet {
+ background-image: url(/images/network-selector-icons/rsk-mainnet.png)
+ }
+ &-ropsten-testnet {
+ background-image: url(/images/network-selector-icons/ropsten-testnet.png)
+ }
+ &-xdai-chain {
+ background-image: url(/images/network-selector-icons/xdai-chain.png)
+ }
}
.network-selector-item-title {
diff --git a/apps/block_scout_web/assets/css/components/_stakes_variables.scss b/apps/block_scout_web/assets/css/components/_stakes_variables.scss
new file mode 100644
index 0000000000..4c23ea421b
--- /dev/null
+++ b/apps/block_scout_web/assets/css/components/_stakes_variables.scss
@@ -0,0 +1,2 @@
+$stakes-banned-background: #fff3f7 !default;
+$stakes-banned-color: #ff7986 !default;
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/components/_copy_icon.scss b/apps/block_scout_web/assets/css/components/stakes/_copy_icon.scss
similarity index 100%
rename from apps/block_scout_web/assets/css/components/_copy_icon.scss
rename to apps/block_scout_web/assets/css/components/stakes/_copy_icon.scss
diff --git a/apps/block_scout_web/assets/css/components/_modal_become_candidate.scss b/apps/block_scout_web/assets/css/components/stakes/_modal_become_candidate.scss
similarity index 100%
rename from apps/block_scout_web/assets/css/components/_modal_become_candidate.scss
rename to apps/block_scout_web/assets/css/components/stakes/_modal_become_candidate.scss
diff --git a/apps/block_scout_web/assets/css/components/_modal_bottom_disclaimer.scss b/apps/block_scout_web/assets/css/components/stakes/_modal_bottom_disclaimer.scss
similarity index 100%
rename from apps/block_scout_web/assets/css/components/_modal_bottom_disclaimer.scss
rename to apps/block_scout_web/assets/css/components/stakes/_modal_bottom_disclaimer.scss
diff --git a/apps/block_scout_web/assets/css/components/_modal_stake.scss b/apps/block_scout_web/assets/css/components/stakes/_modal_stake.scss
similarity index 100%
rename from apps/block_scout_web/assets/css/components/_modal_stake.scss
rename to apps/block_scout_web/assets/css/components/stakes/_modal_stake.scss
diff --git a/apps/block_scout_web/assets/css/components/_modal_validator_info.scss b/apps/block_scout_web/assets/css/components/stakes/_modal_validator_info.scss
similarity index 100%
rename from apps/block_scout_web/assets/css/components/_modal_validator_info.scss
rename to apps/block_scout_web/assets/css/components/stakes/_modal_validator_info.scss
diff --git a/apps/block_scout_web/assets/css/components/_progress_from_to.scss b/apps/block_scout_web/assets/css/components/stakes/_progress_from_to.scss
similarity index 100%
rename from apps/block_scout_web/assets/css/components/_progress_from_to.scss
rename to apps/block_scout_web/assets/css/components/stakes/_progress_from_to.scss
diff --git a/apps/block_scout_web/assets/css/components/_stakes.scss b/apps/block_scout_web/assets/css/components/stakes/_stakes.scss
similarity index 96%
rename from apps/block_scout_web/assets/css/components/_stakes.scss
rename to apps/block_scout_web/assets/css/components/stakes/_stakes.scss
index 87271bc983..c6335ec066 100644
--- a/apps/block_scout_web/assets/css/components/_stakes.scss
+++ b/apps/block_scout_web/assets/css/components/stakes/_stakes.scss
@@ -1,8 +1,6 @@
$stakes-dashboard-copy-icon-color: $copy-icon-color !default;
$stakes-address-color: $primary !default;
$stakes-control-color: $primary !default;
-$stakes-banned-color: #ff7986 !default;
-$stakes-banned-background: #fff3f7 !default;
$stakes-stats-item-color: #fff !default;
$stakes-stats-item-border-color: #fff !default;
diff --git a/apps/block_scout_web/assets/css/components/_stakes_btn_remove_pool.scss b/apps/block_scout_web/assets/css/components/stakes/_stakes_btn_remove_pool.scss
similarity index 100%
rename from apps/block_scout_web/assets/css/components/_stakes_btn_remove_pool.scss
rename to apps/block_scout_web/assets/css/components/stakes/_stakes_btn_remove_pool.scss
diff --git a/apps/block_scout_web/assets/css/components/_stakes_empty_content.scss b/apps/block_scout_web/assets/css/components/stakes/_stakes_empty_content.scss
similarity index 100%
rename from apps/block_scout_web/assets/css/components/_stakes_empty_content.scss
rename to apps/block_scout_web/assets/css/components/stakes/_stakes_empty_content.scss
diff --git a/apps/block_scout_web/assets/css/components/_stakes_progress.scss b/apps/block_scout_web/assets/css/components/stakes/_stakes_progress.scss
similarity index 100%
rename from apps/block_scout_web/assets/css/components/_stakes_progress.scss
rename to apps/block_scout_web/assets/css/components/stakes/_stakes_progress.scss
diff --git a/apps/block_scout_web/assets/css/non-critical.scss b/apps/block_scout_web/assets/css/non-critical.scss
new file mode 100644
index 0000000000..826f349d31
--- /dev/null
+++ b/apps/block_scout_web/assets/css/non-critical.scss
@@ -0,0 +1,17 @@
+// Bootstrap Core CSS
+@import "node_modules/bootstrap/scss/functions";
+@import "node_modules/bootstrap/scss/mixins";
+
+@import "theme/variables-non-critical";
+
+@import "node_modules/bootstrap/scss/modal";
+@import "node_modules/bootstrap/scss/tooltip";
+
+@import "components/i_tooltip";
+@import "components/check_tooltip";
+@import "components/tooltip";
+
+@import "components/qr-code";
+@import "components/modal_variables";
+@import "components/modal";
+@import "components/modal_status";
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/stakes.scss b/apps/block_scout_web/assets/css/stakes.scss
new file mode 100644
index 0000000000..77ada07b46
--- /dev/null
+++ b/apps/block_scout_web/assets/css/stakes.scss
@@ -0,0 +1,20 @@
+@import "./mixins";
+
+// Bootstrap Core CSS
+@import "node_modules/bootstrap/scss/functions";
+@import "node_modules/bootstrap/scss/mixins";
+
+@import "theme/variables";
+
+@import "components/stakes_variables";
+@import "components/stakes/copy_icon";
+@import "components/stakes/stakes";
+@import "components/stakes/progress_from_to";
+@import "components/stakes/stakes_empty_content";
+@import "components/stakes/stakes_btn_remove_pool";
+@import "components/modal_variables";
+@import "components/stakes/stakes_progress";
+@import "components/stakes/modal_stake";
+@import "components/stakes/modal_become_candidate";
+@import "components/stakes/modal_validator_info";
+@import "components/stakes/modal_bottom_disclaimer";
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_base_variables.scss b/apps/block_scout_web/assets/css/theme/_base_variables.scss
index 2238327bd4..b4cf443f05 100644
--- a/apps/block_scout_web/assets/css/theme/_base_variables.scss
+++ b/apps/block_scout_web/assets/css/theme/_base_variables.scss
@@ -359,7 +359,7 @@ $input-btn-padding-y: 0.375rem !default;
$input-btn-padding-x: 0.75rem !default;
$input-btn-line-height: $line-height-base !default;
-$input-btn-focus-width: 0.2rem !default;
+$input-btn-focus-width: 1px !default;
$input-btn-focus-color: rgba($component-active-bg, 0.25) !default;
$input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color !default;
diff --git a/apps/block_scout_web/assets/css/theme/_dai_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_dai_variables-non-critical.scss
new file mode 100644
index 0000000000..d9f670f980
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_dai_variables-non-critical.scss
@@ -0,0 +1,7 @@
+// general
+$primary: #17314f;
+$secondary: #15bba6;
+$tertiary: #93d7ff;
+$additional-font: #fff;
+
+$btn-line-color: $secondary; // button border and font color && hover bg color
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_dai_variables.scss b/apps/block_scout_web/assets/css/theme/_dai_variables.scss
index 0c09660240..6722369c7f 100644
--- a/apps/block_scout_web/assets/css/theme/_dai_variables.scss
+++ b/apps/block_scout_web/assets/css/theme/_dai_variables.scss
@@ -38,6 +38,7 @@ $btn-line-bg: #fff; // button bg
$btn-line-color: $secondary; // button border and font color && hover bg color
$btn-copy-color: $secondary; // btn copy
$btn-qr-color: $secondary; // btn qr-code
+$btn-address-card-icon-color: $secondary; // btn address color
//links & tile
$tile-body-a-color: $secondary;
diff --git a/apps/block_scout_web/assets/css/theme/_dark-theme.scss b/apps/block_scout_web/assets/css/theme/_dark-theme.scss
index c0adb0a84b..dd1ee83d96 100644
--- a/apps/block_scout_web/assets/css/theme/_dark-theme.scss
+++ b/apps/block_scout_web/assets/css/theme/_dark-theme.scss
@@ -234,7 +234,7 @@ $labels-dark: #8a8dba; // header nav, labels
}
}
- .btn-copy-icon, .btn-qr-icon {
+ .btn-copy-icon, .btn-qr-icon, .btn-address-card-icon {
border-color: $dark-primary;
path {
fill: $dark-primary;
@@ -578,9 +578,41 @@ $labels-dark: #8a8dba; // header nav, labels
}
}
}
- .verify-other-explorers-cell {
- .exp-logo {
- color: #333 !important;
+
+ #explorersModal {
+ .modal-title {
+ color: #fff;
+ }
+
+ .text-muted {
+ color: $labels-dark;
+ }
+
+ .modal-footer {
+ border-top-color: darken($labels-dark, 30);
+ }
+
+ .modal-content {
+ background-color: $dark-light-bg;
+
+ .btn-primary {
+ background-color: $dark-primary;
+ border-color: $dark-primary;
+ &:hover {
+ background-color: $dark-primary;
+ border-color: $dark-primary;
+ }
+ }
+ }
+
+ .verify-other-explorers-cell {
+ .exp-logo {
+ color: #fff;
+ }
+ }
+
+ .close {
+ color: #fff;
}
}
@@ -680,4 +712,70 @@ $labels-dark: #8a8dba; // header nav, labels
color: #3f436b !important;
border-right-color: #3f436b !important;
}
+
+ // 'text dark' label
+ .text-dark {
+ color: #fff;
+ }
+
+ // validator info
+ #validatorModal {
+ .modal-title {
+ color: #fff;
+ }
+
+ .text-muted {
+ color: $labels-dark;
+ }
+
+ .modal-footer {
+ border-top-color: darken($labels-dark, 30);
+ }
+
+ .modal-content {
+ background-color: $dark-light-bg;
+
+ .btn-primary {
+ background-color: $dark-primary;
+ border-color: $dark-primary;
+ &:hover {
+ background-color: $dark-primary;
+ border-color: $dark-primary;
+ }
+ }
+ }
+
+ .close {
+ color: #fff;
+ }
+ }
+
+ // alerts
+ .alert-link {
+ color: $labels-dark;
+ }
+
+ .alert-danger {
+ background-color: $dark-light;
+ border-color: $dark-light;
+ .alert-link {
+ color: $alert-danger-color;
+ }
+ }
+
+ .tile .alert {
+ background: rgba(#000, .1);
+ }
+
+ // primary buttons
+ .btn-full-primary, .button-primary {
+ background: $dark-primary;
+ border-color: $dark-primary;
+ color: #fff;
+ &:hover {
+ background: darken($dark-primary, 6);
+ border-color: darken($dark-primary, 6);
+ color: #fff;
+ }
+ }
}
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_ether1_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_ether1_variables-non-critical.scss
new file mode 100644
index 0000000000..f7bf90f4fd
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_ether1_variables-non-critical.scss
@@ -0,0 +1,7 @@
+// general
+$primary: #840032;
+$secondary: #343434;
+$tertiary: #7f7f7f;
+$additional-font: #ff95db;
+
+$btn-line-color: #4b021e; // button border and font color && hover bg color
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables-non-critical.scss
new file mode 100644
index 0000000000..bc716596bf
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables-non-critical.scss
@@ -0,0 +1,7 @@
+// general
+$primary: #1c1c3d;
+$secondary: #4ad7a7;
+$tertiary: #5959d8;
+$additional-font: #bdbdff;
+
+$btn-line-color: $tertiary; // button border and font color && hover bg color
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables.scss b/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables.scss
index dff677d7cc..6b8d6d7f96 100644
--- a/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables.scss
+++ b/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables.scss
@@ -34,6 +34,7 @@ $btn-line-bg: #fff; // button bg
$btn-line-color: $tertiary; // button border and font color && hover bg color
$btn-copy-color: $tertiary; // btn copy
$btn-qr-color: $tertiary; // btn qr-code
+$btn-address-card-icon-color: $tertiary; // btn address color
//links & tile
$tile-body-a-color: $tertiary;
diff --git a/apps/block_scout_web/assets/css/theme/_ethereum_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_ethereum_variables-non-critical.scss
new file mode 100644
index 0000000000..160c0cb684
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_ethereum_variables-non-critical.scss
@@ -0,0 +1,7 @@
+// general
+$primary: #153550;
+$secondary: #49a2ee;
+$tertiary: #4ad7a7;
+$additional-font: #89cae6;
+
+$btn-line-color: $secondary; // button border and font color && hover bg color
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_ethereum_variables.scss b/apps/block_scout_web/assets/css/theme/_ethereum_variables.scss
index 0b5cac0c43..a06e0be93e 100644
--- a/apps/block_scout_web/assets/css/theme/_ethereum_variables.scss
+++ b/apps/block_scout_web/assets/css/theme/_ethereum_variables.scss
@@ -37,6 +37,7 @@ $btn-line-bg: #fff; // button bg
$btn-line-color: $secondary; // button border and font color && hover bg color
$btn-copy-color: $secondary; // btn copy
$btn-qr-color: $secondary; // btn qr-code
+$btn-address-card-icon-color: $secondary; // btn address color
//links & tile
$tile-body-a-color: $secondary;
diff --git a/apps/block_scout_web/assets/css/theme/_goerli_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_goerli_variables-non-critical.scss
new file mode 100644
index 0000000000..70cfaddd73
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_goerli_variables-non-critical.scss
@@ -0,0 +1,8 @@
+// general
+$primary: #2b2b2b;
+$secondary: #eac247;
+$tertiary: #929292;
+$additional-font: #ffffff;
+$sub-accent-color: #a46f30;
+
+$btn-line-color: $sub-accent-color; // button border and font color && hover bg color
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_goerli_variables.scss b/apps/block_scout_web/assets/css/theme/_goerli_variables.scss
index 04f953b477..af57f2d1a2 100644
--- a/apps/block_scout_web/assets/css/theme/_goerli_variables.scss
+++ b/apps/block_scout_web/assets/css/theme/_goerli_variables.scss
@@ -43,6 +43,7 @@ $btn-line-bg: #fff; // button bg
$btn-line-color: $sub-accent-color; // button border and font color && hover bg color
$btn-copy-color: $sub-accent-color; // btn copy
$btn-qr-color: $sub-accent-color; // btn qr-code
+$btn-address-card-icon-color: $sub-accent-color; // btn address color
//links & tile
$tile-body-a-color: $sub-accent-color;
diff --git a/apps/block_scout_web/assets/css/theme/_kovan_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_kovan_variables-non-critical.scss
new file mode 100644
index 0000000000..ab4568e14f
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_kovan_variables-non-critical.scss
@@ -0,0 +1,7 @@
+// general
+$primary: #101f25;
+$secondary: #35e3d8;
+$tertiary: #1f857f;
+$additional-font: #99fff9;
+
+$btn-line-color: $tertiary; // button border and font color && hover bg color
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_kovan_variables.scss b/apps/block_scout_web/assets/css/theme/_kovan_variables.scss
index a4edc629d0..b47c3de0b2 100644
--- a/apps/block_scout_web/assets/css/theme/_kovan_variables.scss
+++ b/apps/block_scout_web/assets/css/theme/_kovan_variables.scss
@@ -38,6 +38,7 @@ $btn-line-bg: #fff; // button bg
$btn-line-color: $tertiary; // button border and font color && hover bg color
$btn-copy-color: $tertiary; // btn copy
$btn-qr-color: $tertiary; // btn qr-code
+$btn-address-card-icon-color: $tertiary; // btn address color
//links & tile
$tile-body-a-color: $tertiary;
diff --git a/apps/block_scout_web/assets/css/theme/_lukso_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_lukso_variables-non-critical.scss
new file mode 100644
index 0000000000..8a8d7e5eaf
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_lukso_variables-non-critical.scss
@@ -0,0 +1,7 @@
+// general
+$primary: #1d3154;
+$secondary: #fdcec4;
+$tertiary: #a96c55;
+$additional-font: #a1ded1;
+
+$btn-line-color: $primary; // button border and font color && hover bg color
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_lukso_variables.scss b/apps/block_scout_web/assets/css/theme/_lukso_variables.scss
index a7b608bfa8..abfcc05472 100644
--- a/apps/block_scout_web/assets/css/theme/_lukso_variables.scss
+++ b/apps/block_scout_web/assets/css/theme/_lukso_variables.scss
@@ -44,6 +44,7 @@ $btn-line-bg: #fff; // button bg
$btn-line-color: $primary; // button border and font color && hover bg color
$btn-copy-color: $primary; // btn copy
$btn-qr-color: $primary; // btn qr-code
+$btn-address-card-icon-color: $primary; // btn address color
// card
$card-background-1: $primary;
diff --git a/apps/block_scout_web/assets/css/theme/_neutral_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_neutral_variables-non-critical.scss
new file mode 100644
index 0000000000..3d85713af9
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_neutral_variables-non-critical.scss
@@ -0,0 +1,7 @@
+// general
+$primary: #5c34a2;
+$secondary: #87e1a9;
+$tertiary: #bf9cff;
+$additional-font: #fff;
+
+$btn-line-color: $primary; // button border and font color && hover bg color
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_neutral_variables.scss b/apps/block_scout_web/assets/css/theme/_neutral_variables.scss
index 120a11c222..536ecdc105 100644
--- a/apps/block_scout_web/assets/css/theme/_neutral_variables.scss
+++ b/apps/block_scout_web/assets/css/theme/_neutral_variables.scss
@@ -40,6 +40,7 @@ $btn-line-bg: #fff; // button bg
$btn-line-color: $primary; // button border and font color && hover bg color
$btn-copy-color: $primary; // btn copy
$btn-qr-color: $primary; // btn qr-code
+$btn-address-card-icon-color: $primary; // btn address color
//links & tile
$tile-body-a-color: $primary;
diff --git a/apps/block_scout_web/assets/css/theme/_poa_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_poa_variables-non-critical.scss
new file mode 100644
index 0000000000..3d85713af9
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_poa_variables-non-critical.scss
@@ -0,0 +1,7 @@
+// general
+$primary: #5c34a2;
+$secondary: #87e1a9;
+$tertiary: #bf9cff;
+$additional-font: #fff;
+
+$btn-line-color: $primary; // button border and font color && hover bg color
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_poa_variables.scss b/apps/block_scout_web/assets/css/theme/_poa_variables.scss
index 120a11c222..536ecdc105 100644
--- a/apps/block_scout_web/assets/css/theme/_poa_variables.scss
+++ b/apps/block_scout_web/assets/css/theme/_poa_variables.scss
@@ -40,6 +40,7 @@ $btn-line-bg: #fff; // button bg
$btn-line-color: $primary; // button border and font color && hover bg color
$btn-copy-color: $primary; // btn copy
$btn-qr-color: $primary; // btn qr-code
+$btn-address-card-icon-color: $primary; // btn address color
//links & tile
$tile-body-a-color: $primary;
diff --git a/apps/block_scout_web/assets/css/theme/_rinkeby_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_rinkeby_variables-non-critical.scss
new file mode 100644
index 0000000000..823da9f2d6
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_rinkeby_variables-non-critical.scss
@@ -0,0 +1,7 @@
+// general
+$primary: #153550;
+$secondary: #38a9f5;
+$tertiary: #76f1ff;
+$additional-font: #89cae6;
+
+$btn-line-color: $secondary; // button border and font color && hover bg color
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_rinkeby_variables.scss b/apps/block_scout_web/assets/css/theme/_rinkeby_variables.scss
index 4fa700c50e..21388bb315 100644
--- a/apps/block_scout_web/assets/css/theme/_rinkeby_variables.scss
+++ b/apps/block_scout_web/assets/css/theme/_rinkeby_variables.scss
@@ -37,6 +37,7 @@ $btn-line-bg: #fff; // button bg
$btn-line-color: $secondary; // button border and font color && hover bg color
$btn-copy-color: $secondary; // btn copy
$btn-qr-color: $secondary; // btn qr-code
+$btn-address-card-icon-color: $secondary; // btn address color
//links & tile
$tile-body-a-color: $secondary;
diff --git a/apps/block_scout_web/assets/css/theme/_ropsten_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_ropsten_variables-non-critical.scss
new file mode 100644
index 0000000000..823da9f2d6
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_ropsten_variables-non-critical.scss
@@ -0,0 +1,7 @@
+// general
+$primary: #153550;
+$secondary: #38a9f5;
+$tertiary: #76f1ff;
+$additional-font: #89cae6;
+
+$btn-line-color: $secondary; // button border and font color && hover bg color
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_ropsten_variables.scss b/apps/block_scout_web/assets/css/theme/_ropsten_variables.scss
index 4fa700c50e..21388bb315 100644
--- a/apps/block_scout_web/assets/css/theme/_ropsten_variables.scss
+++ b/apps/block_scout_web/assets/css/theme/_ropsten_variables.scss
@@ -37,6 +37,7 @@ $btn-line-bg: #fff; // button bg
$btn-line-color: $secondary; // button border and font color && hover bg color
$btn-copy-color: $secondary; // btn copy
$btn-qr-color: $secondary; // btn qr-code
+$btn-address-card-icon-color: $secondary; // btn address color
//links & tile
$tile-body-a-color: $secondary;
diff --git a/apps/block_scout_web/assets/css/theme/_rsk_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_rsk_variables-non-critical.scss
new file mode 100644
index 0000000000..c874423aa6
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_rsk_variables-non-critical.scss
@@ -0,0 +1,7 @@
+// general
+$primary: #101f25;
+$secondary: #27ac8d;
+$tertiary: #e39a54;
+$additional-font: #a1ded1;
+
+$btn-line-color: $secondary; // button border and font color && hover bg color
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_rsk_variables.scss b/apps/block_scout_web/assets/css/theme/_rsk_variables.scss
index d33996a89e..8d3bff31d2 100644
--- a/apps/block_scout_web/assets/css/theme/_rsk_variables.scss
+++ b/apps/block_scout_web/assets/css/theme/_rsk_variables.scss
@@ -38,6 +38,7 @@ $btn-line-bg: #fff; // button bg
$btn-line-color: $secondary; // button border and font color && hover bg color
$btn-copy-color: $secondary; // btn copy
$btn-qr-color: $secondary; // btn qr-code
+$btn-address-card-icon-color: $secondary; // btn address color
// card
$card-background-1: $secondary;
diff --git a/apps/block_scout_web/assets/css/theme/_sokol_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_sokol_variables-non-critical.scss
new file mode 100644
index 0000000000..64fd3c9f9c
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_sokol_variables-non-critical.scss
@@ -0,0 +1,8 @@
+// general
+$primary: #093731;
+$secondary: #40bfb2;
+$tertiary: #25c9ff;
+$additional-font: #93e8dd;
+$sub-accent-color: #1c9f90;
+
+$btn-line-color: $sub-accent-color; // button border and font color && hover bg color
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/css/theme/_sokol_variables.scss b/apps/block_scout_web/assets/css/theme/_sokol_variables.scss
index 8d479f397d..2860342f1e 100644
--- a/apps/block_scout_web/assets/css/theme/_sokol_variables.scss
+++ b/apps/block_scout_web/assets/css/theme/_sokol_variables.scss
@@ -49,6 +49,7 @@ $btn-line-bg: #fff; // button bg
$btn-line-color: $sub-accent-color; // button border and font color && hover bg color
$btn-copy-color: $sub-accent-color; // btn copy
$btn-qr-color: $sub-accent-color; // btn qr-code
+$btn-address-card-icon-color: $sub-accent-color; // btn address color
//links & tile
$tile-body-a-color: $sub-accent-color;
diff --git a/apps/block_scout_web/assets/css/theme/_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_variables-non-critical.scss
new file mode 100644
index 0000000000..232b9cc647
--- /dev/null
+++ b/apps/block_scout_web/assets/css/theme/_variables-non-critical.scss
@@ -0,0 +1,22 @@
+@import "theme/base_variables";
+@import "neutral_variables-non-critical";
+// @import "dai_variables-non-critical";
+// @import "ethereum_classic_variables-non-critical";
+// @import "ethereum_variables-non-critical";
+// @import "ether1_variables-non-critical";
+// @import "expanse_variables-non-critical";
+// @import "gochain_variables-non-critical";
+// @import "goerli_variables-non-critical";
+// @import "kovan_variables-non-critical";
+// @import "lukso_variables-non-critical";
+// @import "musicoin_variables-non-critical";
+// @import "pirl_variables-non-critical";
+// @import "poa_variables-non-critical";
+// @import "posdao_variables-non-critical";
+// @import "rinkeby_variables-non-critical";
+// @import "ropsten_variables-non-critical";
+// @import "social_variables-non-critical";
+// @import "sokol_variables-non-critical";
+// @import "tobalaba_variables-non-critical";
+// @import "tomochain_variables-non-critical";
+// @import "rsk_variables-non-critical";
\ No newline at end of file
diff --git a/apps/block_scout_web/assets/js/app.js b/apps/block_scout_web/assets/js/app.js
index 89059dd306..4aa89e4142 100644
--- a/apps/block_scout_web/assets/js/app.js
+++ b/apps/block_scout_web/assets/js/app.js
@@ -20,6 +20,9 @@ import 'bootstrap'
import './locale'
+// support of preload in Firefox
+import '../node_modules/fg-loadcss/dist/cssrelpreload.min'
+
import './pages/address'
import './pages/address/coin_balances'
import './pages/address/transactions'
@@ -59,5 +62,6 @@ import './lib/async_listing_load'
import './lib/tooltip'
import './lib/modals'
import './lib/try_api'
+import './lib/try_eth_api'
import './lib/card_tabs'
import './lib/network_selector'
diff --git a/apps/block_scout_web/assets/js/lib/async_listing_load.js b/apps/block_scout_web/assets/js/lib/async_listing_load.js
index 8477a7d728..13c4aa7da2 100644
--- a/apps/block_scout_web/assets/js/lib/async_listing_load.js
+++ b/apps/block_scout_web/assets/js/lib/async_listing_load.js
@@ -51,8 +51,6 @@ export const asyncInitialState = {
requestError: false,
/* if response has no items */
emptyResponse: false,
- /* if it is loading the first page */
- loadingFirstPage: true,
/* link to the next page */
nextPagePath: null,
/* link to the previous page */
@@ -80,8 +78,7 @@ export function asyncReducer (state = asyncInitialState, action) {
}
case 'FINISH_REQUEST': {
return Object.assign({}, state, {
- loading: false,
- loadingFirstPage: false
+ loading: false
})
}
case 'ITEMS_FETCHED': {
@@ -134,7 +131,7 @@ export const elements = {
},
'[data-async-listing] [data-loading-message]': {
render ($el, state) {
- if (state.loadingFirstPage) return $el.show()
+ if (state.loading) return $el.show()
$el.hide()
}
@@ -143,7 +140,7 @@ export const elements = {
render ($el, state) {
if (
!state.requestError &&
- (!state.loading || !state.loadingFirstPage) &&
+ (!state.loading) &&
state.items.length === 0
) {
return $el.show()
@@ -201,6 +198,25 @@ export const elements = {
$el.attr('href', state.prevPagePath)
}
},
+ '[data-async-listing] [data-first-page-button]': {
+ render ($el, state) {
+ if (state.pagesStack.length === 0) {
+ return $el.hide()
+ }
+ $el.show()
+ $el.attr('disabled', false)
+
+ const urlParams = new URLSearchParams(window.location.search)
+ const blockParam = urlParams.get('block_type')
+ const firstPageHref = window.location.href.split('?')[0]
+
+ if (blockParam !== null) {
+ $el.attr('href', firstPageHref + '?block_type=' + blockParam)
+ } else {
+ $el.attr('href', firstPageHref)
+ }
+ }
+ },
'[data-async-listing] [data-page-number]': {
render ($el, state) {
if (state.emptyResponse) {
@@ -216,7 +232,7 @@ export const elements = {
},
'[data-async-listing] [data-loading-button]': {
render ($el, state) {
- if (!state.loadingFirstPage && state.loading) return $el.show()
+ if (state.loading) return $el.show()
$el.hide()
}
diff --git a/apps/block_scout_web/assets/js/lib/awesomplete-util.js b/apps/block_scout_web/assets/js/lib/awesomplete-util.js
new file mode 100644
index 0000000000..5b37e1dd2f
--- /dev/null
+++ b/apps/block_scout_web/assets/js/lib/awesomplete-util.js
@@ -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('&', '&').replace('<', '<').replace('>', '>')
+ }
+
+ // 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 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, '$&')
+ }
+
+ // 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 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(/
/)
+ arr[0] = _mark(arr[0], input)
+ text = arr.join('
')
+ }
+ 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: '', value: '', label: '