diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000..5008ddfcf5 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitbook.yaml b/.gitbook.yaml new file mode 100644 index 0000000000..9af5f7d276 --- /dev/null +++ b/.gitbook.yaml @@ -0,0 +1,97 @@ +root: ./ + +redirects: + build-apps/references/bns: clarity/example-contracts/bns.md + build-apps/references/gaia: stacks-in-depth/gaia.md/README.md + docs/blockchain/stacks-blockchain-api: stacks-101/api.md + docs/clarity: clarity/overview.md + docs/clarity/crash-course: clarity/clarity-crash-course.md + docs/clarity/language-functions: clarity/functions.md + docs/clarity/language-keywords: clarity/keywords.md + docs/clarity/language-types: clarity/types.md + docs/clarity/noteworthy-contracts/bns-contract: clarity/example-contracts/bns.md + docs/clarity/noteworthy-contracts/stacking-contract: clarity/example-contracts/stacking.md + docs/clarity/noteworthy-contracts/xbtc: clarity/example-contracts/xbtc.md + docs/clarity/sample-contracts: clarity/example-contracts/audited-starter-contracts.md + docs/clarity/security: clarity/decidability.md + docs/clarity/security/decidable: clarity/decidability.md + docs/contribute: stacks-101/what-is-stacks.md + docs/contribute/docs: stacks-101/what-is-stacks.md + docs/contribute/translations: stacks-101/what-is-stacks.md + docs/cookbook: tutorials/hello-stacks-quickstart-tutorial.md + docs/cookbook/creating-an-ft: tutorials/hello-stacks-quickstart-tutorial.md + docs/cookbook/creating-an-nft: tutorials/hello-stacks-quickstart-tutorial.md + docs/cookbook/get-sats-per-stx: tutorials/hello-stacks-quickstart-tutorial.md + docs/cookbook/parse-a-btc-tx: tutorials/hello-stacks-quickstart-tutorial.md + docs/cookbook/post-conditions: tutorials/hello-stacks-quickstart-tutorial.md + docs/cookbook/sending-bitcoin-with-hiro-wallet: tutorials/hello-stacks-quickstart-tutorial.md + docs/cookbook/stacks-js-auth: tutorials/hello-stacks-quickstart-tutorial.md + docs/cookbook/stacks-js-sending-transactions: tutorials/hello-stacks-quickstart-tutorial.md + docs/cookbook/verifying-a-btc-tx-was-mined: tutorials/hello-stacks-quickstart-tutorial.md + docs/gaia: stacks-in-depth/gaia/README.md + docs/gaia/configuration: stacks-in-depth/gaia/configuration.md + docs/gaia/deploy-gaia-hub: stacks-in-depth/gaia/deploy-gaia-hub.md + docs/gaia/gaia-on-ec2: stacks-in-depth/gaia/gaia-on-ec2.md + docs/gaia/setup-linux: stacks-in-depth/gaia/setup-linux.md + docs/gaia/setup-mac: stacks-in-depth/gaia/setup-mac.md + docs/glossary: stacks-101/what-is-stacks.md + docs/intro: stacks-101/what-is-stacks.md + docs/nakamoto: nakamoto-upgrade/what-is-the-nakamoto-release.md + docs/nakamoto/bitcoin-mev-mitigation: nakamoto-upgrade/nakamoto-in-10-minutes.md + docs/nakamoto/block-production: nakamoto-upgrade/nakamoto-in-10-minutes.md + docs/nakamoto/neon: nakamoto-upgrade/testnets/neon.md + docs/nakamoto/signer: nakamoto-upgrade/running-a-signer.md + docs/nakamoto/stacking: nakamoto-upgrade/stacking-flow.md + docs/next-steps: stacks-101/what-is-stacks.md + docs/nodes-and-miners: stacks-in-depth/nodes-and-miners/README.md + docs/nodes-and-miners/digitalocean: stacks-in-depth/nodes-and-miners/run-a-node-with-digital-ocean.md + docs/nodes-and-miners/hosted-nodes: stacks-in-depth/nodes-and-miners/run-a-node-with-a-hosted-provider.md + docs/nodes-and-miners/miner-costs-and-fees: stacks-in-depth/nodes-and-miners/miner-costs-and-fees.md + docs/nodes-and-miners/miner-mainnet: stacks-in-depth/nodes-and-miners/mine-mainnet-stacks-tokens.md + docs/nodes-and-miners/miner-testnet: stacks-in-depth/nodes-and-miners/mine-testnet-stacks-tokens.md + docs/nodes-and-miners/quicknode: stacks-in-depth/nodes-and-miners/run-a-node-with-quicknode.md + docs/nodes-and-miners/run-a-node: stacks-in-depth/nodes-and-miners/run-a-node-with-docker.md + docs/nodes-and-miners/stacks-node-configuration: stacks-in-depth/nodes-and-miners/stacks-node-configuration.md + docs/nodes-and-miners/verify-miner: stacks-in-depth/nodes-and-miners/verify-miner.md + docs/services-using-stacks/defi: stacks-101/what-is-stacks.md + docs/services-using-stacks/more: stacks-101/what-is-stacks.md + docs/services-using-stacks/nft: stacks-101/what-is-stacks.md + docs/services-using-stacks/stacks-info-sites: stacks-101/what-is-stacks.md + docs/services-using-stacks/wallets: stacks-101/what-is-stacks.md + docs/stacks-academy: stacks-101/what-is-stacks.md + docs/stacks-academy/accounts: stacks-101/accounts.md + docs/stacks-academy/authentication: stacks-101/authentication.md + docs/stacks-academy/bns: stacks-101/bitcoin-name-system.md + docs/stacks-academy/btc-connection: stacks-101/bitcoin-connection.md + docs/stacks-academy/microblocks: nakamoto-upgrade/nakamoto-in-depth/what-about-microblocks.md + docs/stacks-academy/mining: stacks-101/mining.md + docs/stacks-academy/network: stacks-101/network.md + docs/stacks-academy/post-conditions: stacks-101/post-conditions.md + docs/stacks-academy/proof-of-transfer: stacks-101/proof-of-transfer.md + docs/stacks-academy/sips: stacks-101/sips.md + docs/stacks-academy/stacking: stacks-101/stacking.md + docs/stacks-academy/stacks-blockchain-api: stacks-101/api.md + docs/stacks-academy/technical-specs: stacks-101/technical-specifications.md + docs/stacks-academy/testnet: stacks-101/testnet.md + docs/stacks-academy/transactions: stacks-101/transactions.md + docs/stacks-academy/what-is-stacks: stacks-101/what-is-stacks.md + docs/stacks-academy/whitepapers: stacks-101/whitepapers.md + docs/tutorials: tutorials/hello-stacks-quickstart-tutorial.md + docs/tutorials/community-tutorials: tutorials/community-tutorials.md + docs/tutorials/hello-stacks: tutorials/hello-stacks-quickstart-tutorial.md + docs/understand-stacks/testnet: stacks-101/testnet.md + docs/write-smart-contracts: clarity/overview.md + references/bns-contract: clarity/example-contracts/bns.md + references/language-functions: clarity/functions.md + understand-stacks/microblocks: nakamoto-upgrade/nakamoto-in-depth/what-about-microblocks.md + understand-stacks/mining: stacks-101/mining.md + understand-stacks/network: stacks-101/network.md + understand-stacks/proof-of-transfer: stacks-101/proof-of-transfer.md + understand-stacks/stacking: stacks-101/stacking.md + understand-stacks/testnet: stacks-101/testnet.md + understand-stacks/transactions: stacks-101/transactions.md + write-smart-contracts/language-functions: clarity/functions.md + write-smart-contracts/overview: clarity/overview.md + write-smart-contracts/principals: clarity/types.md + write-smart-contracts/tokens: tutorials/tokens.md + sbtc/introduction: concepts/sbtc/README.md diff --git a/.gitbook/assets/+ (6).svg b/.gitbook/assets/+ (6).svg new file mode 100644 index 0000000000..779857b113 --- /dev/null +++ b/.gitbook/assets/+ (6).svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/.gitbook/assets/._lw3 faucet.png b/.gitbook/assets/._lw3 faucet.png new file mode 100644 index 0000000000..24ebd31ae5 Binary files /dev/null and b/.gitbook/assets/._lw3 faucet.png differ diff --git a/.gitbook/assets/Blockstack_Desktop_Wallet_Pentest_Report_11-12-2020 (1).pdf b/.gitbook/assets/Blockstack_Desktop_Wallet_Pentest_Report_11-12-2020 (1).pdf new file mode 100644 index 0000000000..72b6b717bf Binary files /dev/null and b/.gitbook/assets/Blockstack_Desktop_Wallet_Pentest_Report_11-12-2020 (1).pdf differ diff --git a/.gitbook/assets/Blockstack_Desktop_Wallet_Pentest_Report_11-12-2020.pdf b/.gitbook/assets/Blockstack_Desktop_Wallet_Pentest_Report_11-12-2020.pdf new file mode 100644 index 0000000000..72b6b717bf Binary files /dev/null and b/.gitbook/assets/Blockstack_Desktop_Wallet_Pentest_Report_11-12-2020.pdf differ diff --git a/.gitbook/assets/Clarity Alliance - Axelar.pdf b/.gitbook/assets/Clarity Alliance - Axelar.pdf new file mode 100644 index 0000000000..42698570ba Binary files /dev/null and b/.gitbook/assets/Clarity Alliance - Axelar.pdf differ diff --git a/.gitbook/assets/Clarity Alliance - Nakamoto.pdf b/.gitbook/assets/Clarity Alliance - Nakamoto.pdf new file mode 100644 index 0000000000..7f697432b8 Binary files /dev/null and b/.gitbook/assets/Clarity Alliance - Nakamoto.pdf differ diff --git a/.gitbook/assets/Clarity Alliance - sBTC (1).pdf b/.gitbook/assets/Clarity Alliance - sBTC (1).pdf new file mode 100644 index 0000000000..46fb8bb7ca Binary files /dev/null and b/.gitbook/assets/Clarity Alliance - sBTC (1).pdf differ diff --git a/.gitbook/assets/Clarity Alliance - sBTC Rewards Program (1).pdf b/.gitbook/assets/Clarity Alliance - sBTC Rewards Program (1).pdf new file mode 100644 index 0000000000..3fab1ee354 Binary files /dev/null and b/.gitbook/assets/Clarity Alliance - sBTC Rewards Program (1).pdf differ diff --git a/.gitbook/assets/Clarity Alliance - sBTC Rewards Program-2.pdf b/.gitbook/assets/Clarity Alliance - sBTC Rewards Program-2.pdf new file mode 100644 index 0000000000..3fab1ee354 Binary files /dev/null and b/.gitbook/assets/Clarity Alliance - sBTC Rewards Program-2.pdf differ diff --git a/.gitbook/assets/Clarity Alliance - sBTC Rewards Program.pdf b/.gitbook/assets/Clarity Alliance - sBTC Rewards Program.pdf new file mode 100644 index 0000000000..3fab1ee354 Binary files /dev/null and b/.gitbook/assets/Clarity Alliance - sBTC Rewards Program.pdf differ diff --git a/.gitbook/assets/Clarity Alliance - sBTC-2 (1).pdf b/.gitbook/assets/Clarity Alliance - sBTC-2 (1).pdf new file mode 100644 index 0000000000..46fb8bb7ca Binary files /dev/null and b/.gitbook/assets/Clarity Alliance - sBTC-2 (1).pdf differ diff --git a/.gitbook/assets/Clarity Alliance - sBTC-2.pdf b/.gitbook/assets/Clarity Alliance - sBTC-2.pdf new file mode 100644 index 0000000000..46fb8bb7ca Binary files /dev/null and b/.gitbook/assets/Clarity Alliance - sBTC-2.pdf differ diff --git a/.gitbook/assets/Clarity Alliance - sBTC.pdf b/.gitbook/assets/Clarity Alliance - sBTC.pdf new file mode 100644 index 0000000000..46fb8bb7ca Binary files /dev/null and b/.gitbook/assets/Clarity Alliance - sBTC.pdf differ diff --git a/.gitbook/assets/CoinFabrik - Signer Binary.pdf b/.gitbook/assets/CoinFabrik - Signer Binary.pdf new file mode 100644 index 0000000000..dc059e72dc Binary files /dev/null and b/.gitbook/assets/CoinFabrik - Signer Binary.pdf differ diff --git a/.gitbook/assets/CoinFabrik - StackerDB.pdf b/.gitbook/assets/CoinFabrik - StackerDB.pdf new file mode 100644 index 0000000000..b0e846fba3 Binary files /dev/null and b/.gitbook/assets/CoinFabrik - StackerDB.pdf differ diff --git a/.gitbook/assets/CoinFabrik - Stacks LibSigner (1).pdf b/.gitbook/assets/CoinFabrik - Stacks LibSigner (1).pdf new file mode 100644 index 0000000000..6e1a11c272 Binary files /dev/null and b/.gitbook/assets/CoinFabrik - Stacks LibSigner (1).pdf differ diff --git a/.gitbook/assets/CoinFabrik - Stacks LibSigner.pdf b/.gitbook/assets/CoinFabrik - Stacks LibSigner.pdf new file mode 100644 index 0000000000..6e1a11c272 Binary files /dev/null and b/.gitbook/assets/CoinFabrik - Stacks LibSigner.pdf differ diff --git a/.gitbook/assets/CoinFabrik - Stacks Signer Audit (1).pdf b/.gitbook/assets/CoinFabrik - Stacks Signer Audit (1).pdf new file mode 100644 index 0000000000..4a733d4e4f Binary files /dev/null and b/.gitbook/assets/CoinFabrik - Stacks Signer Audit (1).pdf differ diff --git a/.gitbook/assets/CoinFabrik - Stacks Signer Audit.pdf b/.gitbook/assets/CoinFabrik - Stacks Signer Audit.pdf new file mode 100644 index 0000000000..4a733d4e4f Binary files /dev/null and b/.gitbook/assets/CoinFabrik - Stacks Signer Audit.pdf differ diff --git a/.gitbook/assets/CoinFabrik - WSTS.pdf b/.gitbook/assets/CoinFabrik - WSTS.pdf new file mode 100644 index 0000000000..1b73a0857e Binary files /dev/null and b/.gitbook/assets/CoinFabrik - WSTS.pdf differ diff --git a/.gitbook/assets/CoinFabrik_PoX-4.pdf b/.gitbook/assets/CoinFabrik_PoX-4.pdf new file mode 100644 index 0000000000..3c23acd9e3 Binary files /dev/null and b/.gitbook/assets/CoinFabrik_PoX-4.pdf differ diff --git a/.gitbook/assets/CoinFabrik_Signer Binary (1).pdf b/.gitbook/assets/CoinFabrik_Signer Binary (1).pdf new file mode 100644 index 0000000000..dc059e72dc Binary files /dev/null and b/.gitbook/assets/CoinFabrik_Signer Binary (1).pdf differ diff --git a/.gitbook/assets/CoinFabrik_Signer Binary.pdf b/.gitbook/assets/CoinFabrik_Signer Binary.pdf new file mode 100644 index 0000000000..dc059e72dc Binary files /dev/null and b/.gitbook/assets/CoinFabrik_Signer Binary.pdf differ diff --git a/.gitbook/assets/CoinFabrik_StackerDB (1).pdf b/.gitbook/assets/CoinFabrik_StackerDB (1).pdf new file mode 100644 index 0000000000..b0e846fba3 Binary files /dev/null and b/.gitbook/assets/CoinFabrik_StackerDB (1).pdf differ diff --git a/.gitbook/assets/CoinFabrik_StackerDB.pdf b/.gitbook/assets/CoinFabrik_StackerDB.pdf new file mode 100644 index 0000000000..b0e846fba3 Binary files /dev/null and b/.gitbook/assets/CoinFabrik_StackerDB.pdf differ diff --git a/.gitbook/assets/CoinFabrik_WSTS (1) (1).pdf b/.gitbook/assets/CoinFabrik_WSTS (1) (1).pdf new file mode 100644 index 0000000000..1b73a0857e Binary files /dev/null and b/.gitbook/assets/CoinFabrik_WSTS (1) (1).pdf differ diff --git a/.gitbook/assets/CoinFabrik_WSTS (1).pdf b/.gitbook/assets/CoinFabrik_WSTS (1).pdf new file mode 100644 index 0000000000..1b73a0857e Binary files /dev/null and b/.gitbook/assets/CoinFabrik_WSTS (1).pdf differ diff --git a/.gitbook/assets/CoinFabrik_WSTS.pdf b/.gitbook/assets/CoinFabrik_WSTS.pdf new file mode 100644 index 0000000000..1b73a0857e Binary files /dev/null and b/.gitbook/assets/CoinFabrik_WSTS.pdf differ diff --git a/.gitbook/assets/Coinfabrik - Stacks PoX (1).pdf b/.gitbook/assets/Coinfabrik - Stacks PoX (1).pdf new file mode 100644 index 0000000000..e9678769ea Binary files /dev/null and b/.gitbook/assets/Coinfabrik - Stacks PoX (1).pdf differ diff --git a/.gitbook/assets/Coinfabrik - Stacks PoX.pdf b/.gitbook/assets/Coinfabrik - Stacks PoX.pdf new file mode 100644 index 0000000000..e9678769ea Binary files /dev/null and b/.gitbook/assets/Coinfabrik - Stacks PoX.pdf differ diff --git a/.gitbook/assets/Copy of Bitcoin Layers-modified.png b/.gitbook/assets/Copy of Bitcoin Layers-modified.png new file mode 100644 index 0000000000..d0741a9709 Binary files /dev/null and b/.gitbook/assets/Copy of Bitcoin Layers-modified.png differ diff --git a/.gitbook/assets/Copy of Bitcoin Layers.png b/.gitbook/assets/Copy of Bitcoin Layers.png new file mode 100644 index 0000000000..c0efd5a90e Binary files /dev/null and b/.gitbook/assets/Copy of Bitcoin Layers.png differ diff --git a/.gitbook/assets/Diagram Feb 2 2024 from Mermaid Chart (1).png b/.gitbook/assets/Diagram Feb 2 2024 from Mermaid Chart (1).png new file mode 100644 index 0000000000..ade88fb32f Binary files /dev/null and b/.gitbook/assets/Diagram Feb 2 2024 from Mermaid Chart (1).png differ diff --git a/.gitbook/assets/Diagram Feb 2 2024 from Mermaid Chart.png b/.gitbook/assets/Diagram Feb 2 2024 from Mermaid Chart.png new file mode 100644 index 0000000000..5115528c3e Binary files /dev/null and b/.gitbook/assets/Diagram Feb 2 2024 from Mermaid Chart.png differ diff --git a/.gitbook/assets/Diagram Feb 2 2024.png b/.gitbook/assets/Diagram Feb 2 2024.png new file mode 100644 index 0000000000..66f0f55535 Binary files /dev/null and b/.gitbook/assets/Diagram Feb 2 2024.png differ diff --git a/.gitbook/assets/Docs Walkthrough.mp4 b/.gitbook/assets/Docs Walkthrough.mp4 new file mode 100644 index 0000000000..e2590072f8 Binary files /dev/null and b/.gitbook/assets/Docs Walkthrough.mp4 differ diff --git a/.gitbook/assets/Image from Skiff (1).png b/.gitbook/assets/Image from Skiff (1).png new file mode 100644 index 0000000000..f4ded1273c Binary files /dev/null and b/.gitbook/assets/Image from Skiff (1).png differ diff --git a/.gitbook/assets/Image from Skiff.png b/.gitbook/assets/Image from Skiff.png new file mode 100644 index 0000000000..d058496d5a Binary files /dev/null and b/.gitbook/assets/Image from Skiff.png differ diff --git a/.gitbook/assets/NCC_Group_Stacks_Blockchain_Audit_Report_2020-11-23_v1.0 (1).pdf b/.gitbook/assets/NCC_Group_Stacks_Blockchain_Audit_Report_2020-11-23_v1.0 (1).pdf new file mode 100644 index 0000000000..bed4f3bd8b Binary files /dev/null and b/.gitbook/assets/NCC_Group_Stacks_Blockchain_Audit_Report_2020-11-23_v1.0 (1).pdf differ diff --git a/.gitbook/assets/NCC_Group_Stacks_Blockchain_Audit_Report_2020-11-23_v1.0.pdf b/.gitbook/assets/NCC_Group_Stacks_Blockchain_Audit_Report_2020-11-23_v1.0.pdf new file mode 100644 index 0000000000..bed4f3bd8b Binary files /dev/null and b/.gitbook/assets/NCC_Group_Stacks_Blockchain_Audit_Report_2020-11-23_v1.0.pdf differ diff --git a/.gitbook/assets/NCC_Group_Stacks_Wallet_Report_2020-11-17_v1.0 (1).pdf b/.gitbook/assets/NCC_Group_Stacks_Wallet_Report_2020-11-17_v1.0 (1).pdf new file mode 100644 index 0000000000..cd00d448fa Binary files /dev/null and b/.gitbook/assets/NCC_Group_Stacks_Wallet_Report_2020-11-17_v1.0 (1).pdf differ diff --git a/.gitbook/assets/NCC_Group_Stacks_Wallet_Report_2020-11-17_v1.0.pdf b/.gitbook/assets/NCC_Group_Stacks_Wallet_Report_2020-11-17_v1.0.pdf new file mode 100644 index 0000000000..cd00d448fa Binary files /dev/null and b/.gitbook/assets/NCC_Group_Stacks_Wallet_Report_2020-11-17_v1.0.pdf differ diff --git a/.gitbook/assets/Ottersec - WSTS (1).pdf b/.gitbook/assets/Ottersec - WSTS (1).pdf new file mode 100644 index 0000000000..b3929dbb39 Binary files /dev/null and b/.gitbook/assets/Ottersec - WSTS (1).pdf differ diff --git a/.gitbook/assets/Ottersec - WSTS.pdf b/.gitbook/assets/Ottersec - WSTS.pdf new file mode 100644 index 0000000000..b3929dbb39 Binary files /dev/null and b/.gitbook/assets/Ottersec - WSTS.pdf differ diff --git a/.gitbook/assets/Ottersec - sBTC Withdrawal (1).pdf b/.gitbook/assets/Ottersec - sBTC Withdrawal (1).pdf new file mode 100644 index 0000000000..b093ed87bf Binary files /dev/null and b/.gitbook/assets/Ottersec - sBTC Withdrawal (1).pdf differ diff --git a/.gitbook/assets/Ottersec - sBTC Withdrawal.pdf b/.gitbook/assets/Ottersec - sBTC Withdrawal.pdf new file mode 100644 index 0000000000..b093ed87bf Binary files /dev/null and b/.gitbook/assets/Ottersec - sBTC Withdrawal.pdf differ diff --git a/.gitbook/assets/Ottersec - stacks_sbtc_audit_final (1).pdf b/.gitbook/assets/Ottersec - stacks_sbtc_audit_final (1).pdf new file mode 100644 index 0000000000..b093ed87bf Binary files /dev/null and b/.gitbook/assets/Ottersec - stacks_sbtc_audit_final (1).pdf differ diff --git a/.gitbook/assets/Ottersec - stacks_sbtc_audit_final.pdf b/.gitbook/assets/Ottersec - stacks_sbtc_audit_final.pdf new file mode 100644 index 0000000000..b093ed87bf Binary files /dev/null and b/.gitbook/assets/Ottersec - stacks_sbtc_audit_final.pdf differ diff --git a/.gitbook/assets/Ottersec - stacks_wsts_audit_final (1).pdf b/.gitbook/assets/Ottersec - stacks_wsts_audit_final (1).pdf new file mode 100644 index 0000000000..b3929dbb39 Binary files /dev/null and b/.gitbook/assets/Ottersec - stacks_wsts_audit_final (1).pdf differ diff --git a/.gitbook/assets/Ottersec - stacks_wsts_audit_final.pdf b/.gitbook/assets/Ottersec - stacks_wsts_audit_final.pdf new file mode 100644 index 0000000000..b3929dbb39 Binary files /dev/null and b/.gitbook/assets/Ottersec - stacks_wsts_audit_final.pdf differ diff --git a/.gitbook/assets/Quantstamp - Network State Machine.pdf b/.gitbook/assets/Quantstamp - Network State Machine.pdf new file mode 100644 index 0000000000..150723b091 Binary files /dev/null and b/.gitbook/assets/Quantstamp - Network State Machine.pdf differ diff --git a/.gitbook/assets/Quantstamp_Network State Machine (1).pdf b/.gitbook/assets/Quantstamp_Network State Machine (1).pdf new file mode 100644 index 0000000000..150723b091 Binary files /dev/null and b/.gitbook/assets/Quantstamp_Network State Machine (1).pdf differ diff --git a/.gitbook/assets/Quantstamp_Network State Machine.pdf b/.gitbook/assets/Quantstamp_Network State Machine.pdf new file mode 100644 index 0000000000..150723b091 Binary files /dev/null and b/.gitbook/assets/Quantstamp_Network State Machine.pdf differ diff --git a/.gitbook/assets/Screenshot 2024-04-06 105944.png b/.gitbook/assets/Screenshot 2024-04-06 105944.png new file mode 100644 index 0000000000..a0f346e1a4 Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-04-06 105944.png differ diff --git a/.gitbook/assets/Screenshot 2024-04-10 141918.png b/.gitbook/assets/Screenshot 2024-04-10 141918.png new file mode 100644 index 0000000000..6b769eca2a Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-04-10 141918.png differ diff --git a/.gitbook/assets/Screenshot 2024-04-30 154011.png b/.gitbook/assets/Screenshot 2024-04-30 154011.png new file mode 100644 index 0000000000..9bbd0666da Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-04-30 154011.png differ diff --git a/.gitbook/assets/Screenshot 2024-08-05 111756.png b/.gitbook/assets/Screenshot 2024-08-05 111756.png new file mode 100644 index 0000000000..04553f1466 Binary files /dev/null and b/.gitbook/assets/Screenshot 2024-08-05 111756.png differ diff --git a/.gitbook/assets/Stacking Graphic (1).png b/.gitbook/assets/Stacking Graphic (1).png new file mode 100644 index 0000000000..83799fed4c Binary files /dev/null and b/.gitbook/assets/Stacking Graphic (1).png differ diff --git a/.gitbook/assets/Untitled design (1).png b/.gitbook/assets/Untitled design (1).png new file mode 100644 index 0000000000..f9ac086a54 Binary files /dev/null and b/.gitbook/assets/Untitled design (1).png differ diff --git a/.gitbook/assets/Untitled design.png b/.gitbook/assets/Untitled design.png new file mode 100644 index 0000000000..f9ac086a54 Binary files /dev/null and b/.gitbook/assets/Untitled design.png differ diff --git a/.gitbook/assets/_category_ (1).json b/.gitbook/assets/_category_ (1).json new file mode 100644 index 0000000000..0bb2c6de80 --- /dev/null +++ b/.gitbook/assets/_category_ (1).json @@ -0,0 +1,9 @@ +{ + "label": "References", + "position": 9, + "link": { + "type": "generated-index", + "description": "Stacks references." + } + } + \ No newline at end of file diff --git a/.gitbook/assets/_category_ (2).json b/.gitbook/assets/_category_ (2).json new file mode 100644 index 0000000000..eb32d48e32 --- /dev/null +++ b/.gitbook/assets/_category_ (2).json @@ -0,0 +1,8 @@ +{ + "label": "Noteworthy Contracts", + "position": 5, + "link": { + "type": "generated-index", + "description": "Specific deployed Clarity Contracts worth mentioning." + } +} diff --git a/.gitbook/assets/_category_.json b/.gitbook/assets/_category_.json new file mode 100644 index 0000000000..72c0987c1f --- /dev/null +++ b/.gitbook/assets/_category_.json @@ -0,0 +1,9 @@ +{ + "label": "Services using Stacks", + "position": 8, + "link": { + "type": "generated-index", + "description": "An 'always incomplete' list of services that use Stacks." + } +} + \ No newline at end of file diff --git a/.gitbook/assets/clarinet structure (1).png b/.gitbook/assets/clarinet structure (1).png new file mode 100644 index 0000000000..8d16dc55cf Binary files /dev/null and b/.gitbook/assets/clarinet structure (1).png differ diff --git a/.gitbook/assets/clarinet structure.png b/.gitbook/assets/clarinet structure.png new file mode 100644 index 0000000000..8d16dc55cf Binary files /dev/null and b/.gitbook/assets/clarinet structure.png differ diff --git a/.gitbook/assets/deposit-flow.png b/.gitbook/assets/deposit-flow.png new file mode 100644 index 0000000000..60ce335756 Binary files /dev/null and b/.gitbook/assets/deposit-flow.png differ diff --git a/.gitbook/assets/error-1.jpg b/.gitbook/assets/error-1.jpg new file mode 100644 index 0000000000..57bc661d55 Binary files /dev/null and b/.gitbook/assets/error-1.jpg differ diff --git a/.gitbook/assets/event log (1).png b/.gitbook/assets/event log (1).png new file mode 100644 index 0000000000..51f04851f8 Binary files /dev/null and b/.gitbook/assets/event log (1).png differ diff --git a/.gitbook/assets/event log.png b/.gitbook/assets/event log.png new file mode 100644 index 0000000000..51f04851f8 Binary files /dev/null and b/.gitbook/assets/event log.png differ diff --git a/.gitbook/assets/explorer view (1).png b/.gitbook/assets/explorer view (1).png new file mode 100644 index 0000000000..74a2042496 Binary files /dev/null and b/.gitbook/assets/explorer view (1).png differ diff --git a/.gitbook/assets/explorer view.png b/.gitbook/assets/explorer view.png new file mode 100644 index 0000000000..74a2042496 Binary files /dev/null and b/.gitbook/assets/explorer view.png differ diff --git a/.gitbook/assets/function call.png b/.gitbook/assets/function call.png new file mode 100644 index 0000000000..32a28b347a Binary files /dev/null and b/.gitbook/assets/function call.png differ diff --git a/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png b/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png new file mode 100644 index 0000000000..7c20603126 Binary files /dev/null and b/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png differ diff --git a/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png b/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png new file mode 100644 index 0000000000..c3f47bbe62 Binary files /dev/null and b/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png differ diff --git a/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png b/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png new file mode 100644 index 0000000000..bdf72f67cc Binary files /dev/null and b/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png differ diff --git a/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1).png b/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1).png new file mode 100644 index 0000000000..ef9c9aabe2 Binary files /dev/null and b/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1).png differ diff --git a/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1).png b/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1).png new file mode 100644 index 0000000000..ef9c9aabe2 Binary files /dev/null and b/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1).png differ diff --git a/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1).png b/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1).png new file mode 100644 index 0000000000..46c75b3215 Binary files /dev/null and b/.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1).png differ diff --git a/.gitbook/assets/image (1) (1) (1) (1) (1) (1).png b/.gitbook/assets/image (1) (1) (1) (1) (1) (1).png new file mode 100644 index 0000000000..db81843857 Binary files /dev/null and b/.gitbook/assets/image (1) (1) (1) (1) (1) (1).png differ diff --git a/.gitbook/assets/image (1) (1) (1) (1) (1).png b/.gitbook/assets/image (1) (1) (1) (1) (1).png new file mode 100644 index 0000000000..dcc459fad4 Binary files /dev/null and b/.gitbook/assets/image (1) (1) (1) (1) (1).png differ diff --git a/.gitbook/assets/image (1) (1) (1) (1).png b/.gitbook/assets/image (1) (1) (1) (1).png new file mode 100644 index 0000000000..47124f1445 Binary files /dev/null and b/.gitbook/assets/image (1) (1) (1) (1).png differ diff --git a/.gitbook/assets/image (1) (1) (1).png b/.gitbook/assets/image (1) (1) (1).png new file mode 100644 index 0000000000..3ae07bec6d Binary files /dev/null and b/.gitbook/assets/image (1) (1) (1).png differ diff --git a/.gitbook/assets/image (1) (1).png b/.gitbook/assets/image (1) (1).png new file mode 100644 index 0000000000..069fdb2137 Binary files /dev/null and b/.gitbook/assets/image (1) (1).png differ diff --git a/.gitbook/assets/image (1).png b/.gitbook/assets/image (1).png new file mode 100644 index 0000000000..e88d09374d Binary files /dev/null and b/.gitbook/assets/image (1).png differ diff --git a/.gitbook/assets/image (10).png b/.gitbook/assets/image (10).png new file mode 100644 index 0000000000..32a8b88537 Binary files /dev/null and b/.gitbook/assets/image (10).png differ diff --git a/.gitbook/assets/image (11).png b/.gitbook/assets/image (11).png new file mode 100644 index 0000000000..22cea089f0 Binary files /dev/null and b/.gitbook/assets/image (11).png differ diff --git a/.gitbook/assets/image (12) (1).png b/.gitbook/assets/image (12) (1).png new file mode 100644 index 0000000000..177165905a Binary files /dev/null and b/.gitbook/assets/image (12) (1).png differ diff --git a/.gitbook/assets/image (12).png b/.gitbook/assets/image (12).png new file mode 100644 index 0000000000..6801c67710 Binary files /dev/null and b/.gitbook/assets/image (12).png differ diff --git a/.gitbook/assets/image (13).png b/.gitbook/assets/image (13).png new file mode 100644 index 0000000000..6b97f69623 Binary files /dev/null and b/.gitbook/assets/image (13).png differ diff --git a/.gitbook/assets/image (14).png b/.gitbook/assets/image (14).png new file mode 100644 index 0000000000..74747c4066 Binary files /dev/null and b/.gitbook/assets/image (14).png differ diff --git a/.gitbook/assets/image (15).png b/.gitbook/assets/image (15).png new file mode 100644 index 0000000000..a07a970d39 Binary files /dev/null and b/.gitbook/assets/image (15).png differ diff --git a/.gitbook/assets/image (16).png b/.gitbook/assets/image (16).png new file mode 100644 index 0000000000..3f64fe503c Binary files /dev/null and b/.gitbook/assets/image (16).png differ diff --git a/.gitbook/assets/image (17).png b/.gitbook/assets/image (17).png new file mode 100644 index 0000000000..1d17b1fe67 Binary files /dev/null and b/.gitbook/assets/image (17).png differ diff --git a/.gitbook/assets/image (18).png b/.gitbook/assets/image (18).png new file mode 100644 index 0000000000..6b769eca2a Binary files /dev/null and b/.gitbook/assets/image (18).png differ diff --git a/.gitbook/assets/image (19).png b/.gitbook/assets/image (19).png new file mode 100644 index 0000000000..6b769eca2a Binary files /dev/null and b/.gitbook/assets/image (19).png differ diff --git a/.gitbook/assets/image (2) (1) (1) (1) (1).png b/.gitbook/assets/image (2) (1) (1) (1) (1).png new file mode 100644 index 0000000000..c9bb5f0c0a Binary files /dev/null and b/.gitbook/assets/image (2) (1) (1) (1) (1).png differ diff --git a/.gitbook/assets/image (2) (1) (1) (1).png b/.gitbook/assets/image (2) (1) (1) (1).png new file mode 100644 index 0000000000..dc546f1675 Binary files /dev/null and b/.gitbook/assets/image (2) (1) (1) (1).png differ diff --git a/.gitbook/assets/image (2) (1) (1).png b/.gitbook/assets/image (2) (1) (1).png new file mode 100644 index 0000000000..7683e8937d Binary files /dev/null and b/.gitbook/assets/image (2) (1) (1).png differ diff --git a/.gitbook/assets/image (2) (1).png b/.gitbook/assets/image (2) (1).png new file mode 100644 index 0000000000..251d56c4fa Binary files /dev/null and b/.gitbook/assets/image (2) (1).png differ diff --git a/.gitbook/assets/image (2).png b/.gitbook/assets/image (2).png new file mode 100644 index 0000000000..461e4360ca Binary files /dev/null and b/.gitbook/assets/image (2).png differ diff --git a/.gitbook/assets/image (20).png b/.gitbook/assets/image (20).png new file mode 100644 index 0000000000..07aa27fb9d Binary files /dev/null and b/.gitbook/assets/image (20).png differ diff --git a/.gitbook/assets/image (21).png b/.gitbook/assets/image (21).png new file mode 100644 index 0000000000..6a334af0b4 Binary files /dev/null and b/.gitbook/assets/image (21).png differ diff --git a/.gitbook/assets/image (22).png b/.gitbook/assets/image (22).png new file mode 100644 index 0000000000..6db006d72b Binary files /dev/null and b/.gitbook/assets/image (22).png differ diff --git a/.gitbook/assets/image (23).png b/.gitbook/assets/image (23).png new file mode 100644 index 0000000000..94b5356c28 Binary files /dev/null and b/.gitbook/assets/image (23).png differ diff --git a/.gitbook/assets/image (24).png b/.gitbook/assets/image (24).png new file mode 100644 index 0000000000..c281ef2d2c Binary files /dev/null and b/.gitbook/assets/image (24).png differ diff --git a/.gitbook/assets/image (25).png b/.gitbook/assets/image (25).png new file mode 100644 index 0000000000..ac1140e3b9 Binary files /dev/null and b/.gitbook/assets/image (25).png differ diff --git a/.gitbook/assets/image (3) (1) (1).png b/.gitbook/assets/image (3) (1) (1).png new file mode 100644 index 0000000000..4d5d838943 Binary files /dev/null and b/.gitbook/assets/image (3) (1) (1).png differ diff --git a/.gitbook/assets/image (3) (1).png b/.gitbook/assets/image (3) (1).png new file mode 100644 index 0000000000..e003e7d3ff Binary files /dev/null and b/.gitbook/assets/image (3) (1).png differ diff --git a/.gitbook/assets/image (3).png b/.gitbook/assets/image (3).png new file mode 100644 index 0000000000..d89f5232bb Binary files /dev/null and b/.gitbook/assets/image (3).png differ diff --git a/.gitbook/assets/image (4) (1) (1).png b/.gitbook/assets/image (4) (1) (1).png new file mode 100644 index 0000000000..ff4958f8a3 Binary files /dev/null and b/.gitbook/assets/image (4) (1) (1).png differ diff --git a/.gitbook/assets/image (4) (1).png b/.gitbook/assets/image (4) (1).png new file mode 100644 index 0000000000..71d3076650 Binary files /dev/null and b/.gitbook/assets/image (4) (1).png differ diff --git a/.gitbook/assets/image (4).png b/.gitbook/assets/image (4).png new file mode 100644 index 0000000000..14da174535 Binary files /dev/null and b/.gitbook/assets/image (4).png differ diff --git a/.gitbook/assets/image (5).png b/.gitbook/assets/image (5).png new file mode 100644 index 0000000000..94f40f5e5c Binary files /dev/null and b/.gitbook/assets/image (5).png differ diff --git a/.gitbook/assets/image (6).png b/.gitbook/assets/image (6).png new file mode 100644 index 0000000000..cc5939c28f Binary files /dev/null and b/.gitbook/assets/image (6).png differ diff --git a/.gitbook/assets/image (7).png b/.gitbook/assets/image (7).png new file mode 100644 index 0000000000..5d5fc5edad Binary files /dev/null and b/.gitbook/assets/image (7).png differ diff --git a/.gitbook/assets/image (8).png b/.gitbook/assets/image (8).png new file mode 100644 index 0000000000..2f2497d30b Binary files /dev/null and b/.gitbook/assets/image (8).png differ diff --git a/.gitbook/assets/image (9).png b/.gitbook/assets/image (9).png new file mode 100644 index 0000000000..ebb4cff489 Binary files /dev/null and b/.gitbook/assets/image (9).png differ diff --git a/.gitbook/assets/image 41.png b/.gitbook/assets/image 41.png new file mode 100644 index 0000000000..aede46c8f0 Binary files /dev/null and b/.gitbook/assets/image 41.png differ diff --git a/.gitbook/assets/image 42.png b/.gitbook/assets/image 42.png new file mode 100644 index 0000000000..a11b3a2538 Binary files /dev/null and b/.gitbook/assets/image 42.png differ diff --git a/.gitbook/assets/image 43.png b/.gitbook/assets/image 43.png new file mode 100644 index 0000000000..0318bdb20f Binary files /dev/null and b/.gitbook/assets/image 43.png differ diff --git a/.gitbook/assets/image 45.png b/.gitbook/assets/image 45.png new file mode 100644 index 0000000000..f1b2cbeeb2 Binary files /dev/null and b/.gitbook/assets/image 45.png differ diff --git a/.gitbook/assets/image 48.png b/.gitbook/assets/image 48.png new file mode 100644 index 0000000000..30f121f435 Binary files /dev/null and b/.gitbook/assets/image 48.png differ diff --git a/.gitbook/assets/image 50.png b/.gitbook/assets/image 50.png new file mode 100644 index 0000000000..3ba6f6b5af Binary files /dev/null and b/.gitbook/assets/image 50.png differ diff --git a/.gitbook/assets/image.png b/.gitbook/assets/image.png new file mode 100644 index 0000000000..c281ef2d2c Binary files /dev/null and b/.gitbook/assets/image.png differ diff --git a/.gitbook/assets/initial screen (1).png b/.gitbook/assets/initial screen (1).png new file mode 100644 index 0000000000..222c5c517d Binary files /dev/null and b/.gitbook/assets/initial screen (1).png differ diff --git a/.gitbook/assets/initial screen.png b/.gitbook/assets/initial screen.png new file mode 100644 index 0000000000..222c5c517d Binary files /dev/null and b/.gitbook/assets/initial screen.png differ diff --git a/.gitbook/assets/language functions.mdx b/.gitbook/assets/language functions.mdx new file mode 100644 index 0000000000..a2aef51295 --- /dev/null +++ b/.gitbook/assets/language functions.mdx @@ -0,0 +1,15 @@ +--- +title: Functions +description: See a detailed list of all functions for the Clarity language. +sidebar_position: 3 +tags: + - clarity +--- + +import { FunctionsReferences } from "@site/src/components/ApiReferences/FunctionsReferences.jsx"; + +## Functions + +Detailed list of all functions for the Clarity language. + + diff --git a/.gitbook/assets/language keywords.mdx b/.gitbook/assets/language keywords.mdx new file mode 100644 index 0000000000..63658fab60 --- /dev/null +++ b/.gitbook/assets/language keywords.mdx @@ -0,0 +1,17 @@ +--- +title: Keywords +description: See a detailed list of all keywords for the Clarity language. +sidebar_position: 4 +tags: + - clarity +--- + +import { KeywordsReferences } from "@site/src/components/ApiReferences/KeywordsReferences.jsx"; + +![Abstract image illustrate Clarity native keywords](/img/keywords.jpg) + +## Keyword reference + +Detailed list of all keywords for the Clarity language. + + diff --git a/.gitbook/assets/loaded contract (1).png b/.gitbook/assets/loaded contract (1).png new file mode 100644 index 0000000000..95a182977d Binary files /dev/null and b/.gitbook/assets/loaded contract (1).png differ diff --git a/.gitbook/assets/loaded contract.png b/.gitbook/assets/loaded contract.png new file mode 100644 index 0000000000..95a182977d Binary files /dev/null and b/.gitbook/assets/loaded contract.png differ diff --git a/.gitbook/assets/lw3 faucet (1).png b/.gitbook/assets/lw3 faucet (1).png new file mode 100644 index 0000000000..cea83a0086 Binary files /dev/null and b/.gitbook/assets/lw3 faucet (1).png differ diff --git a/.gitbook/assets/lw3 faucet.png b/.gitbook/assets/lw3 faucet.png new file mode 100644 index 0000000000..cea83a0086 Binary files /dev/null and b/.gitbook/assets/lw3 faucet.png differ diff --git a/.gitbook/assets/new function.png b/.gitbook/assets/new function.png new file mode 100644 index 0000000000..a956d776ba Binary files /dev/null and b/.gitbook/assets/new function.png differ diff --git a/.gitbook/assets/post condition (1).jpeg b/.gitbook/assets/post condition (1).jpeg new file mode 100644 index 0000000000..465695e0bd Binary files /dev/null and b/.gitbook/assets/post condition (1).jpeg differ diff --git a/.gitbook/assets/post condition.jpeg b/.gitbook/assets/post condition.jpeg new file mode 100644 index 0000000000..465695e0bd Binary files /dev/null and b/.gitbook/assets/post condition.jpeg differ diff --git a/.gitbook/assets/quicknode endpoint (1).png b/.gitbook/assets/quicknode endpoint (1).png new file mode 100644 index 0000000000..0bde1b9353 Binary files /dev/null and b/.gitbook/assets/quicknode endpoint (1).png differ diff --git a/.gitbook/assets/quicknode endpoint.png b/.gitbook/assets/quicknode endpoint.png new file mode 100644 index 0000000000..0bde1b9353 Binary files /dev/null and b/.gitbook/assets/quicknode endpoint.png differ diff --git a/.gitbook/assets/rollup comparison (1).png b/.gitbook/assets/rollup comparison (1).png new file mode 100644 index 0000000000..e444a6e3ec Binary files /dev/null and b/.gitbook/assets/rollup comparison (1).png differ diff --git a/.gitbook/assets/rollup comparison.png b/.gitbook/assets/rollup comparison.png new file mode 100644 index 0000000000..e444a6e3ec Binary files /dev/null and b/.gitbook/assets/rollup comparison.png differ diff --git a/.gitbook/assets/sbtc-design.png b/.gitbook/assets/sbtc-design.png new file mode 100644 index 0000000000..8c8cca865d Binary files /dev/null and b/.gitbook/assets/sbtc-design.png differ diff --git a/.gitbook/assets/sbtc-design.svg b/.gitbook/assets/sbtc-design.svg new file mode 100644 index 0000000000..df88afcfca --- /dev/null +++ b/.gitbook/assets/sbtc-design.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitbook/assets/sbtc_diagram.png b/.gitbook/assets/sbtc_diagram.png new file mode 100644 index 0000000000..f47d489ec5 Binary files /dev/null and b/.gitbook/assets/sbtc_diagram.png differ diff --git a/.gitbook/assets/telegram-cloud-photo-size-4-6046167312920330449-y.jpg b/.gitbook/assets/telegram-cloud-photo-size-4-6046167312920330449-y.jpg new file mode 100644 index 0000000000..0f0acb3503 Binary files /dev/null and b/.gitbook/assets/telegram-cloud-photo-size-4-6046167312920330449-y.jpg differ diff --git a/.gitbook/assets/telegram-cloud-photo-size-5-6334823135647481068-y (1).jpg b/.gitbook/assets/telegram-cloud-photo-size-5-6334823135647481068-y (1).jpg new file mode 100644 index 0000000000..57bc661d55 Binary files /dev/null and b/.gitbook/assets/telegram-cloud-photo-size-5-6334823135647481068-y (1).jpg differ diff --git a/.gitbook/assets/telegram-cloud-photo-size-5-6334823135647481068-y (2).jpg b/.gitbook/assets/telegram-cloud-photo-size-5-6334823135647481068-y (2).jpg new file mode 100644 index 0000000000..57bc661d55 Binary files /dev/null and b/.gitbook/assets/telegram-cloud-photo-size-5-6334823135647481068-y (2).jpg differ diff --git a/.gitbook/assets/telegram-cloud-photo-size-5-6334823135647481068-y (3).jpg b/.gitbook/assets/telegram-cloud-photo-size-5-6334823135647481068-y (3).jpg new file mode 100644 index 0000000000..57bc661d55 Binary files /dev/null and b/.gitbook/assets/telegram-cloud-photo-size-5-6334823135647481068-y (3).jpg differ diff --git a/.gitbook/assets/telegram-cloud-photo-size-5-6334823135647481068-y.jpg b/.gitbook/assets/telegram-cloud-photo-size-5-6334823135647481068-y.jpg new file mode 100644 index 0000000000..57bc661d55 Binary files /dev/null and b/.gitbook/assets/telegram-cloud-photo-size-5-6334823135647481068-y.jpg differ diff --git a/.gitbook/assets/tools new (1).png b/.gitbook/assets/tools new (1).png new file mode 100644 index 0000000000..094e36cc4f Binary files /dev/null and b/.gitbook/assets/tools new (1).png differ diff --git a/.gitbook/assets/tools new.png b/.gitbook/assets/tools new.png new file mode 100644 index 0000000000..094e36cc4f Binary files /dev/null and b/.gitbook/assets/tools new.png differ diff --git a/.gitbook/assets/tx call (1).png b/.gitbook/assets/tx call (1).png new file mode 100644 index 0000000000..e376f24ef1 Binary files /dev/null and b/.gitbook/assets/tx call (1).png differ diff --git a/.gitbook/assets/tx call.png b/.gitbook/assets/tx call.png new file mode 100644 index 0000000000..e376f24ef1 Binary files /dev/null and b/.gitbook/assets/tx call.png differ diff --git a/.gitbook/assets/tx proof (1).png b/.gitbook/assets/tx proof (1).png new file mode 100644 index 0000000000..0bd0a0525f Binary files /dev/null and b/.gitbook/assets/tx proof (1).png differ diff --git a/.gitbook/assets/tx proof.png b/.gitbook/assets/tx proof.png new file mode 100644 index 0000000000..0bd0a0525f Binary files /dev/null and b/.gitbook/assets/tx proof.png differ diff --git a/.gitbook/assets/withdrawal-flow.png b/.gitbook/assets/withdrawal-flow.png new file mode 100644 index 0000000000..4b3ff75e8a Binary files /dev/null and b/.gitbook/assets/withdrawal-flow.png differ diff --git a/.gitbook/includes/note-for-existing-signersth....md b/.gitbook/includes/note-for-existing-signersth....md new file mode 100644 index 0000000000..42aa121362 --- /dev/null +++ b/.gitbook/includes/note-for-existing-signersth....md @@ -0,0 +1,45 @@ +--- +title: Note for Existing SignersTh... +--- + +{% hint style="info" %} +**Note for Existing Signers** + +The block for Nakamoto activation has been chosen as Bitcoin block 867,867, which is currently expected on October 28th. This block is subject to change should core developers need additional time for testing or unexpected issues. + +The 3.0 binaries (and Docker images) are provided below. Note that if you do not upgrade ahead of the hard fork, your nodes will be dropped from the network. + +If you have previously been running a signer, you'll want to make sure you do the following: + +First, upgrade your node and signer to the latest version (listed below). + +For miners: If you are currently using the config value `[miner].activated_vrf_key_path`, the saved json file will need to be removed prior to restart (it will be recreated). You will also need to add a new key to the miner section: + +``` +[miner] +mining_key = "private key" ## may be the same as [node].seed +``` + +**Important note**: Any unrecognized and uncommented config value _will_ cause the binary to panic. + +The output will specify which configuration key caused the panic (there may be more than one, but the message will only show the first unrecognized key) in the format: + +``` +$ stacks-node check-config --config ./Config.toml +INFO [1738695683.067315] [testnet/stacks-node/src/main.rs:278] [main] stacks-node 3.1.0.0.5 (release/3.1.0.0.5:513dbc5, release build, linux [x86_64]) +INFO [1729707265.671604] [testnet/stacks-node/src/main.rs:318] [main] Loading config at path ./Config.toml +WARN [1729707265.671992] [testnet/stacks-node/src/main.rs:325] [main] Invalid config file: Invalid toml: unknown field `foo`, expected one of `name`, `seed`, `deny_nodes`, `working_dir`, `rpc_bind`, `p2p_bind`, `p2p_address`, `data_url`, `bootstrap_node`, `local_peer_seed`, `miner`, `stacker`, `mock_mining`, `mock_mining_output_dir`, `mine_microblocks`, `microblock_frequency`, `max_microblocks`, `wait_time_for_microblocks`, `wait_time_for_blocks`, `next_initiative_delay`, `prometheus_bind`, `marf_cache_strategy`, `marf_defer_hashing`, `pox_sync_sample_secs`, `use_test_genesis_chainstate`, `always_use_affirmation_maps`, `require_affirmed_anchor_blocks`, `chain_liveness_poll_time_secs`, `stacker_dbs`, `fault_injection_block_push_fail_probability` for key `node` at line 20 column 1 +``` + +**Changed config field** + +Note that in the Stacks node config file, the `block_proposal_token` field has been changed to `auth_token`. + +**Current Signer and Stacks Node Versions** + +For quick reference, here are the current latest versions you'll want to be running as a signer. If you don't yet have your signer up and running, this guide will walk you through that. + +* [Binaries](https://github.com/stacks-network/stacks-core/releases/latest) +* Stacks Signer Docker Image - [3.1.0.0.5.0](https://hub.docker.com/layers/blockstack/stacks-signer/3.1.0.0.5.0/images/sha256-4f0c19225065754ed08594deb3d2c67dc1126558e9e50f8174a1bc1736fedb99) +* Stacks Node Docker Image - [3.1.0.0.5](https://hub.docker.com/layers/blockstack/stacks-core/3.1.0.0.5/images/sha256-cf2c04b6f56d7a54c2032a13826ebb258cebe50a30b847810a279476824bd2c0) +{% endhint %} diff --git a/.github/ISSUE_TEMPLATE/add-documentation.md b/.github/ISSUE_TEMPLATE/add-documentation.md new file mode 100644 index 0000000000..3f702dfad7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/add-documentation.md @@ -0,0 +1,20 @@ +--- +name: Add documentation +about: Use this to add new content to docs.stacks.co +title: "[Add docs]" +labels: documentation +assignees: '' + +--- + +*You can use the following template to add new documentation to docs.stacks.co. You can drag and drop images or screenshots if needed.* +--- +title: Name of my new article +description: +--- + +## Section 1 + +## Section 2 + +### Subsection diff --git a/.github/ISSUE_TEMPLATE/edit-documentation.md b/.github/ISSUE_TEMPLATE/edit-documentation.md new file mode 100644 index 0000000000..21bfc20088 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/edit-documentation.md @@ -0,0 +1,12 @@ +--- +name: Edit documentation +about: Use this to make changes on already published page +title: "[Edit docs]" +labels: documentation +assignees: '' + +--- + +URL of the page to change: +--- +Explain here what you would like to change. diff --git a/.github/ISSUE_TEMPLATE/other-issues.md b/.github/ISSUE_TEMPLATE/other-issues.md new file mode 100644 index 0000000000..84cfcfe1a8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other-issues.md @@ -0,0 +1,14 @@ +--- +name: Other issues +about: Any other issue +title: '' +labels: '' +assignees: '' + +--- + +**Describe the issue** +A clear and concise description of what the issue is. + +**Screenshots** +If helpful, you may add screenshots to help explain your problem. diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000000..e85768c855 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,13 @@ +# Add labels when specific files are changed +translations: + - any: ['i18n/**/*'] + +documentation: + - any: ['docs/**/*.md', 'docs/**/*.mdx'] + +dependencies: + - 'package.json' + - 'package-lock-json' + +docusaurus-config: + - 'docusaurus.config.js' \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..1ef76dc1f3 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,3 @@ +## Description + +Describe the changes that were made in this pull request. When possible start with the motivations behind the change. \ No newline at end of file diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000000..07ee1d3a8c --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,18 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 180 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: > + This issue has been automatically closed. Please reopen if needed. \ No newline at end of file diff --git a/.github/workflows/.markdown-link-check_config.json b/.github/workflows/.markdown-link-check_config.json new file mode 100644 index 0000000000..e0e786f3fb --- /dev/null +++ b/.github/workflows/.markdown-link-check_config.json @@ -0,0 +1,20 @@ +{ + "ignorePatterns": [ + { + "pattern": "^https:\/\/www\.coinbase\.com\b" + }, + { + "pattern": "^https:\/\/www\.kraken\.com\b" + } + ], + "replacementPatterns": [ + { + "pattern": "^/", + "replacement": "/github/workspace/" + } + ], + "aliveStatusCodes": [ + 200, + 429 + ] +} \ No newline at end of file diff --git a/.github/workflows/add-to-project.yml.draft b/.github/workflows/add-to-project.yml.draft new file mode 100644 index 0000000000..2afce92968 --- /dev/null +++ b/.github/workflows/add-to-project.yml.draft @@ -0,0 +1,18 @@ +name: Add issue/PR to project + +on: + issues: + types: + - opened + pull_request: + branches: [master] + +jobs: + add-to-project: + name: Add issue to project + runs-on: ubuntu-latest + steps: + - uses: actions/add-to-project@main + with: + project-url: https://github.com/orgs/stacks-network/projects/56 + github-token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/auto-approve-translations.yml b/.github/workflows/auto-approve-translations.yml new file mode 100644 index 0000000000..0aaf99a723 --- /dev/null +++ b/.github/workflows/auto-approve-translations.yml @@ -0,0 +1,13 @@ +name: Auto approve translations +on: + pull_request +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: hmarr/auto-approve-action@v2 + if: github.actor == 'bot-translations' + with: + github-token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml new file mode 100644 index 0000000000..205b0fe26d --- /dev/null +++ b/.github/workflows/claude-code-review.yml @@ -0,0 +1,57 @@ +name: Claude Code Review + +on: + pull_request: + types: [opened, synchronize] + # Optional: Only run on specific file changes + # paths: + # - "src/**/*.ts" + # - "src/**/*.tsx" + # - "src/**/*.js" + # - "src/**/*.jsx" + +jobs: + claude-review: + # Optional: Filter by PR author + # if: | + # github.event.pull_request.user.login == 'external-contributor' || + # github.event.pull_request.user.login == 'new-developer' || + # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' + + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + issues: read + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run Claude Code Review + id: claude-review + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + prompt: | + REPO: ${{ github.repository }} + PR NUMBER: ${{ github.event.pull_request.number }} + + Please review this pull request and provide feedback on: + - Code quality and best practices + - Potential bugs or issues + - Performance considerations + - Security concerns + - Test coverage + + Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback. + + Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR. + + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md + # or https://docs.claude.com/en/docs/claude-code/cli-reference for available options + claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"' + diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml new file mode 100644 index 0000000000..412cef9e67 --- /dev/null +++ b/.github/workflows/claude.yml @@ -0,0 +1,50 @@ +name: Claude Code + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + issues: + types: [opened, assigned] + pull_request_review: + types: [submitted] + +jobs: + claude: + if: | + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || + (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + issues: read + id-token: write + actions: read # Required for Claude to read CI results on PRs + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run Claude Code + id: claude + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + + # This is an optional setting that allows Claude to read CI results on PRs + additional_permissions: | + actions: read + + # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. + # prompt: 'Update the pull request description to include a summary of changes.' + + # Optional: Add claude_args to customize behavior and configuration + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md + # or https://docs.claude.com/en/docs/claude-code/cli-reference for available options + # claude_args: '--allowed-tools Bash(gh pr:*)' + diff --git a/.github/workflows/code-quality.yml.old b/.github/workflows/code-quality.yml.old new file mode 100644 index 0000000000..3989cc53ab --- /dev/null +++ b/.github/workflows/code-quality.yml.old @@ -0,0 +1,23 @@ + pull_request: + branches: [master] + +jobs: + code_quality: + runs-on: ubuntu-latest + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@0.5.0 + with: + access_token: ${{ github.token }} + - name: Checkout + uses: actions/checkout@v2 + - name: Set Node Version + uses: actions/setup-node@v1 + with: + node-version: '14.x' + - name: Install deps + run: yarn --frozen-lockfile + - name: Lint + run: yarn lint + - name: Typecheck + run: yarn typecheck diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml new file mode 100644 index 0000000000..ef3d7d51f2 --- /dev/null +++ b/.github/workflows/label.yml @@ -0,0 +1,22 @@ +# This workflow will triage pull requests and apply a label based on the +# paths that are modified in the pull request. +# +# To use this workflow, you will need to set up a .github/labeler.yml +# file with configuration. For more information, see: +# https://github.com/actions/labeler + +name: Labeler +on: [pull_request] + +jobs: + label: + + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + + steps: + - uses: actions/labeler@v4 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file diff --git a/.github/workflows/markdown-link-check.yml b/.github/workflows/markdown-link-check.yml new file mode 100644 index 0000000000..2e3fcf9bcf --- /dev/null +++ b/.github/workflows/markdown-link-check.yml @@ -0,0 +1,25 @@ +# This only works for external links +# (local links are already checked in normal build) +name: Check Markdown links + +on: + push: + branches: + - master + pull_request: + +jobs: + markdown-link-check: + name: Check for broken links + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Run link check + uses: gaurav-nelson/github-action-markdown-link-check@v1 + with: + use-verbose-mode: 'yes' + folder-path: './' + config-file: '.github/workflows/.markdown-link-check_config.json' diff --git a/.github/workflows/smart-docs-monitor.yml b/.github/workflows/smart-docs-monitor.yml new file mode 100644 index 0000000000..197112d833 --- /dev/null +++ b/.github/workflows/smart-docs-monitor.yml @@ -0,0 +1,193 @@ +name: Smart Docs Monitor + +on: + schedule: + - cron: '0 0 * * *' # Run every 24 hours at midnight UTC + workflow_dispatch: + inputs: + hours_back: + description: 'Hours to look back for merged PRs' + required: false + default: '24' + +permissions: + contents: read + issues: write + id-token: write + +jobs: + monitor: + runs-on: ubuntu-latest + + steps: + - name: Checkout docs repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Fetch merged PRs and create review batch + id: batch + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + const repos = fs.readFileSync('repos-to-monitor.txt', 'utf8') + .split('\n') + .filter(line => line.trim() && !line.startsWith('#')); + + const hoursBack = parseInt('${{ github.event.inputs.hours_back || '24' }}'); + const since = new Date(Date.now() - hoursBack * 60 * 60 * 1000).toISOString(); + + let batchSummary = '# Merged PRs to Review\n\n'; + let prCount = 0; + + for (const repo of repos) { + const [owner, repoName] = repo.split('/'); + + try { + const { data: pulls } = await github.rest.pulls.list({ + owner, + repo: repoName, + state: 'closed', + sort: 'updated', + direction: 'desc', + per_page: 30 + }); + + const merged = pulls.filter(pr => + pr.merged_at && + new Date(pr.merged_at) > new Date(since) + ); + + if (merged.length > 0) { + batchSummary += `## ${repo}\n\n`; + + for (const pr of merged) { + batchSummary += `### PR #${pr.number}: ${pr.title}\n`; + batchSummary += `- **URL:** ${pr.html_url}\n`; + batchSummary += `- **Merged:** ${pr.merged_at}\n`; + batchSummary += `- **Diff:** ${pr.diff_url}\n`; + if (pr.body) { + batchSummary += `- **Description:** ${pr.body.substring(0, 200)}...\n`; + } + batchSummary += '\n'; + prCount++; + } + batchSummary += '\n'; + } + } catch (error) { + console.log(`Error with ${repo}:`, error.message); + } + } + + fs.writeFileSync('pr-batch.md', batchSummary); + console.log(`Found ${prCount} merged PRs`); + core.setOutput('pr_count', prCount); + return prCount; + + - name: Analyze PRs with Claude + if: steps.batch.outputs.pr_count > 0 + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + prompt: | + Review the merged PRs listed in `pr-batch.md` and determine which ones require documentation updates, new documentation, or removal/deprecation of existing docs. + + **Your Process:** + + 1. **Read pr-batch.md and CLAUDE.md** + - Follow CLAUDE.md's Review Process and "What Requires Documentation" guidelines + - See all merged PRs in pr-batch.md + + 2. **For each PR - Fetch and analyze the full diff:** + - Use the Diff URL from pr-batch.md to fetch the complete PR diff (use curl or gh api) + - Do NOT rely only on PR titles or summaries - read the actual code changes + - Extract user-facing changes: new/changed/removed public APIs, endpoints, CLIs, flags, config keys, setup instructions, breaking changes, renames + + 3. **Classify documentation impact** (may be multiple categories per PR): + a) **UPDATE** existing docs (content exists but is now incorrect/outdated) + b) **CREATE** new documentation (feature/API is not documented anywhere) + c) **REMOVE or DEPRECATE** documentation (feature/API was removed or deprecated) + + 4. **Thoroughly search our existing documentation:** + - Use Grep tool to search /concepts, /guides-and-tutorials, /reference, README.md for ALL relevant terms from the diff: + * Function/method names (e.g., "map-insert", "map_insert") + * API endpoints (e.g., "/v2/accounts", "accounts endpoint") + * CLI commands and flags (e.g., "stacks-node", "--config") + * Configuration keys (e.g., "peer_host", "miner.seed") + * Type/class names + - Try both case-sensitive and case-insensitive searches + - Try word variations (snake_case, kebab-case, camelCase) + + **Search results determine the impact:** + - **If NOT found:** Propose new doc with specific path + outline/content + - **If found:** Identify exact files/sections that are incorrect and need updating + - **If PR removes/deprecates something:** List ALL references across docs and propose removal/deprecation action + + 5. **Look for detection keywords in the diff:** + - "deprecate", "remove", "delete", "drop" → Check for docs to remove/deprecate + - "add", "new", "introduce", "implement" → Check if docs exist; if not, flag as CREATE + - "breaking", "rename", "change" → Check existing docs need UPDATE + - "BREAKING CHANGE" in commit messages → High priority + + 6. **Output JSON for issues to create:** + + For each PR needing documentation changes, create a JSON object with this structure: + + { + "title": "[Docs Update] [repo-name] PR #123: Brief description", + "body": "## Source PR\n- Repository: [repo-name]\n- PR: #123 - [PR title]\n- URL: [PR URL]\n- Merged: [date]\n\n## What Changed\n[Clear user-impact summary based on the DIFF, not just the PR description]\n\n## Documentation Impact\n\n### Updates to Existing Docs\n- [ ] `/path/to/file.md` (Section: X) - [Precise change needed; e.g., update signature, add new flag, fix example]\n\n### New Docs to Create\n- [ ] `/proposed/path/new-doc.md` - Title: [Proposed H1 title]\n **Suggested content:**\n ```markdown\n [Outline or initial content with examples, signatures, usage]\n ```\n\n### Docs to Remove or Deprecate\n- [ ] `/path/to/obsolete.md` - Action: [Remove/Deprecate]\n **Replacement:** [Link to new doc if applicable]\n **Rationale:** [Why; cite specific diff changes]\n\n## Recommended Changes\n\n### In `/path/to/file.md`\n\n**Section: [Section name]**\n\n[Specific text to add/change with concrete examples]\n\n## Priority\n[High/Medium/Low] - [Reason]\n\n---\n*Auto-generated by docs sync monitor*", + "labels": ["documentation", "auto-generated", "sync-needed", "[repo-name]", "priority-[level]", "[docs-update|docs-new|docs-remove]"] + } + + **Save the output as a JSON array to a file called `issues-to-create.json`** + + Example: + [ + { + "title": "[Docs Update] stacks-core PR #456: Add new Clarity function map-insert", + "body": "## Source PR\n...", + "labels": ["documentation", "auto-generated", "sync-needed", "stacks-core", "priority-high", "docs-new"] + } + ] + + 7. **Optimization and Quality:** + - Skip PRs with only internal refactoring, tests, or CI changes (follow CLAUDE.md skip list) + - Group multiple PRs affecting the same doc section into one issue when it makes sense + - Prioritize user-facing changes (APIs, features, setup, breaking changes) + - Be specific with file paths and sections - don't create vague "update docs" issues + - When uncertain about removal, prefer "deprecate with redirect" over hard delete + - Include concrete examples, signatures, and suggested content in recommendations + + **Important:** + - Only include PRs that genuinely need documentation updates/additions/removals + - Create GitHub issues DIRECTLY using the `gh issue create` command + - Do NOT create a JSON file - create issues immediately as you identify them + - Always fetch and read the actual PR diff - summaries are insufficient + + **Creating Issues:** + For each PR that needs documentation, create an issue using: + ```bash + gh issue create \ + --repo stacks-network/docs \ + --title "[Docs Update] [repo-name] PR #123: Brief description" \ + --body "$(cat <<'EOF' + ## Source PR + - Repository: [repo-name] + - PR: #123 - [PR title] + ... + EOF + )" \ + --label "documentation,auto-generated,sync-needed,[repo-name],priority-[level],[docs-update|docs-new|docs-remove]" + ``` + + **Important:** Create issues in the stacks-network/docs repository. + + After creating each issue, mark it as completed in your todo list before moving to the next one. + claude_args: | + --max-turns 40 + --model claude-sonnet-4-20250514 + --allowed-tools "Bash(curl*),Bash(gh*),Grep,Read,Write,glob" + + diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 903b453646..0000000000 --- a/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -# downstream content -_core -_browser -_android -_ios -_gaia - - -# OS or Editor folders -.DS_Store -node_modules - -# Jekyllg -_site -.sass-cache -.jekyll-metadata diff --git a/404.md b/404.md deleted file mode 100644 index 0514c4806a..0000000000 --- a/404.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: page -title: Feeling Lost -permalink: /404.html ---- - -## The page you are looking for cannot be found. Please navigate to [homepage]({{ site.url }}). diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..a488357872 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,390 @@ +# Documentation Sync System + +## Purpose +This repository monitors merged PRs across all Stacks Network and Hiro Systems projects and ensures documentation stays in sync with code changes. + +## Monitored Repositories + +### Stacks Network (21 repositories) +- **stacks-core** - Core Stacks blockchain implementation +- **clarity-wasm** - Clarity smart contract WASM implementation +- **docs** - This documentation repository +- **stacks-blockchain-docker** - Docker configurations for Stacks blockchain +- **clarunit** - Unit testing framework for Clarity +- **bitcoin** - Bitcoin integration components +- **c32check** - C32check encoding library +- **stacks** - Stacks utilities and tools +- **send-many-stx-cli** - CLI for batch STX transfers +- And 12 additional supporting repositories + +### Hiro Systems (8 repositories) +- **clarinet** - Clarity development environment and testing framework +- **stacks.js** - JavaScript libraries for Stacks +- **connect** - Wallet connection library (formerly stacks-connect) +- **stacks.js-starters** - Starter templates for Stacks.js +- **vitest-environment-clarinet** - Vitest environment for Clarity testing +- And 3 additional supporting repositories + +## Documentation Structure + +### Primary Documentation Sections +- `/concepts/` - Conceptual documentation about Stacks, PoX, Clarity, sBTC +- `/guides-and-tutorials/` - Hands-on tutorials and how-to guides +- `/reference/` - API reference and technical specifications +- `README.md` - Quick start and overview + +### Key Documentation Areas +- **Stacks blockchain concepts** - `/concepts/stacks-101/` +- **sBTC documentation** - `/concepts/sbtc/` +- **Clarity language** - `/concepts/clarity/` +- **Node operations** - `/guides-and-tutorials/nodes-and-miners/` +- **Signer operations** - `/guides-and-tutorials/running-a-signer/` +- **Stacking guides** - `/guides-and-tutorials/stack-stx/` +- **Developer quickstart** - `/guides-and-tutorials/hello-stacks-quickstart-tutorial.md` + +## Review Process + +### When Analyzing Merged PRs + +1. **Fetch the full diff** - Don't just read the PR summary + - Use `curl [diff_url]` or `gh api /repos/OWNER/REPO/pulls/NUMBER` to get complete changes + - Look at actual code changes, not just commit messages + +2. **Identify user impact** - Ask these questions: + - Does this change what developers see or do? + - Does this affect APIs, CLIs, or SDKs? + - Does this change configuration or setup steps? + - Does this introduce new features or capabilities? + - Does this break existing functionality? + +3. **Check documentation** - Search our docs for related content: + - Use `grep -r "relevant_term" concepts/ guides-and-tutorials/ reference/` + - Look for mentions of changed functions, APIs, or concepts + - Check if tutorials reference affected code + +4. **Be specific** - Point to exact files and sections that need updates + - Don't say "update the Clarity docs" + - Say "update `/concepts/clarity/language-functions.md` line 145 to reflect new `map-set` signature" + +### What Requires Documentation + +✅ **Always document:** +- New API endpoints or methods +- Changed function signatures (especially in public APIs) +- New Clarity functions or keywords +- New CLI commands or flags +- Modified configuration options +- Changed setup/installation steps +- Breaking changes (HIGH PRIORITY) +- New features or capabilities +- Changes to sBTC, stacking, or signing operations +- Updates to Stacks.js APIs +- Changes to Clarinet commands or configuration + +❌ **Skip documentation for:** +- Internal refactoring with no external impact +- Test-only changes +- CI/CD pipeline updates +- Dependency updates (unless breaking or adding new capabilities) +- Bug fixes that restore intended behavior without changing usage +- Performance optimizations that don't change APIs +- Internal code comments or documentation in code + +### Detection Scenarios + +When analyzing PRs, classify documentation impact into three categories: + +#### 1. UPDATE Existing Docs +**When:** Code changes make existing documentation incorrect or outdated. + +**Detection signals:** +- Modified function signatures in public APIs +- Changed CLI flag behavior or defaults +- Updated configuration schema +- Renamed types, methods, or modules +- Changed setup/installation steps +- Breaking changes that alter existing functionality + +**Example:** +``` +PR changes `map-set` to require a third parameter `ttl` +→ Search docs for "map-set" +→ Found in /concepts/clarity/language-functions.md +→ Issue: UPDATE the signature and examples to include ttl parameter +``` + +#### 2. CREATE New Docs +**When:** New functionality has zero documentation coverage. + +**Detection signals:** +- New public functions, methods, or classes +- New API endpoints +- New CLI commands or subcommands +- New configuration options +- New features or capabilities +- Keywords in diff: "add", "new", "introduce", "implement" + +**Example:** +``` +PR adds new Clarity function `map-insert` +→ Search docs for "map-insert", "map_insert" +→ NOT FOUND in any documentation +→ Issue: CREATE new section in /concepts/clarity/language-functions.md with signature, usage, examples +``` + +#### 3. REMOVE or DEPRECATE Docs +**When:** Features are removed or deprecated but docs still reference them. + +**Detection signals:** +- Removed functions, endpoints, or commands +- Deprecated APIs with warnings +- Deleted configuration options +- Keywords in diff: "remove", "delete", "deprecate", "drop" +- BREAKING CHANGE notices about removals + +**Example:** +``` +PR removes legacy endpoint `/v1/accounts` (deprecated in favor of `/v2/accounts`) +→ Search docs for "v1/accounts", "/v1/accounts" +→ Found in /guides-and-tutorials/api-quickstart.md and /reference/api-endpoints.md +→ Issue: REMOVE or DEPRECATE these references, add redirect note to v2 endpoint +``` + +**Search Strategy:** +For each scenario, thoroughly search using multiple term variations: +- Exact names (e.g., "map-insert") +- Case variations (case-sensitive and case-insensitive) +- Format variations (snake_case, kebab-case, camelCase) +- Partial matches (e.g., "map" functions) +- Related terms and synonyms + +### Priority Levels + +**High Priority** (Document immediately): +- Breaking changes to public APIs +- New major features (sBTC updates, new Clarity functions) +- Security-related changes +- Changes affecting node operators, signers, or stackers +- Setup/installation changes + +**Medium Priority** (Document soon): +- New optional features +- Enhanced functionality in existing features +- New CLI flags or options +- Performance improvements with usage implications +- Updated examples or best practices + +**Low Priority** (Document when convenient): +- Minor API additions that are self-explanatory +- Internal improvements with minor user-facing impact +- Clarifications or additional options + +### Issue Creation Guidelines + +When creating a documentation issue: + +**Title Format:** +``` +[Docs Update] [repo-name] PR #123: Brief description +``` + +**Body Structure:** +```markdown +## Source PR +- Repository: [owner/repo] +- PR: #123 - [PR title] +- URL: [PR URL] +- Merged: [YYYY-MM-DD] + +## What Changed +[2-3 sentences describing the functional changes from a user perspective] + +## Documentation Impact + +### Files Needing Updates +- [ ] `/path/to/file.md` - [Specific change needed] +- [ ] `/another/file.md` - [Specific change needed] + +## Recommended Changes + +### In `/path/to/file.md` + +**Section: [Section Name]** + +Add/Update: +``` +[Exact or suggested text to add/change] +``` + +Context: [Why this change is needed and where it fits] + +### In `/another/file.md` + +**Section: [Section Name]** + +Add/Update: +``` +[Exact or suggested text to add/change] +``` + +Context: [Why this change is needed and where it fits] + +## Priority +[High/Medium/Low] - [Reason] + +--- +*Auto-generated by docs sync monitor* +``` + +**Labels to Apply:** +- `documentation` (always) +- `auto-generated` (always) +- `sync-needed` (always) +- Repository name (e.g., `stacks-core`, `clarinet`, `stacks.js`) +- Priority level (e.g., `priority-high`, `priority-medium`, `priority-low`) + +### Example Good Issue + +**Title:** `[Docs Update] stacks-core PR #456: Add new Clarity built-in function map-insert` + +**Body:** +```markdown +## Source PR +- Repository: stacks-network/stacks-core +- PR: #456 - Add map-insert built-in function to Clarity +- URL: https://github.com/stacks-network/stacks-core/pull/456 +- Merged: 2025-09-15 + +## What Changed +Added a new Clarity built-in function `map-insert` that inserts a key-value pair into a map only if the key doesn't exist. Returns `(ok true)` if inserted, `(err false)` if key already exists. This complements the existing `map-set` function which always overwrites. + +## Documentation Impact + +### Files Needing Updates +- [ ] `/concepts/clarity/language-functions.md` - Add documentation for map-insert function +- [ ] `/guides-and-tutorials/clarity-hello-world.md` - Update map operations example +- [ ] `/reference/clarity-language-reference.md` - Add to function reference table + +## Recommended Changes + +### In `/concepts/clarity/language-functions.md` + +**Section: Map Functions** + +Add after the `map-set` documentation: + +```markdown +#### map-insert + +`(map-insert map-name key-tuple value-tuple)` + +Inserts a key-value pair into the specified map only if the key does not already exist. If the key exists, the map is unchanged and an error is returned. + +**Parameters:** +- `map-name` - The name of the map +- `key-tuple` - The key to insert +- `value-tuple` - The value to associate with the key + +**Returns:** `(response bool bool)` - Returns `(ok true)` if the key was inserted, `(err false)` if the key already existed. + +**Example:** +```clarity +(define-map scores principal uint) + +(map-insert scores tx-sender u100) ;; Returns (ok true) +(map-insert scores tx-sender u200) ;; Returns (err false) - key exists +``` + +Use `map-insert` when you need to ensure a key is only set once, such as initializing user records. Use `map-set` when you want to always update the value regardless of whether the key exists. +``` + +### In `/reference/clarity-language-reference.md` + +**Section: Built-in Functions Table** + +Add row to the Map Functions table: +``` +| map-insert | (map-insert map-name key value) | Inserts key-value pair if key doesn't exist | (response bool bool) | +``` + +## Priority +High - New language feature that developers need to know about immediately + +--- +*Auto-generated by docs sync monitor* +``` + +### Example Bad Issue (Don't Do This) + +❌ **Title:** `Update docs for PR #456` + +❌ **Body:** +```markdown +PR #456 was merged. It added some new Clarity stuff. Someone should update the docs. +``` + +**Why it's bad:** +- Vague title without context +- No specifics on what changed +- No guidance on what docs need updates +- No recommended changes +- No priority assessment + +## GitHub API Usage + +You have access to GitHub API via `gh` CLI: + +```bash +# Fetch PR details +gh api /repos/OWNER/REPO/pulls/NUMBER + +# Get file contents +gh api /repos/OWNER/REPO/contents/PATH + +# Create issues +gh issue create --repo OWNER/REPO \ + --title "[Docs Update] ..." \ + --body "..." \ + --label "documentation,auto-generated,sync-needed" + +# Search code +gh api /search/code?q=map-insert+repo:stacks-network/docs +``` + +## Grouping Related PRs + +If multiple PRs from the same repository affect the same documentation section, consider creating ONE issue that covers all changes: + +**Title:** `[Docs Update] [repo-name] Multiple PRs: Clarity map function updates` + +**Body includes all PRs:** +```markdown +## Source PRs +- PR #456 - Add map-insert function +- PR #457 - Add map-update function +- PR #458 - Deprecate map-set? function + +## What Changed +[Combined summary of all changes] + +[Rest of issue structure...] +``` + +## Testing and Validation + +Before creating an issue, verify: +1. ✅ The PR actually changed user-facing functionality +2. ✅ Our documentation references the changed functionality +3. ✅ The recommended changes are accurate and specific +4. ✅ The priority level is appropriate +5. ✅ Similar issues don't already exist + +## Notes for Claude Code Action + +When running in the GitHub Action context: +- You have full read access to this docs repository +- You can create issues but NOT commit changes directly +- Focus on creating high-quality, specific issues +- Batch API calls when possible to avoid rate limits +- Be conservative - it's better to skip a PR than create a low-quality issue +- If unsure about a PR's impact, include that uncertainty in the issue with a lower priority diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000000..f43d23b863 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,2 @@ +# These owners will be the default owners for everything in the repo. +* @stacks-network/stacks-foundation-docs diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 4a90445cea..0000000000 --- a/Gemfile +++ /dev/null @@ -1,33 +0,0 @@ -source "https://rubygems.org" -ruby RUBY_VERSION - -# Hello! This is where you manage which Jekyll version is used to run. -# When you want to use a different version, change it below, save the -# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: -# -# bundle exec jekyll serve -# -# This will help ensure the proper Jekyll version is running. -# Happy Jekylling! -gem "jekyll", "3.5" - -# This is the default theme for new Jekyll sites. You may change this to anything you like. -# gem "minima", "~> 2.0" - -# If you want to use GitHub Pages, remove the "gem "jekyll"" above and -# uncomment the line below. To upgrade, run `bundle update github-pages`. -# gem "github-pages", group: :jekyll_plugins - -# If you have any plugins, put them here! -group :jekyll_plugins do - gem "jekyll-feed", "~> 0.6" - gem 'jekyll-paginate', '~> 1.1' - gem 'jekyll-seo-tag' - gem 'jekyll-gist' - gem 'jekyll-livereload' - gem 'jekyll-avatar' - gem 'jekyll-titles-from-headings' -end - -# Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index f6a39a0f57..0000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,91 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - addressable (2.5.2) - public_suffix (>= 2.0.2, < 4.0) - colorator (1.1.0) - em-websocket (0.5.1) - eventmachine (>= 0.12.9) - http_parser.rb (~> 0.6.0) - eventmachine (1.2.7) - faraday (0.15.2) - multipart-post (>= 1.2, < 3) - ffi (1.9.25) - forwardable-extended (2.6.0) - http_parser.rb (0.6.0) - jekyll (3.5.0) - addressable (~> 2.4) - colorator (~> 1.0) - jekyll-sass-converter (~> 1.0) - jekyll-watch (~> 1.1) - kramdown (~> 1.3) - liquid (~> 4.0) - mercenary (~> 0.3.3) - pathutil (~> 0.9) - rouge (~> 1.7) - safe_yaml (~> 1.0) - jekyll-avatar (0.6.0) - jekyll (~> 3.0) - jekyll-feed (0.10.0) - jekyll (~> 3.3) - jekyll-gist (1.5.0) - octokit (~> 4.2) - jekyll-livereload (0.2.2) - em-websocket (~> 0.5) - jekyll (~> 3.0) - jekyll-paginate (1.1.0) - jekyll-sass-converter (1.5.2) - sass (~> 3.4) - jekyll-seo-tag (2.5.0) - jekyll (~> 3.3) - jekyll-titles-from-headings (0.5.1) - jekyll (~> 3.3) - jekyll-watch (1.5.1) - listen (~> 3.0) - kramdown (1.17.0) - liquid (4.0.0) - listen (3.1.5) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - ruby_dep (~> 1.2) - mercenary (0.3.6) - multipart-post (2.0.0) - octokit (4.10.0) - sawyer (~> 0.8.0, >= 0.5.3) - pathutil (0.16.1) - forwardable-extended (~> 2.6) - public_suffix (3.0.3) - rb-fsevent (0.10.3) - rb-inotify (0.9.10) - ffi (>= 0.5.0, < 2) - rouge (1.11.1) - ruby_dep (1.5.0) - safe_yaml (1.0.4) - sass (3.5.7) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - sawyer (0.8.1) - addressable (>= 2.3.5, < 2.6) - faraday (~> 0.8, < 1.0) - -PLATFORMS - ruby - -DEPENDENCIES - jekyll (= 3.5) - jekyll-avatar - jekyll-feed (~> 0.6) - jekyll-gist - jekyll-livereload - jekyll-paginate (~> 1.1) - jekyll-seo-tag - jekyll-titles-from-headings - tzinfo-data - -RUBY VERSION - ruby 2.3.3p222 - -BUNDLED WITH - 1.16.3 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..0e259d42c9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/README.md b/README.md index bc6497a514..6c71a0b591 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,57 @@ -## Docs site +[![License](https://img.shields.io/github/license/stacks-network/docs)](./LICENSE) -To run locally: +# Start Here -1. Get the content from the downstream repos. +## Stacks: The TL;DR - ``` - ./get-content.sh - ``` +**Stacks is the leading Bitcoin L2, bringing smart contract functionality to Bitcoin, without modifying Bitcoin itself.** -3. Build and serve locally. +Want to get a guided introduction to everything you need to know to become a Stacks developer? The Stacks Primer is a 5-day email course designed to take you from brand new to building your first contract, and even how to get paid for building out your own project. - ``` - bundle exec jekyll serve - ``` +[Take the Course](https://stacks.org/dev) - Use this format to turn on production features: +It does so through three key components, that we'll dig into in more detail in the rest of the docs: - ``` - JEKYLL_ENV=production bundle exec jekyll serve - ``` +#### Proof of Transfer -## Deploy via Netlify +Proof of Transfer (PoX) is the block production mechanism of the Stacks chain. Essentially, it attempts to recreate the block production patterns of PoW programmatically. Stacks miners spend BTC for a chance to mine new Stacks blocks. Under the hood, this block production mechanism anchors Stacks blocks to Bitcoin blocks, making it as hard to reverse a Stacks block as it is to reverse a Bitcoin block. That's a big claim, and we unpack it in further detail in the sections on Nakamoto block production. -To deploy to Netlify: +#### Clarity -1. Build the site. +Clarity is the smart contract language that Stacks uses. it has been designed from the ground up to make it easier for developers to write safe, secure smart contracts. Additionally, since it has been purpose-built for Stacks and Bitcoin, there are built-in functions for reading Bitcoin state, which means you can use Bitcoin state to perform actions in Clarity. For example, you could set up a check to make sure a particular Bitcoin transaction has occurred before executing a mint function in Clarity, which just so happens to be what happens with the third component: sBTC. - ``` - JEKYLL_ENV=production jekyll build - ``` -2. Force add the `_site` directory. +#### sBTC - ``` - git push -f origin - ``` +sBTC is the trust-minimized 2-way Bitcoin peg on the Stacks layer. sBTC is the key to making Bitcoin programmable and bringing full smart contract functionality to Bitcoin via Stacks. sBTC is not a federation, but operates as an open-network, decentralized 2-way peg solution to bring smart contract functionality to Bitcoin with as little counterparty risk as possible. There is an entire section of these docs dedicated to explaining sBTC. + +### AI-Powered Semantic Search + +Looking for something specific? These docs are integrated with AI-powered semantic search, hit `Cmd/Ctrl + K` to open up the search box and ask the docs whatever you like. + +### What Next? + +#### Learn About Stacks + +Looking to learn more about exactly how Stacks works? The "Learn" section in the left navigation is where you'll want to go. The "What is Stacks?" page is the best place to start your learning journey. This is where you can dive deep into exactly how Stacks works and learn about all the different building blocks. + +#### Build a Stacks Dapp + +Are you a developer itching to get building? The "Build" section is the best place to start. It will introduce you to the essential things you need to know to build on Stacks in just 30 minutes. After that, check the rest of the Guides & Tutorials to learn how to build things like DeFi apps, crowdfunding, and collectibles, among other use cases. + +#### Run a Stacks Node + +Looking to run a Stacks node? You can either run a follower node or a miner node. We have guides for how to do both on testnet and mainnet in the "Operate" section of the Guides. + +#### Run a Signer + +Signers are a critical component of the Stacks ecosystem and are in charge of validating and appending new Stacks blocks and sBTC transactions. We have an entire section dedicated to running a signer. + +#### Stack Your STX + +Stacking is one of the key components behind Stacks and the Proof of Transfer consensus mechanism. There are many different ways you can stack depending on if you are stacking solo, stacking in a pool, and running a signer or not. We have a section on stacking to walk you through the process no matter your situation. + +#### Get More Involved + +Looking to grow your career in the Stacks ecosystem? Be sure to start working on your own project and submit it to the [Code for STX](https://stacks.org/code-for-stx) program to earn STX every month just for working on your project. And, if you're feeling up to the challenge, apply to the Clarity Collective, an exclusive community of proven, committed Stacks builders all dedicated to becoming exceptional Stacks developers. + +Next up, dig into exactly what Stacks is and how it works 👇🏻 diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000000..35bc5744d1 --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,177 @@ +# Table of contents + +* [Start Here](README.md) + +## 🧠 Concepts + +* [Stacks 101](concepts/stacks-101/README.md) + * [What Is Stacks?](concepts/stacks-101/what-is-stacks.md) + * [Bitcoin Connection](concepts/stacks-101/bitcoin-connection.md) + * [Proof of Transfer](concepts/stacks-101/proof-of-transfer.md) + * [Stacks Among Other Layers](concepts/stacks-101/stacks-among-other-layers.md) + * [Financial Incentive and Security Budget](concepts/stacks-101/financial-incentive-and-security-budget.md) +* [Network Fundamentals](concepts/network-fundamentals/README.md) + * [Network Basics](concepts/network-fundamentals/network.md) + * [Mainnet and Testnets](concepts/network-fundamentals/mainnet-and-testnets.md) + * [Accounts](concepts/network-fundamentals/accounts.md) + * [Authentication](concepts/network-fundamentals/authentication.md) + * [Bitcoin Name System](concepts/network-fundamentals/bitcoin-name-system.md) + * [SIPs](concepts/network-fundamentals/sips.md) + * [Technical Specifications](concepts/network-fundamentals/technical-specifications/README.md) + * [Audits](concepts/network-fundamentals/technical-specifications/audits.md) +* [Block Production](concepts/block-production/README.md) + * [Mining](concepts/block-production/mining.md) + * [Signing](concepts/block-production/stackers-and-signing.md) + * [Bitcoin Finality](concepts/block-production/bitcoin-finality.md) + * [Bitcoin Reorgs](concepts/block-production/bitcoin-reorgs.md) + * [Stacking](concepts/block-production/stacking.md) +* [Transactions](concepts/transactions/README.md) + * [How Transactions Work](concepts/transactions/transactions.md) + * [Post Conditions](concepts/transactions/post-conditions.md) +* [Clarity](concepts/clarity/README.md) + * [Overview](concepts/clarity/overview.md) + * [Decidability](concepts/clarity/decidability.md) +* [sBTC](concepts/sbtc/README.md) + * [Core Features](concepts/sbtc/core-features.md) + * [sBTC Operations](concepts/sbtc/operations/README.md) + * [Deposit](concepts/sbtc/operations/deposit.md) + * [Withdrawal](concepts/sbtc/operations/withdrawal.md) + * [Deposit vs Withdrawal Times](concepts/sbtc/operations/deposit-withdrawal-times.md) + * [Emily API](concepts/sbtc/emily.md) + * [Peg Wallet UTXO](concepts/sbtc/peg-wallet-utxo.md) + * [Clarity Contracts](concepts/sbtc/clarity-contracts/README.md) + * [sBTC Bootstrap Signers](concepts/sbtc/clarity-contracts/sbtc-bootstrap-signers.md) + * [sBTC Registry](concepts/sbtc/clarity-contracts/sbtc-registry.md) + * [sBTC Token](concepts/sbtc/clarity-contracts/sbtc-token.md) + * [sBTC Deposit](concepts/sbtc/clarity-contracts/sbtc-deposit.md) + * [sBTC Withdrawal](concepts/sbtc/clarity-contracts/sbtc-withdrawal.md) + * [Auxiliary Features](concepts/sbtc/auxiliary-features/README.md) + * [Transaction Fee Sponsorship](concepts/sbtc/auxiliary-features/fee-sponsorship.md) + * [Signer Wallet Rotation](concepts/sbtc/auxiliary-features/signer-wallet-rotation.md) + * [Walkthroughs](concepts/sbtc/walkthroughs/README.md) + * [Signer Process Walkthrough](concepts/sbtc/walkthroughs/signer-process.md) + * [sBTC Transaction Walkthrough](concepts/sbtc/walkthroughs/sbtc-transaction-lifecycle.md) + * [sBTC FAQ](concepts/sbtc/sbtc-faq.md) + * [sBTC Audits](concepts/sbtc/sbtc-audits.md) +* [Gaia](concepts/gaia/README.md) + * [Configuration](concepts/gaia/configuration.md) + * [Deploy Gaia Hub](concepts/gaia/deploy-gaia-hub.md) + * [Amazon EC2](concepts/gaia/amazon-ec2.md) + * [Linux](concepts/gaia/linux.md) + * [Mac OS](concepts/gaia/mac-os.md) + +## 🛠️ Guides & Tutorials + +* [Developer Quickstart](guides-and-tutorials/hello-stacks-quickstart-tutorial.md) +* [Clarity Crash Course](guides-and-tutorials/clarity-crash-course.md) +* [Build a Borrowing & Lending Protocol](guides-and-tutorials/build-a-borrowing-and-lending-protocol.md) +* [Bitcoin Integration](guides-and-tutorials/bitcoin-integration/README.md) + * [Sending Bitcoin with Leather Wallet](guides-and-tutorials/bitcoin-integration/sending-bitcoin-with-leather-wallet.md) + * [Verifying a Bitcoin Transaction](guides-and-tutorials/bitcoin-integration/verifying-a-bitcoin-transaction.md) + * [Parsing a Bitcoin Transaction](guides-and-tutorials/bitcoin-integration/parsing-a-bitcoin-transaction.md) +* [Create Tokens](guides-and-tutorials/tokens/README.md) + * [Creating a NFT](guides-and-tutorials/tokens/creating-a-nft.md) + * [Creating a Fungible Token](guides-and-tutorials/tokens/creating-a-fungible-token.md) +* [Build a Frontend](guides-and-tutorials/frontend/README.md) + * [Post Conditions with Stacks.js](guides-and-tutorials/frontend/post-conditions-with-stacks.js.md) + * [Authentication with Stacks.js](guides-and-tutorials/frontend/authentication-with-stacks.js.md) + * [Sending Transactions with Stacks.js](guides-and-tutorials/frontend/sending-transactions-with-stacks.js.md) +* [Run a Node](guides-and-tutorials/nodes-and-miners/README.md) + * [Run a Node with Docker](guides-and-tutorials/nodes-and-miners/run-a-node-with-docker.md) + * [Run a Node with Digital Ocean](guides-and-tutorials/nodes-and-miners/run-a-node-with-digital-ocean.md) + * [Run a Node with a Hosted Provider](guides-and-tutorials/nodes-and-miners/run-a-node-with-a-hosted-provider.md) + * [Run a Node with Quicknode](guides-and-tutorials/nodes-and-miners/run-a-node-with-quicknode.md) + * [Run a Bitcoin Node](guides-and-tutorials/nodes-and-miners/run-a-bitcoin-node.md) + * [Run a Pruned Bitcoin Node](guides-and-tutorials/nodes-and-miners/run-a-pruned-bitcoin-node.md) +* [Run a Miner](guides-and-tutorials/run-a-miner/README.md) + * [Miner Prerequisites](guides-and-tutorials/run-a-miner/miner-prerequisites.md) + * [Miner Costs and Fees](guides-and-tutorials/run-a-miner/miner-costs-and-fees.md) + * [Mine Testnet Stacks Tokens](guides-and-tutorials/run-a-miner/mine-testnet-stacks-tokens.md) + * [Mine Mainnet Stacks Tokens](guides-and-tutorials/run-a-miner/mine-mainnet-stacks-tokens.md) + * [Verify Miner](guides-and-tutorials/run-a-miner/verify-miner.md) +* [Run a Signer](guides-and-tutorials/running-a-signer/README.md) + * [Signer Quickstart](guides-and-tutorials/running-a-signer/signer-quickstart.md) + * [How to Read Signer Logs](guides-and-tutorials/running-a-signer/how-to-read-signer-logs.md) + * [How to Monitor a Signer](guides-and-tutorials/running-a-signer/how-to-monitor-signer.md) + * [Best practices for running a Signer](guides-and-tutorials/running-a-signer/best-practices-to-run-a-signer.md) + * [OpSec Best Practices](guides-and-tutorials/running-a-signer/opsec-best-practices.md) +* [sBTC](guides-and-tutorials/sbtc/README.md) + * [sBTC Builder Quickstart](guides-and-tutorials/sbtc/sbtc-builder-quickstart.md) + * [How to Run an sBTC Signer](guides-and-tutorials/sbtc/how-to-run-sbtc-signer.md) + * [Best practices for running an sBTC Signer](guides-and-tutorials/sbtc/best-practices-for-running-an-sbtc-signer.md) + * [How to Use the sBTC Bridge](guides-and-tutorials/sbtc/how-to-use-the-sbtc-bridge.md) + * [Earn sBTC Rewards](guides-and-tutorials/sbtc/earn-sbtc-rewards.md) +* [Stack STX](guides-and-tutorials/stack-stx/README.md) + * [Solo Stack](guides-and-tutorials/stack-stx/stacking-flow.md) + * [Operate a Pool](guides-and-tutorials/stack-stx/operate-a-pool.md) + * [Stack with a Pool](guides-and-tutorials/stack-stx/stack-with-a-pool.md) + * [Increase Stacked Position](guides-and-tutorials/stack-stx/increase-stacking.md) + * [Stop Stacking](guides-and-tutorials/stack-stx/stop-stacking.md) +* [Snapshot the chainstate](guides-and-tutorials/best-practices-to-snapshot-the-chainstate.md) +* [Oracles](guides-and-tutorials/oracles.md) +* [Community Tutorials](guides-and-tutorials/community-tutorials.md) + +## 📚 Reference + +* [API](reference/api.md) +* [Clarity Types](reference/types.md) +* [Clarity Functions](reference/functions.md) +* [Clarity Keywords](reference/keywords.md) +* [Stacks Node Configuration](reference/stacks-node-configuration.md) +* [Signer Configuration](reference/sample-configuration-files.md) +* [Stacks Tooling](reference/the-stack.md) + +## 🏗️ Example Contracts + +* [Audited Starter Contracts](example-contracts/audited-starter-contracts.md) +* [Stacking](example-contracts/stacking.md) +* [BNS](example-contracts/bns.md) +* [Multi Send](example-contracts/multi-send.md) + +## 🧡 Press & Top Links + +* [🔶 2024](press-and-top-links/2024/README.md) + * [🔸 January 2024](press-and-top-links/2024/january-2024.md) + * [🔸 February 2024](press-and-top-links/2024/february-2024.md) + * [🔸 March 2024](press-and-top-links/2024/march-2024.md) + * [🔸 April 2024](press-and-top-links/2024/april-2024.md) + * [🔸 May 2024](press-and-top-links/2024/may-2024.md) + * [🔸 June 2024](press-and-top-links/2024/may-2024-1.md) + * [🔸 July 2024](press-and-top-links/2024/may-2024-2.md) + * [🔸 August 2024](press-and-top-links/2024/may-2024-3.md) + * [🔸 September 2024](press-and-top-links/2024/september-2024.md) + * [🔸 October 2024](press-and-top-links/2024/october-2024.md) + * [🔸 November 2024](press-and-top-links/2024/october-2024-1.md) + * [🔸 December 2024](press-and-top-links/2024/october-2024-1-1.md) +* [🔶 2025](press-and-top-links/2025/README.md) + * [🔸 January 2025](press-and-top-links/2025/january-2024.md) + * [🔸 February 2025](press-and-top-links/2025/february-2025.md) + * [🔸 March 2025](press-and-top-links/2025/march-2025.md) + * [🔸 April 2025](press-and-top-links/2025/march-2025-1.md) + * [🔸 May 2025](press-and-top-links/2025/march-2025-2.md) + * [🔸 June 2025](press-and-top-links/2025/june-2025.md) + * [🔸 July 2025](press-and-top-links/2025/june-2025-1.md) + * [🔸 August 2025](press-and-top-links/2025/june-2025-2.md) + * [🔸 September 2025](press-and-top-links/2025/june-2025-2-1.md) + +## 🧡 Bitcoin Theses and Reports + +* [🟠 Bitcoin Theses](bitcoin-theses-and-reports/bitcoin-theses.md) +* [📙 Bitcoin Reports](bitcoin-theses-and-reports/bitcoin-reports.md) + +*** + +* [Contribute](contribute.md) + +## 🟧 Nakamoto Upgrade + +* [Nakamoto Upgrade - Start Here](nakamoto-upgrade/nakamoto-upgrade-start-here.md) +* [What is the Nakamoto Upgrade?](nakamoto-upgrade/what-is-the-nakamoto-release.md) +* [Nakamoto in 10 Minutes](nakamoto-upgrade/nakamoto-in-10-minutes.md) +* [Nakamoto Rollout Plan](nakamoto-upgrade/nakamoto-rollout-plan/README.md) + * [Nakamoto for Stackers](nakamoto-upgrade/nakamoto-rollout-plan/nakamoto-for-stackers.md) + * [Nakamoto for Exchanges](nakamoto-upgrade/nakamoto-rollout-plan/nakamoto-for-exchanges.md) + * [Nakamoto for Stacking Providers](nakamoto-upgrade/nakamoto-rollout-plan/nakamoto-for-stacking-providers.md) + * [Nakamoto for App Developers](nakamoto-upgrade/nakamoto-rollout-plan/nakamoto.md) +* [Setting Up a Primary Nakamoto Testnet Node - Signers](nakamoto-upgrade/setting-up-a-primary-post-nakamoto-testnet-node.md) +* [Nakamoto Activation Guide for Signers](nakamoto-upgrade/nakamoto-activation-guide-for-signers.md) diff --git a/THEME_README.md b/THEME_README.md deleted file mode 100644 index 0f4a93e4e3..0000000000 --- a/THEME_README.md +++ /dev/null @@ -1,351 +0,0 @@ -# Docs is a premium documentation Jekyll theme - -Desk was developed by [Ivan Chromjak](https://ivanchromjak.com) for [jekyll.plus](https://jekyll.plus/), theme [live demo](https://docs.jekyll.plus/) available. - -## Features - -* Contact form -* Live Search -* Responsive videos -* Image lightbox -* Google maps -* Github avatar -* Changelog page -* Contact form (FormSpree) -* Pre-built pages -* Disqus comments for posts -* Configurable home page header images -* Optimised for [GitHub](https://pages.github.com/) pages -* RSS feed -* SEO tags -* Google Analytics - - -## Installation - -Install the dependencies with [Bundler](http://bundler.io/): - -```bash -bundle install -``` - -Run the following to generate your site: -```bash -bundle exec jekyll serve -``` - -You can find more on [Deployment Methods](https://jekyllrb.com/docs/deployment-methods/) page on Jekyll website. - -## Setup - -### Site and author details -Add your site and author details in `_config.yml`: -```yaml -# Site title and description -title: Docs - Documentation Jekyll Theme -description: Documentation Jekyll theme. - -# Site base hostname & protocol, e.g. http://example.com -url: "https://docs.jekyll.plus" - -# Site logo, image or text -brand: - image: touch-icon.svg # e.g. logo.png, upload logo image file to /assets/img/ folder - text: Docs # if the above "logo:" image variable is not set, this text logo is displayed instead - -# Default author settings -author: - name: John Smith - github: PressApps # Github username for avatar - -# Author settings, displayed on post and doc pages if front matter references author name e.g. author: peter -authors: - peter: - name: Peter Brown - github: PressApps # Github username for avatar - -# Social icons displayed in footer -social: - email: - website: - facebook: https://www.facebook.com/ - flickr: - dribbble: - github: - googleplus: - instagram: https://www.instagram.com/ - linkedin: - pinterest: - twitter: https://twitter.com/ - vimeo: https://vimeo.com/ - youtube: - -# Twitter share button -twitter_username: - -``` - -### Navigation Bar -Set in the main navigation links in `_data/navigation_header.yml`: -```yaml -- title: About - url: /about/ -``` - -### Footer - -Edit copyright notice in `_config.yml`: -```yaml -footer: - copyright: -``` - -Set in the navigation links in `_data/navigation_footer.yml`: -```yaml -- title: About - url: /about/ -``` - -### Enabling comments (via Disqus) - -Optionally, if you have a Disqus account, you can tell Jekyll to use it to show a comments section below each post. To enable it, add the following lines to your Jekyll site: - -```yaml -disqus: - shortname: my_disqus_shortname -``` - -You can find out more about Disqus' shortnames [here](https://help.disqus.com/customer/portal/articles/466208). - -Comments are enabled by default and will only appear in production, i.e., `JEKYLL_ENV=production`. If you don't want to display comments for a particular post you can disable them by adding `comments: false` to that post's YAML Front Matter. - -### Google Analytics - -To enable Google Anaytics, add the following lines to your Jekyll site: - -```yaml - google_analytics: UA-NNNNNNNN-N -``` - -Google Analytics will only appear in production, i.e., `JEKYLL_ENV=production` - -### Google Map - -To display Google map on contact page, add the following in your page content, replacing latitude, longitude and zoom values: - -```yaml -{% include map.html latitude="40.6700" longitude="-73.9400" zoom="16" %} -``` - -### Contact Form (via FormSpree) - -Submit the form and confirm your email address at [FormSpree](https://formspree.io/). Then add the following lines to contact page YAML Front Matter, replacing the email address: - -```yaml -formspree: - email: my_name@gmail.com - redirect: /thanks/ -``` - -### Update favicon - -You can find the current favicon (favicon.png) inside the theme `/assets/img/` directory, just replace it with your new favicon. - -## Posts - -To create a new post, you can create a new markdown file inside the `_posts` directory by following the recommended file naming format: -``` -YEAR-MONTH-DAY-title.MARKUP -``` -Where `YEAR` is a four-digit number, `MONTH` and `DAY` are both two-digit numbers, and `MARKUP` is the file extension representing the format used in the file. For example, the following are examples of valid post filenames: - -``` -2011-12-31-new-years-eve-is-awesome.md -2012-09-12-how-to-write-a-blog.md -``` - -Post requires front matter, everything in between the first and second --- are part of the YAML Front Matter, and everything after the second --- will be rendered with Markdown and show up as “Content”. -The following is a post file with different configurations you can add as example: - -```yaml ---- -layout: post -title: How To Travel On Low Budget ---- -``` - -You can rebuild the site in many different ways, but the most common way is to run `bundle exec jekyll serve`, which launches a web server and auto-regenerates your site when a file is updated. - -To keep things more organized, add post images to `/assets/posts/` directory, and add theme images to `/assets/img/` directory. - -### Adding images -To add an image to a post or page use the following codes: -Local image from `/assets/posts/` directory: -```yaml -{% include image.html img="girl.jpg" alt="Alt for image" caption="Girl on a rock" %} -``` -External wide image with lightbox: -```yaml -{% include image.html img="https://source.unsplash.com/TT-ROxWj9nA.jpg" lightbox="true" alt="Alt for image" caption="Image in lightbox" %} -``` - -### Adding table of contents -Add the following code at the top of the post: -``` -#### Sections in this article -{:.no_toc} -* TOC -{:toc} -``` -`{:.no_toc}` excludes the `#### Sections in this article` title from indexing in table of contents - -### Responsive Videos -Embed local videos: -```html - -``` -Embed YouTube videos: -```html - -``` - -To create a draft post, create the post file under the `_drafts` directory, and you can find more information in [Working with Drafts](https://jekyllrb.com/docs/drafts/). - -## Home Page - -To create a home page, just create a `index.md` file inside the root directory. The following is a YAML Front Matter example for a page: - -```yaml ---- -layout: home -hero: - title: How Can We Help? # hero section settings - subtitle: Search or browse in depth articles and videos on everything Jekyll, from basic theme setup and hosting to customisation and development - image: imac.svg # display small image above title - search: true # enable search -categories: - columns: 3 # number of category columns; 1, 2, 3, 4 - title: Browse Topics - subtitle: Get your answers fast, jump to most popular documentation content -featured: # featured docs section settings - title: Popular Articles - tag: featured # tag used to populate featured section on home page -section: # display page content - title: Need More? - subtitle: This section displays optional page content lorem ipsum -cta: # call to action section - title: Didn't find an answer to your question? - subtitle: Get in touch with us for details on additional services and custom work pricing - button_text: Contact Us - button_url: /contact/ ---- -``` - -Home page category boxes are added in `_data/navigation_home.yml`, e.g.: -```yml -- title: Getting Started - desc: Get your account up and running in just few easy steps - icon: settings - doc: usage - -- title: Account and Billing - desc: Managing your account, creating new users and exporting data - icon: credit-card - doc: drafts -``` - -All available icons can be found [here](https://getuikit.com/docs/icon#library). - -## Docs - -To create a document post, just create a new page inside the root directory and add the following code in content: -``` -{% include faqs.html %} -``` - -Create new doc post entries in `_docs` folder, similar to creating posts, but with following front matter settings: - -```yml ---- -layout: doc -title: Category hosting Setting up new domain and page -subtitle: This is optional doc subtitle -tags: featured development -author: peter ---- -``` - -Sidebar navigation on docs post can edited in `_data/navigation_docs.yml`: - -```yml -- title: Getting Started # Section title - docs: - - home # Doc file name from _docs folder - - quickstart - - installation - - windows -``` - -## Changelog page - -Create new page with the following front matter: - -```yml ---- -layout: changelog -title: Changelog -permalink: /changelog/ ---- -``` - -Changelog enties are added in `_data/changelog.yml`: - -```yml -- title: Version 0.6.0 - label: - date: Aug 15, 2017 - list: - - Added style support for radio and checkbox in Firefox - - Removed class from Section component -``` - - -## Customization - -To modify the primary color, open `/_sass/theme/variables.scss` and replace the color values e.g.: - -```scss -// Main content -$color-main: #0F1214; -``` - -Further style customisation can be done in the following files: -``` -/_sass/theme/mixins.scss -/_sass/theme/variables.scss -/assets/css/main.scss -``` - -## Development - -Install [UIkit](https://getuikit.com/) font end framework dependency via Npm: -```bash -npm install -``` -Enable live browser reload with the following: -```bash -bundle exec jekyll s --livereload -``` - -## Credits and Sources - -- Google analytics https://www.google.com/analytics/ -- Google maps https://www.google.com/maps -- UIkit front end framework https://getuikit.com/ -- Jekyll CML https://jekyllrb.com/ - -## Support -Customer support is provided through our Envato profile page [contact form](https://themeforest.net/user/pressapps) for up to six months from the purchase date and is provided Monday to Friday during the business week. We aim to answer all support requests daily, most are handled within 48h. diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 96e20145ad..0000000000 --- a/_config.yml +++ /dev/null @@ -1,129 +0,0 @@ -# Welcome to Jekyll! -# -# This config file is meant for settings that affect your whole blog. -# For technical reasons, this file is *NOT* reloaded automatically when you use -# 'bundle exec jekyll serve'. If you change this file, please restart the server process. - -# Site title and description -title: Blockstack -description: Docs - -# Site subpath, e.g. /blog -baseurl: "" - -# Permalink URLs structure, for permalink style options see: https://jekyllrb.com/docs/permalinks/ -# permalink: /:title/ - -# Site base hostname & protocol, e.g. http://example.com -url: "https://zbabystack.netlify.com" - - -github_editme_path: moxiegirl/docs-new/blob/master -# if you're using Github, provide the basepath to the branch you've created for reviews, following the sample here. if not, leave this value blank. - - -# Site logo, image or text -brand: - image: logo.png # e.g. logo.png, upload logo image file to /assets/img/ folder - text: Docs # if the above "logo:" image variable is not set, this text logo is displayed instead - -# Default author settings -author: - name: Blockstack - github: Blockstack # Github username for avatar - -# Author settings, displayed on post and doc pages if front matter references author name e.g. author: peter -authors: - peter: - name: Peter Brown - github: PressApps # Github username for avatar - -# Social icons displayed in footer -social: - email: - website: - facebook: https://www.facebook.com/ - flickr: - dribbble: - github: - googleplus: - instagram: https://www.instagram.com/ - linkedin: - pinterest: - twitter: https://twitter.com/ - vimeo: https://vimeo.com/ - youtube: - -# Twitter share button -twitter_username: - -# Default footer image settings -footer: - copyright: Blockstack - -# Disqus comments shortname, requires Disqus account https://disqus.com/ -disqus: - shortname: # 1234 - -# Google analytics code, get your code here https://www.google.com/analytics/ -google_analytics: - -# Google maps API key, get your key here: https://developers.google.com/maps/documentation/javascript/get-api-key -google_maps_api_key: - -# Number of posts displayed on blog page -paginate: 10 - -# Blog path -paginate_path: "/news/:num/" - -# Path to post content assets directory i.e post images, pdfs etc -post_assets: /assets/posts/ -#keep_files: (`/images/`) - - -# Build settings -markdown: kramdown -highlighter: rouge - -plugins: - - jekyll-feed - - jekyll-seo-tag - - jekyll-gist - - jekyll-avatar - - jekyll-titles-from-headings - -titles_from_headings: - enabled: true - strip_title: true - collections: true - -exclude: - - Gemfile - - Gemfile.lock - - node_modules - - README.md - - THEME_README.md - - collections.json - - get-content.sh - -sass: - style: compressed - -collections: - android: - output: true - core: - output: true - gaia: - output: true - learn: - output: true - naming: - output: true - newinternet: - output: true - auth: - output: true - ios: - output: true diff --git a/_data/changelog.yml b/_data/changelog.yml deleted file mode 100644 index 5d3cbfc338..0000000000 --- a/_data/changelog.yml +++ /dev/null @@ -1,38 +0,0 @@ -# Changelog page source - -- title: Version 1.0.0 - label: STABLE - date: Oct 21, 2017 - list: - - Added Slideshow component - - Added style support for radio and minusbox in Firefox - - Removed class from Section component - - Allow fullscreen mode for videos in Lightbox - - Fixed responsive images in modal for IE11 - - Fix Grid and Margin component for cells with no height - - Larger horizontal padding for form input and textarea - -- title: Version 1.0.0 - label: BETA 1 - date: Sep 01, 2017 - list: - - Allow fullscreen mode for YouTube videos in Lightbox - - Fix icons not displaying if connected in rapid succession - - Fix scrollbar jumping in Switcher - -- title: Version 0.6.0 - label: - date: Aug 15, 2017 - list: - - Added style support for radio and checkbox in Firefox - - Removed class from Section component - - Add workaround to mitigate the duplicating icons issue - - Fixed responsive images in modal for IE11 - -- title: Version 0.5.0 - label: - date: Oct 21, 2017 - list: - - Media options now support any valid media query syntax - - Added style support for radio and checkbox in Firefox - - Fix whitespace trimming in dist diff --git a/_data/comments/overview/entry1536085765430.yml b/_data/comments/overview/entry1536085765430.yml deleted file mode 100644 index d9f13f83df..0000000000 --- a/_data/comments/overview/entry1536085765430.yml +++ /dev/null @@ -1,5 +0,0 @@ -_id: 732c7800-b070-11e8-9ee0-558513fcc2ac -name: moxiegirl -email: 30143f7aec79ae833027367ff0d56f6d -message: this is a test -date: 1536085765 diff --git a/_data/navigation_auth.yml b/_data/navigation_auth.yml deleted file mode 100644 index 69b62baa37..0000000000 --- a/_data/navigation_auth.yml +++ /dev/null @@ -1,4 +0,0 @@ -- docs: - - core/auth/overview - - core/auth/howitworks - - core/auth/howtouse diff --git a/_data/navigation_bns.yml b/_data/navigation_bns.yml deleted file mode 100644 index 4466e8274a..0000000000 --- a/_data/navigation_bns.yml +++ /dev/null @@ -1,25 +0,0 @@ -- title: Overview - docs: - - core/naming/introduction - - core/naming/architecture - - core/naming/namespaces - -- title: Tutorials - docs: - - core/naming/tutorial_creation - - core/naming/tutorial_subdomains - - -- title: How to use BNS - docs: - - core/naming/pickname - - core/naming/resolving - - core/naming/register - - core/naming/manage - - core/naming/subdomains - -- title: Other topics - docs: - - core/naming/forks - - core/naming/did - - core/naming/comparison diff --git a/_data/navigation_docs.yml b/_data/navigation_docs.yml deleted file mode 100644 index a71d813f94..0000000000 --- a/_data/navigation_docs.yml +++ /dev/null @@ -1,25 +0,0 @@ -- title: App Development Tutorial - docs: - - docs/about - - docs/decide - - docs/idea - - docs/build - - docs/register - -- title: Feature examples - docs: - - docs/tutorials/hello-blockstack - - docs/tutorials/multi-player-storage - -- title: Reference - docs: - - apis/jsdocs/index - - docs/faq - - docs/glossary - -- title: Contributing - docs: - - docs/contribute - - docs/issues - - docs/pullrequest - - docs/community diff --git a/_data/navigation_footer.yml b/_data/navigation_footer.yml deleted file mode 100644 index 11f372665e..0000000000 --- a/_data/navigation_footer.yml +++ /dev/null @@ -1,10 +0,0 @@ -# Footer navigation links - -- title: Blockstack.org - url: https://blockstack.org - -- title: Forums - url: https://forum.blockstack.org/ - -- title: GitHub - url: https://github.com/blockstack diff --git a/_data/navigation_gaia.yml b/_data/navigation_gaia.yml deleted file mode 100644 index fbe91048e8..0000000000 --- a/_data/navigation_gaia.yml +++ /dev/null @@ -1,18 +0,0 @@ -- title: About - docs: - - gaia/overview - - gaia/control - - gaia/writeread - - gaia/access - -- title: Tutorials - docs: - - gaia/hello-gaia - - gaia/media - - gaia/sharing - -- title: How to - docs: - - gaia/run-hub - - gaia/configure - - gaia/test diff --git a/_data/navigation_header.yml b/_data/navigation_header.yml deleted file mode 100755 index baae16c1bc..0000000000 --- a/_data/navigation_header.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Navbar menu navigation links -- title: Blockstack.org - url: https://blockstack.org - -- title: Forums - url: https://forum.blockstack.org/ - -- title: GitHub - url: https://github.com/blockstack diff --git a/_data/navigation_home.yml b/_data/navigation_home.yml deleted file mode 100644 index 91302f26c0..0000000000 --- a/_data/navigation_home.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Categories home page navigation - -- title: Evaluate Blockstack - desc: Learn about the technology behind Blockstack. Understand the value it offers and how it provides it. - icon: settings - doc: learn/overview - -- title: Use the New Internet - desc: Learn about the New Internet and its applications. Create an identity and learn how to use it. - icon: settings - doc: newinternet/privacy - -- title: Build Apps and Earn Money - desc: Learn how to build an application that earns with Blockstack. - icon: settings - doc: android/tutorial - -- title: Use the Naming Service - desc: Managing your account, create new users and exporting data - icon: cog - doc: core/naming/introduction - -- title: Implement Authentication - desc: Managing your account, creating new users and exporting data - icon: cog - doc: core/auth/overview - -- title: Implement Storage with GAIA - desc: Backend storage drivers and interactions between developer APIs and the Gaia service. - icon: cog - doc: gaia/overview diff --git a/_data/navigation_learn.yml b/_data/navigation_learn.yml deleted file mode 100644 index a9a43a28f0..0000000000 --- a/_data/navigation_learn.yml +++ /dev/null @@ -1,7 +0,0 @@ -- title: Introduction - docs: - -- title: SDKs - docs: - - android/tutorial - - ios/tutorial diff --git a/_data/navigation_newinternet.yml b/_data/navigation_newinternet.yml deleted file mode 100644 index 3fe12a8a01..0000000000 --- a/_data/navigation_newinternet.yml +++ /dev/null @@ -1,18 +0,0 @@ -- title: Introduction - docs: - - newinternet/overview - - newinternet/responsibility - - newinternet/privacy - - newinternet/community - - -- title: Get started - docs: - - newinternet/browser-introduction - - newinternet/ids-introduction - - newinternet/findapps - - newinternet/install - -- title: Reference - docs: - - newinternet/glossary diff --git a/_includes/comment.html b/_includes/comment.html deleted file mode 100644 index 06ae059962..0000000000 --- a/_includes/comment.html +++ /dev/null @@ -1,49 +0,0 @@ -
-
- {% if include.avatar %} - {{ include.name | escape }} - {% elsif include.email %} - {{ include.name | escape }} - {% else %} - {{ include.name | escape }} - {% endif %} -
-

- {% unless include.url == blank %} - - {% if include.name == site.author.name %} {% endif %}{{ include.name }} - - {% else %} - {% if include.name == site.author.name %} {% endif %}{{ include.name }} - {% endunless %} -

-
- {% if include.date %} - {% if include.index %}{% endif %} - - {% if include.index %}{% endif %} - {% endif %} -
-
- {{ include.message | markdownify }} -
- {% unless include.replying_to != 0 or page.comments_locked == true %} - - {% endunless %} -
- -{% capture i %}{{ include.index }}{% endcapture %} -{% assign replies = site.data.comments[page.slug] | sort | where_exp: 'comment', 'comment[1].replying_to == i' %} -{% for reply in replies %} - {% assign index = forloop.index | prepend: '-' | prepend: include.index %} - {% assign replying_to = reply[1].replying_to | to_integer %} - {% assign avatar = reply[1].avatar %} - {% assign email = reply[1].email %} - {% assign name = reply[1].name %} - {% assign url = reply[1].url %} - {% assign date = reply[1].date %} - {% assign message = reply[1].message %} - {% include comment.html index=index replying_to=replying_to avatar=avatar email=email name=name url=url date=date message=message %} -{% endfor %} diff --git a/_includes/contribute_code.md b/_includes/contribute_code.md deleted file mode 100644 index be2c2b9836..0000000000 --- a/_includes/contribute_code.md +++ /dev/null @@ -1,93 +0,0 @@ -### Sections in this article -{:.no_toc} -* TOC -{:toc} - -Good pull requests—patches, improvements, new features—are a fantastic help. They should remain focused in scope and avoid containing unrelated commits. - -**Please ask first** before embarking on any significant pull request (e.g. implementing features, refactoring code, porting to a different language), otherwise you risk spending a lot of time working on something that the project's developers might not want to merge into the project. - -Please adhere to the [coding guidelines](#code-guidelines) used throughout the project (indentation, accurate comments, etc.) and any other requirements (such as test coverage). - -When contributing to Blockstack's documentation, you should edit the documentation source files in [the `/app/` directory of the `master` branch](https://github.com/blockstack/blockstack-browser/tree/master/app). - -Adhering to the following process is the best way to get your work included in the project: - -1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork, - and configure the remotes: - - ```bash - # Clone your fork of the repo into the current directory - git clone https://github.com//blockstack-browser.git - # Navigate to the newly cloned directory - cd blockstack-browser - # Assign the original repo to a remote called "upstream" - git remote add upstream https://github.com/blockstack/blockstack-browser.git - ``` - -2. If you cloned a while ago, get the latest changes from upstream: - - ```bash - git checkout master - git pull upstream master - ``` - -3. Create a new topic branch (off the main project development branch) to - contain your feature, change, or fix: - - ```bash - git checkout -b - ``` - -4. Commit your changes in logical chunks. Please adhere to these [git commit - message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) - or your code is unlikely be merged into the main project. Use Git's - [interactive rebase](https://help.github.com/articles/interactive-rebase) - feature to tidy up your commits before making them public. - -5. Locally merge (or rebase) the upstream development branch into your topic branch: - - ```bash - git pull [--rebase] upstream master - ``` - -6. Push your topic branch up to your fork: - - ```bash - git push origin - ``` - -7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) - with a clear title and description against the `master` branch. - -**IMPORTANT**: By submitting a patch, you agree to allow the project owners to -license your work under the terms of the [MPL-2.0 License](LICENSE.md) (if it -includes code changes) and under the terms of the -[Creative Commons Attribution 3.0 Unported License](docs/LICENSE.md) -(if it includes documentation changes). - - -## Code guidelines - -### HTML - -[Adhere to the Code Guide.](http://codeguide.co/#html) - -- Use tags and elements appropriate for an HTML5 doctype (e.g., self-closing tags). - -### JS - -- No semicolons (in client-side JS) -- 2 spaces (no tabs) -- strict mode -- "Attractive" -- Don't use jQuery (no "$" allowed) - -### Checking code - -Run `npm run dev` before committing to ensure your changes follow our coding standards. - - -## License - -By contributing your code, you agree to license your contribution under the [MPL-2.0 License](LICENSE.md). diff --git a/_includes/contribute_community.md b/_includes/contribute_community.md deleted file mode 100644 index 20fca8b043..0000000000 --- a/_includes/contribute_community.md +++ /dev/null @@ -1,34 +0,0 @@ -### Sections in this article -{:.no_toc} -* TOC -{:toc} - -## Community Rewards - -Blockstack Community Reward Program -We’ve set up a community portal with all kinds of tasks you can complete to earn Stacks Tokens by contributing to and supporting the community - these stay in your account and convert into tokens when the network goes live. Many are really easy and they’re all fun and helpful for the community, more information here - https://contribute.blockstack.org/. - - -## Blockstack on Social Media - -Musce libero nunc, dignissim quis turpis quis, semper vehicula dolor. Suspendisse tincidunt consequat quam, ac posuere leo dapibus id. Cras fringilla convallis elit, at eleifend mi interam. - -- Slack -- Twitter -- Telegram -- Reddit - - -## Meeting fact to face - -- Events Calendar -- Meetup -- Blockstack Community Rewards Program -- Request Sponsorship for your Event - - -## Become an evangelist - -Musce libero nunc, dignissim quis turpis quis, semper vehicula dolor. Suspendisse tincidunt consequat quam, ac posuere leo dapibus id. Cras fringilla convallis elit, at eleifend mi interam. - -Nulla non sollicitudin. Morbi sit amet laoreet ipsum, vel pretium mi. Morbi varius, tellus in accumsan blandit, elit ligula eleifend velit, luctus mattis ante nulla condimentum nulla. Etiam vestibulum risus vel arcu elementum eleifend. Cras at dolor eget urna varius faucibus tempus in elit. diff --git a/_includes/contribute_issues.md b/_includes/contribute_issues.md deleted file mode 100644 index 0b151294c6..0000000000 --- a/_includes/contribute_issues.md +++ /dev/null @@ -1,71 +0,0 @@ -### Sections in this article -{:.no_toc} -* TOC -{:toc} - -## Using the issue tracker - -The [issue tracker](https://github.com/blockstack/blockstack-browser/issues) is the preferred channel for [bug reports](#bug-reports), [features requests](#feature-requests) and [submitting pull requests](#pull-requests), but please respect the following -restrictions: - -* Please **do not** use the issue tracker for personal support requests. Please use the [Forum](https://forum.blockstack.org) or [Slack](https://chat.blockstack.org) as they are better places to get help. - -* Please **do not** derail or troll issues. Keep the discussion on topic and respect the opinions of others. - -* Please **do not** post comments consisting solely of "+1" or ":thumbsup:". Use [GitHub's "reactions" feature](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments) instead. We reserve the right to delete comments which violate this rule. - - -## Issues and labels - -Our bug tracker utilizes several labels to help organize and identify issues. Here's what they represent and how we use them: - -- `bug` - Issues where code is demonstrably a problem -- `chore` - Issues that are not -- `design` - Issues related to design -- `documentation` - Issue that is specifically for addition, creation or editing of documentation -- `enhancement` - Issues that enhance the product -- `feature` - Issues that identify a new feature -- `release-overview` - Overview of release - -For a complete look at our labels, see the [project labels page](https://github.com/blockstack/blockstack-browser/labels). - - -## Bug reports - -A bug is a _demonstrable problem_ that is caused by the code in the repository. Good bug reports are extremely helpful, so thanks! - -Guidelines for bug reports: - -0. **Validate and lint your code** — [validate your HTML](https://html5.validator.nu) and [lint your HTML](https://github.com/twbs/bootlint) to ensure your problem isn't caused by a simple error in your own code. - -1. **Use the GitHub issue search** — check if the issue has already been reported. - -2. **Check if the issue has been fixed** — try to reproduce it using the latest `master` or development branch in the repository. - -3. **Isolate the problem** — ideally create a [reduced test case](https://css-tricks.com/reduced-test-cases/) and a live example. [This JS Bin](https://jsbin.com/lolome/edit?html,output) is a helpful template. - - -A good bug report shouldn't leave others needing to chase you up for more information. Please try to be as detailed as possible in your report. What is your environment? What steps will reproduce the issue? What browser(s) and OS experience the problem? Do other browsers show the bug differently? What would you expect to be the outcome? All these details will help people to fix any potential bugs. - -Example: - -> Short and descriptive example bug report title -> -> A summary of the issue and the browser/OS environment in which it occurs. If -> suitable, include the steps required to reproduce the bug. -> -> 1. This is the first step -> 2. This is the second step -> 3. Further steps, etc. -> -> `` - a link to the reduced test case -> -> Any other information you want to share that is relevant to the issue being -> reported. This might include the lines of code that you have identified as -> causing the bug, and potential solutions (and your opinions on their -> merits). - - -## Feature requests - -Feature requests are welcome. But take a moment to find out whether your idea fits with the scope and aims of the project. It's up to *you* to make a strong case to convince the project's developers of the merits of this feature. Please provide as much detail and context as possible. diff --git a/_includes/contribute_ovr.md b/_includes/contribute_ovr.md deleted file mode 100644 index f49a2c786d..0000000000 --- a/_includes/contribute_ovr.md +++ /dev/null @@ -1,27 +0,0 @@ -### Sections in this article -{:.no_toc} -* TOC -{:toc} - -Looking to contribute something to Blockstack? **Here's how you can help.** - -Please take a moment to review this document in order to make the contribution -process easy and effective for everyone in the community. - -Following these guidelines helps to communicate that you respect the time of -everyone involved in the Blockstack open source community. In return, the -community will address your issue or assess patches and features as quickly as -possible. - -## Who can contribute? - -Musce libero nunc, dignissim quis turpis quis, semper vehicula dolor. Suspendisse tincidunt consequat quam, ac posuere leo dapibus id. Cras fringilla convallis elit, at eleifend mi interam. - -Nulla non sollicitudin. Morbi sit amet laoreet ipsum, vel pretium mi. Morbi varius, tellus in accumsan blandit, elit ligula eleifend velit, luctus mattis ante nulla condimentum nulla. Etiam vestibulum risus vel arcu elementum eleifend. Cras at dolor eget urna varius faucibus tempus in elit. - -## What can you contribute? -Nunc porta malesuada porta. Etiam tristique vestibulum dolor at ultricies. Proin hendrerit sapien sed erat fermentum, at commodo velit consectetur. - -{% include image.html img="image1.png" style="wide" lightbox="true" alt="Alt for image" caption="Image in lightbox" %} - -Etiam vestibulum risus vel arcu elementum eleifend. Cras at dolor eget urna varius faucibus tempus in elit. Cras a dui imperdiet, tempus metus quis, pharetra turpis. Phasellus at massa sit amet ante semper fermentum sed eget lectus. Quisque id dictum magna, et dapibus turpis. diff --git a/_includes/discourse.html b/_includes/discourse.html deleted file mode 100644 index 83de1571c9..0000000000 --- a/_includes/discourse.html +++ /dev/null @@ -1,15 +0,0 @@ -{% if jekyll.environment == "production" %} -
- - -{% endif %} diff --git a/_includes/disqus_comments.html b/_includes/disqus_comments.html deleted file mode 100644 index f6e9537e20..0000000000 --- a/_includes/disqus_comments.html +++ /dev/null @@ -1,20 +0,0 @@ -{% if page.comments != false and jekyll.environment == "production" %} - -
- - -{% endif %} diff --git a/_includes/editme.html b/_includes/editme.html deleted file mode 100644 index b7f4e9093f..0000000000 --- a/_includes/editme.html +++ /dev/null @@ -1 +0,0 @@ - Edit this page on Github diff --git a/_includes/footer.html b/_includes/footer.html deleted file mode 100644 index 86d5bae1d3..0000000000 --- a/_includes/footer.html +++ /dev/null @@ -1,31 +0,0 @@ -
-
- -
-
    - {% for link in site.data.navigation_footer %} - {% if link.url contains 'http' %} - {% assign domain = '' %} - {% else %} - {% assign domain = relative_url %} - {% endif %} - {% if link.url == page.url %} - {% assign current = ' class="uk-active"' %} - {% else %} - {% assign current = null %} - {% endif %} - {% if link.title %} - {{ link.title }} - {% endif %} - {% endfor %} -
-
-
-
- {% include social-networks.html %} -
-
- - -
-
diff --git a/_includes/glossary.md b/_includes/glossary.md deleted file mode 100644 index c0acb1ec8d..0000000000 --- a/_includes/glossary.md +++ /dev/null @@ -1,148 +0,0 @@ - - -Commonly used terms and jargon in Blockstack - -## Account - -A field in a profile that links the name to an existing service, like Twitter or OpenBazaar. They are listed under the `accounts` listing in a profile. - -Some accounts serve as social proofs, but they can contain any data the user wants. - -## Atlas - -A peer-to-peer network maintained by Blockstack Core nodes that stores each -name's zone files and immutable data. See [this document](atlas_network.md) for -details. - -## Blockstack ID - -(Also called a "name"). - -A human-readable name in Blockstack. It is comprised only of upper and lower-case ASCII characters, numbers, as well as `-`, `_`, and `.`. It must end with a `.`, followed by a namespace ID. It has at least 3 characters, and at most 37 (including the `.` and the namespace ID). - -Anyone can register a Blockstack ID, such as through the [Blockstack Browser](https://github.com/blockstack/blockstack-browser) - -## Blockstack Core - -A server that reads a blockchain with [virtualchain](https://github.com/blockstack/blockstack-virtualchain), filters out transactions that represent name operations, and builds up a database of (name, public key, state value) triples. - -## Blockstack Naming Service (BNS) - -This is the naming protocol that Blockstack Core implements. See [this -document](blockstack_naming_service.md) for details. - -## Consensus Hash - -A cryptographic hash that represents a proof-of-computation by a Blockstack Core node. Two Blockstack Core nodes have seen and processed the same name operations up to block `n` if and only if they each calculate the same consensus hash at height `n`. - -A Blockstack Core node only accepts a name operation if it has a previously-calculated but recent consensus hash. Blockstack clients obtain a consensus hash from a Blockstack Core node in order to construct a name operation. - -## Gaia - -This is Blockstack's storage system. Gaia hosts all of your app-specific data. - -## Gaia Hub - -This is a publicly-routable server that serves as an entry point for Gaia data. -Anyone can stand up and run a Gaia hub by following [these -instructions](https://github.com/blockstack/gaia). -Blockstack provides a [default Gaia hub](https://gaia.blockstack.org). - -## Immutable Data - -This is the general term for chunks of data whose hash is cryptographically -bound to a blockchain transaction. This includes all data stored in the Atlas -network (such as your Blockstack ID's zone file), -as well as any data whose hash is stored in the Atlas network. - -## Mutable Data - -This is the general term for data that is (1) signed by your Blockstack ID, and -(2) can be looked up using your Blockstack ID. This includes all your Gaia -data, as well as your profile. - -## Name - -See Blockstack ID. - -## Name Database - -The set of (name, public key, name state) triples that the Blockstack Core node generates by reading the blockchain. The name state is usually the hash of a DNS zone file stored in Atlas. - -## Name Operation - -A specially-crafted transaction in the underlying blockchain that, when processed, will change each Blockstack Core's name database. Examples include `NAME_PREORDER` (preorders a name), `NAME_REGISTRATION` (registers a name), `NAME_UPDATE` (changes a name's zonefile hash), `NAME_TRANSFER` (changes a name's public key), and `NAME_REVOKE` (locks everyone out of a name until it expires). - -Name operations are encoded on Bitcoin as `OP_RETURN` outputs that start with `id`, followed by a 1-byte character that identifies the particular operation. - -See the [wire format](wire-format.md) document for details. - -## Namespace - -Analogous to a DNS TLD, it represents a grouping of names. All names under the same namespace have the same pricing and lifetime rules. - -Anyone can create a namespace, but doing so is expensive by design. See the -[namespace creation](namespace_creation.md) tutorial for details. - -## Preorder - -The first of two steps to acquire a name. This operation writes the hash of both the name and the address that will own it. - -## Profile - -A signed JSON web token that describes a [Person](https://schema.org/Person), which describes the name's owner. You can put anything you want into your profile. - -Additionally, profiles hold lists of social verifications and pointers to your Gaia data. - -## Register - -(1) The act of acquiring a name in Blockstack. - -(2) The second of two steps to create a new name entry in the name database. Reveals the name as plaintext to the world. Must match a recent preorder to be accepted. - -## Registrar - -An online service that lets you sign up for and manage the profiles of Blockstack IDs. - -## Resolver - -An online service that displays zonefile and profile data for a Blockstack ID. [The Blockstack Explorer](https://explorer.blockstack.org) is a resolver. - -## Social proof - -A post in an account on an existing Web service like Twitter, Facebook, or GitHub that points back to a Blockstack ID. Used to provide some evidence that the person who owns the Blockstack ID is also the person who owns the Web service account. - -Social proofs are listed in your profile. - -## Storage Provider - -This is any service that can serve your zone file, profile, or data. In all cases, the data is signed by one of your wallet's keys, so you can use any provider without having to worry about it trying to change the data. - -Storage providers are accessed through a Gaia hub. Gaia hubs ship with drivers -that allow them to treat storage providers as dumb hard drives, which store -signed encrypted data on the hub's behalf. - -Not all storage providers support writes--some of them are read-only. - -Supported storage providers today include: -* Amazon S3 -* Dropbox -* Your harddrive -* Any HTTP/HTTPS/FTP server (read-only) -* Any public-use Gaia hub -* IPFS - -Support is being added for: -* Google Drive -* Microsoft OneDrive -* Box.com -* BitTorrent - -If you have a preferred storage provider, and you're a developer, please consider sending us a pull request to add support for it! - -## Zone file - -A specially-formatted file that stores routing information for a Blockstack ID. -Blockstack clients use your zone file to find out where your preferred Gaia -hub(s) are. Ever Blockstack Core node stores a copy of every zone file for -every Blockstack ID by participating in the Atlas network. diff --git a/_includes/google-analytics.html b/_includes/google-analytics.html deleted file mode 100644 index eb2a692a56..0000000000 --- a/_includes/google-analytics.html +++ /dev/null @@ -1,11 +0,0 @@ - - diff --git a/_includes/head.html b/_includes/head.html deleted file mode 100644 index fbba336793..0000000000 --- a/_includes/head.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - {% seo %} - - - - - - - {% if jekyll.environment == 'production' and site.google_analytics %} - {% include google-analytics.html %} - {% endif %} - diff --git a/_includes/header.html b/_includes/header.html deleted file mode 100644 index 8c933f29d5..0000000000 --- a/_includes/header.html +++ /dev/null @@ -1,7 +0,0 @@ -
-
- -
-
diff --git a/_includes/hero.html b/_includes/hero.html deleted file mode 100644 index 9cf782ad01..0000000000 --- a/_includes/hero.html +++ /dev/null @@ -1,37 +0,0 @@ -
-
- {% if page.hero.image %} - -

Hero

- - {% endif %} - - {% if page.hero.subtitle %}

{{ page.hero.subtitle }}

{% endif %} - - {% if page.hero.search %} - - {% endif %} - -
-
diff --git a/_includes/image.html b/_includes/image.html deleted file mode 100644 index fcc6958c73..0000000000 --- a/_includes/image.html +++ /dev/null @@ -1,19 +0,0 @@ -{% if include.lightbox == 'true' %} -
- - - - {{ include.alt }} -
- -
-
- {% if include.caption %}
{{ include.caption }}
{% endif %} -
-{% else %} -
- - {{ include.alt }} - {% if include.caption %}
{{ include.caption }}
{% endif %} -
-{% endif %} diff --git a/_includes/map.html b/_includes/map.html deleted file mode 100644 index 33db26b742..0000000000 --- a/_includes/map.html +++ /dev/null @@ -1,38 +0,0 @@ -
- - diff --git a/_includes/navbar.html b/_includes/navbar.html deleted file mode 100644 index e95006baab..0000000000 --- a/_includes/navbar.html +++ /dev/null @@ -1,53 +0,0 @@ -
-
- -
-
    - {% for link in site.data.navigation_header %} - {% if link.url contains 'http' %} - {% assign domain = '' %} - {% else %} - {% assign domain = relative_url %} - {% endif %} - {% if link.url == page.url %} - {% assign current = ' class="uk-active"' %} - {% else %} - {% assign current = null %} - {% endif %} - {% if link.title %} - {{ link.title }} - {% endif %} - {% endfor %} -
- - {% unless page.hero%} -
- -
- -
    -
    -
    - - {% endunless %} - - - -
    -
    -
    diff --git a/_includes/offcanvas.html b/_includes/offcanvas.html deleted file mode 100644 index d3be036dd6..0000000000 --- a/_includes/offcanvas.html +++ /dev/null @@ -1,26 +0,0 @@ -
    -
    - - - - - - - -
    -
    diff --git a/_includes/page__comments.html b/_includes/page__comments.html deleted file mode 100644 index 6c5df0845b..0000000000 --- a/_includes/page__comments.html +++ /dev/null @@ -1,9 +0,0 @@ -
    - - - - - - - -
    diff --git a/_includes/paginate-blog.html b/_includes/paginate-blog.html deleted file mode 100644 index be66ead840..0000000000 --- a/_includes/paginate-blog.html +++ /dev/null @@ -1,8 +0,0 @@ - -
      - {% if paginator.previous_page %} -
    • ← Previous
    • - {% endif %} {% if paginator.next_page %} -
    • Next →
    • - {% endif %} -
    diff --git a/_includes/paginate-doc.html b/_includes/paginate-doc.html deleted file mode 100644 index 24a9c5731a..0000000000 --- a/_includes/paginate-doc.html +++ /dev/null @@ -1,24 +0,0 @@ -{% assign docs = site.data.navigation_docs | map: 'docs' | join: ',' | split: ',' %} - -{% for document in docs %} - {% assign document_url = document | prepend:"/docs/" | append:"/" %} - {% if document_url == page.url %} -
      - {% if forloop.first %} - - {% else %} - {% assign previous = forloop.index0 | minus: 1 %} - {% assign previous_page = docs[previous] | prepend:"/docs/" | append:"/" %} -
    • ← Previous
    • - {% endif %} - {% if forloop.last %} - - {% else %} - {% assign next = forloop.index0 | plus: 1 %} - {% assign next_page = docs[next] | prepend:"/docs/" | append:"/" %} -
    • Next →
    • - {% endif %} -
    - {% break %} - {% endif %} -{% endfor %} diff --git a/_includes/paginate-post.html b/_includes/paginate-post.html deleted file mode 100644 index a9ed0094a3..0000000000 --- a/_includes/paginate-post.html +++ /dev/null @@ -1,18 +0,0 @@ -
    -
    -
    - {% if page.previous.url %} -

    {{page.previous.title}}

    -
    {{ page.previous.excerpt }}
    - - {% endif %} -
    -
    - {% if page.next.url %} -

    {{page.next.title}}

    -
    {{ page.next.excerpt }}
    - - {% endif %} -
    -
    -
    diff --git a/_includes/post-meta.html b/_includes/post-meta.html deleted file mode 100644 index 74e959df6d..0000000000 --- a/_includes/post-meta.html +++ /dev/null @@ -1,18 +0,0 @@ -{% if page.author %} - {% assign author = site.authors[page.author] %} -{% else %} - {% assign author = site.author %} -{% endif %} - -{% if author.github %} - -{% endif %} - -{% if author.name %} - -{% endif %} - - diff --git a/_includes/related-auth.html b/_includes/related-auth.html deleted file mode 100644 index 2004a61510..0000000000 --- a/_includes/related-auth.html +++ /dev/null @@ -1,32 +0,0 @@ -
    -

    Related Articles

    - - {% assign maxRelated = 4 %} - {% assign minCommonTags = 1 %} - {% assign maxRelatedCounter = 0 %} - -
      - {% for doc in site.docs %} - - {% assign sameTagCount = 0 %} - {% assign commonTags = '' %} - - {% for tag in doc.tags %} - {% if doc.url != page.url %} - {% if page.tags contains tag %} - {% assign sameTagCount = sameTagCount | plus: 1 %} - {% endif %} - {% endif %} - {% endfor %} - - {% if sameTagCount >= minCommonTags %} -
    • {{ doc.title }}
    • - {% assign maxRelatedCounter = maxRelatedCounter | plus: 1 %} - {% if maxRelatedCounter >= maxRelated %} - {% break %} - {% endif %} - {% endif %} - - {% endfor %} -
    -
    diff --git a/_includes/related-bns.html b/_includes/related-bns.html deleted file mode 100644 index d9565e8e2d..0000000000 --- a/_includes/related-bns.html +++ /dev/null @@ -1,32 +0,0 @@ -
    -

    Related Articles

    - - {% assign maxRelated = 4 %} - {% assign minCommonTags = 1 %} - {% assign maxRelatedCounter = 0 %} - -
      - {% for doc in site.bns %} - - {% assign sameTagCount = 0 %} - {% assign commonTags = '' %} - - {% for tag in doc.tags %} - {% if doc.url != page.url %} - {% if page.tags contains tag %} - {% assign sameTagCount = sameTagCount | plus: 1 %} - {% endif %} - {% endif %} - {% endfor %} - - {% if sameTagCount >= minCommonTags %} -
    • {{ doc.title }}
    • - {% assign maxRelatedCounter = maxRelatedCounter | plus: 1 %} - {% if maxRelatedCounter >= maxRelated %} - {% break %} - {% endif %} - {% endif %} - - {% endfor %} -
    -
    diff --git a/_includes/related-docs.html b/_includes/related-docs.html deleted file mode 100644 index 2004a61510..0000000000 --- a/_includes/related-docs.html +++ /dev/null @@ -1,32 +0,0 @@ -
    -

    Related Articles

    - - {% assign maxRelated = 4 %} - {% assign minCommonTags = 1 %} - {% assign maxRelatedCounter = 0 %} - -
      - {% for doc in site.docs %} - - {% assign sameTagCount = 0 %} - {% assign commonTags = '' %} - - {% for tag in doc.tags %} - {% if doc.url != page.url %} - {% if page.tags contains tag %} - {% assign sameTagCount = sameTagCount | plus: 1 %} - {% endif %} - {% endif %} - {% endfor %} - - {% if sameTagCount >= minCommonTags %} -
    • {{ doc.title }}
    • - {% assign maxRelatedCounter = maxRelatedCounter | plus: 1 %} - {% if maxRelatedCounter >= maxRelated %} - {% break %} - {% endif %} - {% endif %} - - {% endfor %} -
    -
    diff --git a/_includes/related-gaia.html b/_includes/related-gaia.html deleted file mode 100644 index 2004a61510..0000000000 --- a/_includes/related-gaia.html +++ /dev/null @@ -1,32 +0,0 @@ -
    -

    Related Articles

    - - {% assign maxRelated = 4 %} - {% assign minCommonTags = 1 %} - {% assign maxRelatedCounter = 0 %} - -
      - {% for doc in site.docs %} - - {% assign sameTagCount = 0 %} - {% assign commonTags = '' %} - - {% for tag in doc.tags %} - {% if doc.url != page.url %} - {% if page.tags contains tag %} - {% assign sameTagCount = sameTagCount | plus: 1 %} - {% endif %} - {% endif %} - {% endfor %} - - {% if sameTagCount >= minCommonTags %} -
    • {{ doc.title }}
    • - {% assign maxRelatedCounter = maxRelatedCounter | plus: 1 %} - {% if maxRelatedCounter >= maxRelated %} - {% break %} - {% endif %} - {% endif %} - - {% endfor %} -
    -
    diff --git a/_includes/related-learn.html b/_includes/related-learn.html deleted file mode 100644 index d46766ef48..0000000000 --- a/_includes/related-learn.html +++ /dev/null @@ -1,32 +0,0 @@ -
    -

    Related Articles

    - - {% assign maxRelated = 4 %} - {% assign minCommonTags = 1 %} - {% assign maxRelatedCounter = 0 %} - -
      - {% for doc in site.learn %} - - {% assign sameTagCount = 0 %} - {% assign commonTags = '' %} - - {% for tag in doc.tags %} - {% if doc.url != page.url %} - {% if page.tags contains tag %} - {% assign sameTagCount = sameTagCount | plus: 1 %} - {% endif %} - {% endif %} - {% endfor %} - - {% if sameTagCount >= minCommonTags %} -
    • {{ doc.title }}
    • - {% assign maxRelatedCounter = maxRelatedCounter | plus: 1 %} - {% if maxRelatedCounter >= maxRelated %} - {% break %} - {% endif %} - {% endif %} - - {% endfor %} -
    -
    diff --git a/_includes/related-newinternet.html b/_includes/related-newinternet.html deleted file mode 100644 index d46766ef48..0000000000 --- a/_includes/related-newinternet.html +++ /dev/null @@ -1,32 +0,0 @@ -
    -

    Related Articles

    - - {% assign maxRelated = 4 %} - {% assign minCommonTags = 1 %} - {% assign maxRelatedCounter = 0 %} - -
      - {% for doc in site.learn %} - - {% assign sameTagCount = 0 %} - {% assign commonTags = '' %} - - {% for tag in doc.tags %} - {% if doc.url != page.url %} - {% if page.tags contains tag %} - {% assign sameTagCount = sameTagCount | plus: 1 %} - {% endif %} - {% endif %} - {% endfor %} - - {% if sameTagCount >= minCommonTags %} -
    • {{ doc.title }}
    • - {% assign maxRelatedCounter = maxRelatedCounter | plus: 1 %} - {% if maxRelatedCounter >= maxRelated %} - {% break %} - {% endif %} - {% endif %} - - {% endfor %} -
    -
    diff --git a/_includes/related-posts.html b/_includes/related-posts.html deleted file mode 100644 index 9aa5451a07..0000000000 --- a/_includes/related-posts.html +++ /dev/null @@ -1,32 +0,0 @@ -
    -

    Related Articles

    - - {% assign maxRelated = 4 %} - {% assign minCommonTags = 1 %} - {% assign maxRelatedCounter = 0 %} - -
      - {% for post in site.posts %} - - {% assign sameTagCount = 0 %} - {% assign commonTags = '' %} - - {% for category in post.categories %} - {% if post.url != page.url %} - {% if page.categories contains category %} - {% assign sameTagCount = sameTagCount | plus: 1 %} - {% endif %} - {% endif %} - {% endfor %} - - {% if sameTagCount >= minCommonTags %} -
    • {{ post.title }}
    • - {% assign maxRelatedCounter = maxRelatedCounter | plus: 1 %} - {% if maxRelatedCounter >= maxRelated %} - {% break %} - {% endif %} - {% endif %} - - {% endfor %} -
    -
    diff --git a/_includes/search.html b/_includes/search.html deleted file mode 100644 index 11a675d5ef..0000000000 --- a/_includes/search.html +++ /dev/null @@ -1,10 +0,0 @@ - - diff --git a/_includes/share.html b/_includes/share.html deleted file mode 100644 index d945366abd..0000000000 --- a/_includes/share.html +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/_includes/slideshow.html b/_includes/slideshow.html deleted file mode 100644 index f6c9d2b062..0000000000 --- a/_includes/slideshow.html +++ /dev/null @@ -1,14 +0,0 @@ -
    - -
      - {% for image in site.static_files %} - {% if image.path contains include.gallery %} -
    • - {% endif %} - {% endfor %} -
    - - - - -
    diff --git a/_includes/social-networks.html b/_includes/social-networks.html deleted file mode 100644 index 5427246dad..0000000000 --- a/_includes/social-networks.html +++ /dev/null @@ -1,65 +0,0 @@ -{% if site.social.twitter %} -
    - -
    -{% endif %} -{% if site.social.facebook %} -
    - -
    -{% endif %} -{% if site.social.flickr %} -
    - -
    -{% endif %} -{% if site.social.dribbble %} -
    - -
    -{% endif %} -{% if site.social.instagram %} -
    - -
    -{% endif %} -{% if site.social.googleplus %} -
    - -
    -{% endif %} -{% if site.social.linkedin %} -
    - -
    -{% endif %} -{% if site.social.pinterest %} -
    - -
    -{% endif %} -{% if site.social.vimeo %} -
    - -
    -{% endif %} -{% if site.social.youtube %} -
    - -
    -{% endif %} -{% if site.social.github %} -
    - -
    -{% endif %} -{% if site.social.email %} -
    - -
    -{% endif %} -{% if site.social.website %} -
    - -
    -{% endif %} diff --git a/_includes/tests.html b/_includes/tests.html deleted file mode 100644 index 230b94d684..0000000000 --- a/_includes/tests.html +++ /dev/null @@ -1,19 +0,0 @@ - - -| **Input** | **Result** | -|--------------|------------| -|`page.title` | {{ page.title }} | -|`page.url` | {{ page.url }} | -|`page.date` | {{ page.date }} | -|`page.id` | {{ page.id }} | -|`page.categories` | {{ page.categories }} | -|`page.tags` | {{ page.tags }} | -|`page.path` | {{ page.path }} | -|`page.next` | {{ page.next }} | -|`page.previous` | {{ page.previous }} | -|`page.url | split` | {{ page.url | split: '/'}} -|`page.id | split: '/' | last ` | {{ page.id | split: '/' | last }} | -|`page.id | split: '/' | first ` | {{ page.id | split: '/' | first }} | -|`page.id | split: '/' | 2 ` | {{ page.id | split: '/' | first }} | -|`page.path | relative_url` | {{ page.path | relative_url }} | -|`page.path | absolute_url` | {{ page.path | absolute_url }} | diff --git a/_layouts/auth.html b/_layouts/auth.html deleted file mode 100644 index f104f15436..0000000000 --- a/_layouts/auth.html +++ /dev/null @@ -1,75 +0,0 @@ ---- -layout: default ---- - -
    -
    -
    - - - -
    - -
    - -

    {{ page.title | escape }}

    - - {% if page.subtitle %}

    {{ page.subtitle }}

    {% endif %} - - - - -
    - {{ content }} - {% include share.html %} -
    - -
    - - {% include paginate-doc.html %} - - {% include related-auth.html %} - - {% include page__comments.html %} -
    - - - -
    - -
    -
    diff --git a/_layouts/bns.html b/_layouts/bns.html deleted file mode 100644 index 4a6d818cde..0000000000 --- a/_layouts/bns.html +++ /dev/null @@ -1,70 +0,0 @@ ---- -layout: default ---- - -
    -
    -
    - - - -
    - -
    - -

    {{ page.title | escape }}

    - - {% if page.subtitle %}

    {{ page.subtitle }}

    {% endif %} - - - -
    - {{ content }} - {% include share.html %} -
    - -
    - - {% include paginate-doc.html %} - - {% include related-bns.html %} - - {% if site.disqus.shortname %} {% include disqus_comments.html %} {% endif %} -
    - - - -
    - -
    -
    diff --git a/_layouts/changelog.html b/_layouts/changelog.html deleted file mode 100644 index 2288936c16..0000000000 --- a/_layouts/changelog.html +++ /dev/null @@ -1,37 +0,0 @@ ---- -layout: default ---- - -
    -
    - -
    - -

    {{ page.title | escape }}

    - -
    - {{ content }} -
    - -
    - {% for version in site.data.changelog %} -
    -
    -
    {{ version.date }}
    -
    -
    -

    {{ version.title }} {{ version.label }}

    -
      - {% for item in version.list %} -
    • {{ item }}
    • - {% endfor %} -
    -
    -
    - {% endfor %} -
    - -
    - -
    -
    diff --git a/_layouts/contact.html b/_layouts/contact.html deleted file mode 100644 index bafa0ed88f..0000000000 --- a/_layouts/contact.html +++ /dev/null @@ -1,56 +0,0 @@ ---- -layout: default ---- - -
    -
    - -
    - -

    {{ page.title | escape }}

    - -
    - {{ content }} -
    - -
    - - {% if page.formspree.email %} -

    Contact Form

    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - {% if page.formspree.redirect %} - - {% endif %} - - -
    -
    - {% endif %} - -
    -
    diff --git a/_layouts/default.html b/_layouts/default.html deleted file mode 100644 index 54992b949a..0000000000 --- a/_layouts/default.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - {% include head.html %} - - - - {% include header.html %} - - {% if page.hero %} - {% include hero.html %} - {% endif %} - - {{ content }} - - {% include offcanvas.html %} - - {% if page.layout != 'doc' %} - {% include footer.html %} - {% endif %} - - {% include search.html %} - - - - - diff --git a/_layouts/doc.html b/_layouts/doc.html deleted file mode 100644 index 64428d9ca2..0000000000 --- a/_layouts/doc.html +++ /dev/null @@ -1,70 +0,0 @@ ---- -layout: default ---- - -
    -
    -
    - - - -
    - -
    - -

    {{ page.title | escape }}

    - - {% if page.subtitle %}

    {{ page.subtitle }}

    {% endif %} - - - -
    - {{ content }} - {% include share.html %} -
    - -
    - - {% include paginate-doc.html %} - - {% include related-docs.html %} - - {% if site.disqus.shortname %} {% include disqus_comments.html %} {% endif %} -
    - - - -
    - -
    -
    diff --git a/_layouts/gaia.html b/_layouts/gaia.html deleted file mode 100644 index d401f05680..0000000000 --- a/_layouts/gaia.html +++ /dev/null @@ -1,70 +0,0 @@ ---- -layout: default ---- - -
    -
    -
    - - - -
    - -
    - -

    {{ page.title | escape }}

    - - {% if page.subtitle %}

    {{ page.subtitle }}

    {% endif %} - - - -
    - {{ content }} - {% include share.html %} -
    - -
    - - {% include paginate-doc.html %} - - {% include related-gaia.html %} - - {% if site.disqus.shortname %} {% include disqus_comments.html %} {% endif %} -
    - - - -
    - -
    -
    diff --git a/_layouts/home.html b/_layouts/home.html deleted file mode 100644 index 5e722e3bdd..0000000000 --- a/_layouts/home.html +++ /dev/null @@ -1,102 +0,0 @@ ---- -layout: default ---- - -{% if page.categories.columns %} - {% assign columns = page.categories.columns %} -{% else %} - {% assign columns = 3 %} -{% endif %} - -{% if page.categories %} -
    -
    - - {% if page.categories.title %} -

    {{ page.categories.title }}

    - {% endif %} - - {% if page.categories.subtitle %}

    {{ page.categories.subtitle | escape }}

    {% endif %} - -
    - {% for item in site.data.navigation_home %} - {% if item.title %} - {% assign doc_url = item.doc | prepend:"/" | append:".html" %} - {% assign doc = site.docs | where:"url", doc_url | first %} -
    -
    - - {% if item.icon %} - - {% endif %} -

    {{ item.title }}

    - {% if item.desc %} -

    {{ item.desc }}

    - {% endif %} -
    -
    - {% endif %} - {% endfor %} -
    - -
    -
    -{% endif %} - -{% comment %} ONLY include section if there are featured pages. {% endcomment %} -{% assign featured_exists = false %} -{% for doc in site.docs %} - {% if doc.tags contains page.featured.tag and featured_exists != false %} - {% assign featured_exists = false %} - {% endif %} -{% endfor %} -{% if page.featured.tag and featured_exists == true %} -
    -
    - {% if page.featured.title %} -

    {{ page.featured.title }}

    - {% endif %} - -
    -
    -{% endif %} - -{% if page.section %} -
    -
    - - {% if page.section.title %} -

    {{ page.section.title }}

    - {% endif %} - - {% if page.section.subtitle %}

    {{ page.section.subtitle | escape }}

    {% endif %} - -
    {{ content }}
    -
    -
    -{% endif %} - -{% if page.cta.button_url contains 'http' %} - {% assign domain = '' %} -{% else %} - {% assign domain = relative_url %} -{% endif %} - - -{% if page.cta %} -
    -
    -
    -

    {{ page.cta.title }}

    - {% if page.cta.subtitle %}

    {{ page.cta.subtitle | escape }}

    {% endif %} - {{ page.cta.button_text }} -
    -
    -
    -{% endif %} diff --git a/_layouts/learn.html b/_layouts/learn.html deleted file mode 100644 index 601c9bba7a..0000000000 --- a/_layouts/learn.html +++ /dev/null @@ -1,70 +0,0 @@ ---- -layout: default ---- - -
    -
    -
    - - - -
    - -
    - -

    {{ page.title | escape }}

    - - {% if page.subtitle %}

    {{ page.subtitle }}

    {% endif %} - - - -
    - {{ content }} - {% include share.html %} -
    - -
    - - {% include paginate-doc.html %} - - {% include related-learn.html %} - - {% if site.disqus.shortname %} {% include disqus_comments.html %} {% endif %} -
    - - - -
    - -
    -
    diff --git a/_layouts/newinternet.html b/_layouts/newinternet.html deleted file mode 100644 index 3847871c9b..0000000000 --- a/_layouts/newinternet.html +++ /dev/null @@ -1,70 +0,0 @@ ---- -layout: default ---- - -
    -
    -
    - - - -
    - -
    - -

    {{ page.title | escape }}

    - - {% if page.subtitle %}

    {{ page.subtitle }}

    {% endif %} - - - -
    - {{ content }} - {% include share.html %} -
    - -
    - - {% include paginate-doc.html %} - - {% include related-newinternet.html %} - - {% if site.disqus.shortname %} {% include disqus_comments.html %} {% endif %} -
    - - - -
    - -
    -
    diff --git a/_layouts/page.html b/_layouts/page.html deleted file mode 100644 index 32771b2882..0000000000 --- a/_layouts/page.html +++ /dev/null @@ -1,19 +0,0 @@ ---- -layout: default ---- - -
    -
    - -
    - -

    {{ page.title | escape }}

    - -
    - {{ content }} -
    - -
    - -
    -
    diff --git a/_layouts/post.html b/_layouts/post.html deleted file mode 100644 index 7c0624d0db..0000000000 --- a/_layouts/post.html +++ /dev/null @@ -1,36 +0,0 @@ ---- -layout: default ---- - -
    -
    -
    - -

    {{ page.title | escape }}

    - - - -
    - {{ content }} - {% include share.html %} -
    - -
    - - {% include paginate-post.html %} - - {% if site.disqus.shortname %} {% include disqus_comments.html %} {% endif %} -
    - - - -
    -
    diff --git a/_posts/2017-05-25-post63.md b/_posts/2017-05-25-post63.md deleted file mode 100644 index 85eda15dc1..0000000000 --- a/_posts/2017-05-25-post63.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -layout: post -title: Example Post -author: John Black ---- - -## Site tags - - - {% if site.tags contains page.featured.tag %} - **There is at least one** - {% endif %} - - -{{ site.post_assets | absolute_url }} - -{{ page.url }} - -Musce libero nunc, dignissim quis turpis quis, semper vehicula dolor. Suspendisse tincidunt consequat quam, ac posuere leo dapibus id. Cras fringilla convallis elit, at eleifend mi interam. - -Nulla non sollicitudin. Morbi sit amet laoreet ipsum, vel pretium mi. Morbi varius, tellus in accumsan blandit, elit ligula eleifend velit, luctus mattis ante nulla condimentum nulla. Etiam vestibulum risus vel arcu elementum eleifend. Cras at dolor eget urna varius faucibus tempus in elit. - -## Image Lightbox Example -Nunc porta malesuada porta. Etiam tristique vestibulum dolor at ultricies. Proin hendrerit sapien sed erat fermentum, at commodo velit consectetur. - -{% include image.html img="image1.png" style="wide" lightbox="true" alt="Alt for image" caption="Image in lightbox" %} - -Etiam vestibulum risus vel arcu elementum eleifend. Cras at dolor eget urna varius faucibus tempus in elit. Cras a dui imperdiet, tempus metus quis, pharetra turpis. Phasellus at massa sit amet ante semper fermentum sed eget lectus. Quisque id dictum magna, et dapibus turpis. - -## Example Of Code Block -In accumsan lacus ac neque maximus dictum. Phasellus eleifend leo id mattis bibendum. Curabitur et purus turpis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; - -```html - - - - - - - - -``` - -## Text and Quote -Cras at dolor eget urna varius faucibus tempus in elit. Cras a dui imperdiet, tempus metus quis, pharetra turpis. Phasellus at massa sit amet ante semper fermentum sed eget lectus. Quisque id dictum magna turpis. - -> Etiam vestibulum risus vel arcu elementum eleifend. Cras at dolor eget urna varius faucibus tempus in elit. Cras a dui imperdiet - -In accumsan lacus ac neque maximus dictum. Phasellus eleifend leo id mattis bibendum. Curabitur et purus turpis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; - -Etiam in fermentum mi. Sed et tempor felis, eu aliquet nisi. Nam eget ullamcorper arcu. Nunc porttitor nisl a dolor blandit, eget consequat sem maximus. Phasellus lacinia quam porta orci malesuada, vel tincidunt. diff --git a/_sass/syntax-highlighting/github.scss b/_sass/syntax-highlighting/github.scss deleted file mode 100644 index c85e1ed14a..0000000000 --- a/_sass/syntax-highlighting/github.scss +++ /dev/null @@ -1,289 +0,0 @@ -/** - * Syntax highlighting styles - */ - - - -.highlight, .highlighter-rouge { - background-color: #F7F8FA; - color: #5A6575; - border: none; -} - -.highlight .lineno { - color: #B1B8C4 -} - -.highlight .c { - color: #B1B8C4 -} - -.highlight .err { - color: #5A6575 -} - -.highlight .g { - color: #5A6575 -} - -.highlight .k { - color: #25BEA1 -} - -.highlight .l { - color: #5A6575 -} - -.highlight .n { - color: #5A6575 -} - -.highlight .o { - color: #25BEA1 -} - -.highlight .x { - color: #FBBC09 -} - -.highlight .p { - color: #25BEA1 -} - -.highlight .cm { - color: #B1B8C4 -} - -.highlight .cp { - color: #25BEA1 -} - -.highlight .c1 { - color: #B1B8C4 -} - -.highlight .cs { - color: #25BEA1 -} - -.highlight .gd { - color: #E56134 -} - -.highlight .ge { - color: #5A6575; - font-style: italic -} - -.highlight .gr { - color: #9961C9 -} - -.highlight .gh { - color: #FBBC09 -} - -.highlight .gi { - color: #25BEA1 -} - -.highlight .go { - color: #5A6575 -} - -.highlight .gp { - color: #FBBC09 -} - -.highlight .gs { - color: #5A6575; - font-weight: bold -} - -.highlight .gu { - color: #FBBC09 -} - -.highlight .gt { - color: #5A6575 -} - -.highlight .kc { - color: #FBBC09 -} - -.highlight .kd { - color: #11A0F3 -} - -.highlight .kn { - color: #25BEA1 -} - -.highlight .kp { - color: #25BEA1 -} - -.highlight .kr { - color: #11A0F3 -} - -.highlight .kt { - color: #9961C9 -} - -.highlight .ld { - color: #5A6575 -} - -.highlight .m { - color: #E56134 -} - -.highlight .s { - color: #E56134 -} - -.highlight .na { - color: #5A6575 -} - -.highlight .nb { - color: #5A6575 -} - -.highlight .nc { - color: #11A0F3 -} - -.highlight .no { - color: #5A6575 -} - -.highlight .nd { - color: #11A0F3 -} - -.highlight .ni { - color: #FBBC09 -} - -.highlight .ne { - color: #FBBC09 -} - -.highlight .nf { - color: #11A0F3 -} - -.highlight .nl { - color: #5A6575 -} - -.highlight .nn { - color: #5A6575 -} - -.highlight .nx { - color: #5A6575 -} - -.highlight .py { - color: #5A6575 -} - -.highlight .nt { - color: #11A0F3 -} - -.highlight .nv { - color: #5A6575 -} - -.highlight .ow { - color: #25BEA1 -} - -.highlight .w { - color: #5A6575 -} - -.highlight .mf { - color: #E56134 -} - -.highlight .mh { - color: #E56134 -} - -.highlight .mi { - color: #E56134 -} - -.highlight .mo { - color: #E56134 -} - -.highlight .sb { - color: #B1B8C4 -} - -.highlight .sc { - color: #E56134 -} - -.highlight .sd { - color: #5A6575 -} - -.highlight .s2 { - color: #E56134 -} - -.highlight .se { - color: #FBBC09 -} - -.highlight .sh { - color: #5A6575 -} - -.highlight .si { - color: #E56134 -} - -.highlight .sx { - color: #E56134 -} - -.highlight .sr { - color: #9961C9 -} - -.highlight .s1 { - color: #E56134 -} - -.highlight .ss { - color: #E56134 -} - -.highlight .bp { - color: #11A0F3 -} - -.highlight .vc { - color: #11A0F3 -} - -.highlight .vg { - color: #11A0F3 -} - -.highlight .vi { - color: #11A0F3 -} - -.highlight .il { - color: #E56134 -} diff --git a/_sass/system-font-css/CHANGELOG.md b/_sass/system-font-css/CHANGELOG.md deleted file mode 100644 index 7a4eee3d64..0000000000 --- a/_sass/system-font-css/CHANGELOG.md +++ /dev/null @@ -1,13 +0,0 @@ -## 2.0.0 (2017-06-28) - -- Changed from `system` to `system-ui` to match the CSS specification. - -## 1.1.0 (2015-08-02) - -- Added: Android fonts Roboto and Droid Sans after Ubuntu. -- Removed: Windows 3.1-ME font Microsoft Sans Serif. -- Updated: documentation. - -## 1.0.0 (2015-07-30) - -- Added: Initial version diff --git a/_sass/system-font-css/LICENSE.md b/_sass/system-font-css/LICENSE.md deleted file mode 100644 index 565f84e33c..0000000000 --- a/_sass/system-font-css/LICENSE.md +++ /dev/null @@ -1,15 +0,0 @@ -# CC0 1.0 Universal License - -Public Domain Dedication - -The person(s) who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. - -You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. - -In no way are the patent or trademark rights of any person affected by CC0, nor are the rights that other persons may have in the work or in how the work is used, such as publicity or privacy rights. - -Unless expressly stated otherwise, the person(s) who associated a work with this deed makes no warranties about the work, and disclaims liability for all uses of the work, to the fullest extent permitted by applicable law. - -When using or citing the work, you should not imply endorsement by the author or the affirmer. - -This is a [human-readable summary of the Legal Code](https://creativecommons.org/publicdomain/zero/1.0/) ([read the full text](https://creativecommons.org/publicdomain/zero/1.0/legalcode)). diff --git a/_sass/system-font-css/README.md b/_sass/system-font-css/README.md deleted file mode 100644 index 08268fb2b5..0000000000 --- a/_sass/system-font-css/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# System Font CSS - -System Font CSS is set of `@font-face` rules that let you use the native system font of the OS running the browser. - -```css -body { - font-family: system-ui; -} -``` - -[system-font.css](system-font.css) offers eight variations of the `system-ui` font family; **light** (300) **light italic**, **normal** (400), **normal italic**, **medium** (500), **medium italic**, **bold** (700), and **bold italic**. - -```css -blockquote { - font: italic 300 system-ui; -} - -p { - font: 400 system-ui; -} -``` - -## Quick Start - -### Install - -This package can be installed with: - -* [npm](https://www.npmjs.com/package/system-font-css): `npm install --save system-font-css` - -### Load - -When installed with npm, system-font.css will create both a SCSS and LESS partial for easy importing: - -```scss -@import 'system-font'; -``` - -## OSX - -**OSX** has used three system typefaces. Since **El Capitan** it has used **San Fransisco**. In **Yosemite** it used **Helvetica Neue**. From **Mavericks** back to **Kodiak** it used **Lucida Grande**. - -## Windows - -**Windows** has used four system typefaces. Since **Vista** it has used **Segoe UI**. In XP, it used **Tahoma**, which oddly enough does not have an italic variation. From **Windows ME** back to **Windows 3.1** it used **Microsoft Sans Serif**. Finally, from **Windows 2.0** back to **Windows 1.0** it used **Fixedsys**. Neither **Microsoft Sans Serif** or **Fixedsys** are included in this set, with apologies. - -Also, for those of opposed to joy, remember that **Internet Explorer 8** does not support local `@font-face` rules. Therefore, should you need to reference system fonts in that browser then you will need to do so from the `font` declaration. - -```css -body { - font-family: system-ui, "Segoe UI", Tahoma; -} -``` - -## Android - -**Android** has used two system typefaces. Since **Ice Cream Sandwich** it has used **Roboto**. From **Jelly Bean** back to **Cupcake** it used **Droid Sans**, which also lacks an italic variation. Do you suppose OS developers dislike *emphasis*? - -## Ubuntu - -Ubuntu has always used one system typeface, aptly named **Ubuntu**. That part was easy. - -## Native `system-ui` resources - -* [CSS Fonts Module Level 4 Editor’s Draft Specification](https://drafts.csswg.org/css-fonts-4/#system-ui-def) -* [Chrome Platform Status](https://www.chromestatus.com/feature/5640395337760768) -* Proposed for inclusion on [Can I Use](https://github.com/Fyrd/caniuse/issues/2918) -* Previous [discussions in the W3C](https://lists.w3.org/Archives/Public/www-style/2015Jul/0169.html) to standardize a generic `system` family. diff --git a/_sass/system-font-css/_system-font.scss b/_sass/system-font-css/_system-font.scss deleted file mode 100644 index fd07821988..0000000000 --- a/_sass/system-font-css/_system-font.scss +++ /dev/null @@ -1,57 +0,0 @@ -/*! system-font.css v2.0.2 | CC0-1.0 License | github.com/jonathantneal/system-font-css */ - -@font-face { - font-family: system-ui; - font-style: normal; - font-weight: 300; - src: local(".SFNSText-Light"), local(".HelveticaNeueDeskInterface-Light"), local(".LucidaGrandeUI"), local("Segoe UI Light"), local("Ubuntu Light"), local("Roboto-Light"), local("DroidSans"), local("Tahoma"); -} - -@font-face { - font-family: system-ui; - font-style: italic; - font-weight: 300; - src: local(".SFNSText-LightItalic"), local(".HelveticaNeueDeskInterface-Italic"), local(".LucidaGrandeUI"), local("Segoe UI Light Italic"), local("Ubuntu Light Italic"), local("Roboto-LightItalic"), local("DroidSans"), local("Tahoma"); -} - -@font-face { - font-family: system-ui; - font-style: normal; - font-weight: 400; - src: local(".SFNSText-Regular"), local(".HelveticaNeueDeskInterface-Regular"), local(".LucidaGrandeUI"), local("Segoe UI"), local("Ubuntu"), local("Roboto-Regular"), local("DroidSans"), local("Tahoma"); -} - -@font-face { - font-family: system-ui; - font-style: italic; - font-weight: 400; - src: local(".SFNSText-Italic"), local(".HelveticaNeueDeskInterface-Italic"), local(".LucidaGrandeUI"), local("Segoe UI Italic"), local("Ubuntu Italic"), local("Roboto-Italic"), local("DroidSans"), local("Tahoma"); -} - -@font-face { - font-family: system-ui; - font-style: normal; - font-weight: 500; - src: local(".SFNSText-Medium"), local(".HelveticaNeueDeskInterface-MediumP4"), local(".LucidaGrandeUI"), local("Segoe UI Semibold"), local("Ubuntu Medium"), local("Roboto-Medium"), local("DroidSans-Bold"), local("Tahoma Bold"); -} - -@font-face { - font-family: system-ui; - font-style: italic; - font-weight: 500; - src: local(".SFNSText-MediumItalic"), local(".HelveticaNeueDeskInterface-MediumItalicP4"), local(".LucidaGrandeUI"), local("Segoe UI Semibold Italic"), local("Ubuntu Medium Italic"), local("Roboto-MediumItalic"), local("DroidSans-Bold"), local("Tahoma Bold"); -} - -@font-face { - font-family: system-ui; - font-style: normal; - font-weight: 700; - src: local(".SFNSText-Bold"), local(".HelveticaNeueDeskInterface-Bold"), local(".LucidaGrandeUI"), local("Segoe UI Bold"), local("Ubuntu Bold"), local("Roboto-Bold"), local("DroidSans-Bold"), local("Tahoma Bold"); -} - -@font-face { - font-family: system-ui; - font-style: italic; - font-weight: 700; - src: local(".SFNSText-BoldItalic"), local(".HelveticaNeueDeskInterface-BoldItalic"), local(".LucidaGrandeUI"), local("Segoe UI Bold Italic"), local("Ubuntu Bold Italic"), local("Roboto-BoldItalic"), local("DroidSans-Bold"), local("Tahoma Bold"); -} diff --git a/_sass/system-font-css/index.html b/_sass/system-font-css/index.html deleted file mode 100644 index ec5ea86283..0000000000 --- a/_sass/system-font-css/index.html +++ /dev/null @@ -1,189 +0,0 @@ - -System Font CSS - - - - - - -

    - System Font CSS -

    - -

    - System Font CSS is set of @font-face rules that let you use the native system font of the OS running the browser. -

    - -
    -body {
    -    font-family: system;
    -}
    -
    - -

    - system-font.css offers eight variations of the system font family; light (300) light italic, normal (400), normal italic, medium (500), medium italic, bold (700), and bold italic. -

    - -
    -blockquote {
    -    font: italic 300 system;
    -}
    -
    -p {
    -    font: 400 system;
    -}
    -
    - -

    - OSX -

    - -

    - OSX has used three system typefaces. Since El Capitan it has used San Fransisco. In Yosemite it used Helvetica Neue. From Mavericks back to Kodiak it used Lucida Grande. -

    - -

    - Windows -

    - -

    - Windows has used four system typefaces. Since Vista it has used Segoe UI. In XP, it used Tahoma, which oddly enough lacks an italic variation. From Windows ME back to Windows 3.1 it used Microsoft Sans Serif. Finally, from Windows 2.0 back to Windows 1.0 it used Fixedsys. Neither Microsoft Sans Serif or Fixedsys are included in this set, with apologies. -

    - -

    - Also, for those of opposed to joy, remember that Internet Explorer 8 does not support local @font-face rules. Therefore, should you need to reference system fonts in that browser then you will need to do so from the font declaration. -

    - -
    -body {
    -	font-family: system, "Segoe UI", Tahoma;
    -}
    -
    - -

    - Android -

    - -

    - Android has used two system typefaces. Since Ice Cream Sandwich it has used Roboto. From Jelly Bean back to Cupcake it used Droid Sans, which also lacks an italic variation. Do you suppose OS developers dislike emphasis? -

    - -

    - Ubuntu -

    - -

    - Ubuntu has always used one system typeface, apty named Ubuntu. That part was easy. -

    - -

    - Is system going to be a thing? -

    - -

    - Maybe. There are discussions in the W3C to standardize a generic system family. -

    diff --git a/_sass/system-font-css/package.json b/_sass/system-font-css/package.json deleted file mode 100644 index 91916c24e2..0000000000 --- a/_sass/system-font-css/package.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "_from": "system-font-css@^2.0.1", - "_id": "system-font-css@2.0.2", - "_inBundle": false, - "_integrity": "sha512-zK36lpja4NIi4Po99bXReeqeDcM1sW4hTKJt5Mby/IXX6kLSwjkQ4pZThFdgb/jDwfRsBvxxVG+VekP1sTdF0w==", - "_location": "/system-font-css", - "_phantomChildren": {}, - "_requested": { - "type": "range", - "registry": true, - "raw": "system-font-css@^2.0.1", - "name": "system-font-css", - "escapedName": "system-font-css", - "rawSpec": "^2.0.1", - "saveSpec": null, - "fetchSpec": "^2.0.1" - }, - "_requiredBy": [ - "/" - ], - "_resolved": "https://registry.npmjs.org/system-font-css/-/system-font-css-2.0.2.tgz", - "_shasum": "88c4ae30eda94bc60705cba7d66c8c822f3c47db", - "_spec": "system-font-css@^2.0.1", - "_where": "/Users/ivan/Dev/jekyll/jekyll-theme-docs", - "author": { - "name": "Jonathan Neal", - "email": "jonathantneal@hotmail.com", - "url": "http://jonathantneal.com" - }, - "bugs": { - "url": "https://github.com/jonathantneal/system-font-css/issues" - }, - "bundleDependencies": false, - "contributors": [ - { - "name": "Zach Leatherman", - "email": "zachleatherman@gmail.com", - "url": "https://zachleat.com/" - } - ], - "dependencies": { - "cpr": "^2.2.0" - }, - "deprecated": false, - "description": "Use the native system font of the OS running the browser", - "devDependencies": {}, - "engines": { - "node": ">=0.8.0" - }, - "homepage": "https://github.com/jonathantneal/system-font-css", - "keywords": [ - "system", - "fonts", - "faces", - "rules", - "css", - "mac", - "windows", - "ubuntu", - "san", - "fransisco", - "helvetica", - "neue", - "lucida", - "grande", - "segoe", - "ui", - "microsoft", - "sans", - "serif" - ], - "license": "CC0-1.0", - "main": "system-font.css", - "name": "system-font-css", - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/jonathantneal/system-font-css.git" - }, - "scripts": { - "install": "cpr system-font.css _system-font.scss -o && cpr system-font.css system-font.less -o" - }, - "version": "2.0.2" -} diff --git a/_sass/system-font-css/system-font.css b/_sass/system-font-css/system-font.css deleted file mode 100644 index fd07821988..0000000000 --- a/_sass/system-font-css/system-font.css +++ /dev/null @@ -1,57 +0,0 @@ -/*! system-font.css v2.0.2 | CC0-1.0 License | github.com/jonathantneal/system-font-css */ - -@font-face { - font-family: system-ui; - font-style: normal; - font-weight: 300; - src: local(".SFNSText-Light"), local(".HelveticaNeueDeskInterface-Light"), local(".LucidaGrandeUI"), local("Segoe UI Light"), local("Ubuntu Light"), local("Roboto-Light"), local("DroidSans"), local("Tahoma"); -} - -@font-face { - font-family: system-ui; - font-style: italic; - font-weight: 300; - src: local(".SFNSText-LightItalic"), local(".HelveticaNeueDeskInterface-Italic"), local(".LucidaGrandeUI"), local("Segoe UI Light Italic"), local("Ubuntu Light Italic"), local("Roboto-LightItalic"), local("DroidSans"), local("Tahoma"); -} - -@font-face { - font-family: system-ui; - font-style: normal; - font-weight: 400; - src: local(".SFNSText-Regular"), local(".HelveticaNeueDeskInterface-Regular"), local(".LucidaGrandeUI"), local("Segoe UI"), local("Ubuntu"), local("Roboto-Regular"), local("DroidSans"), local("Tahoma"); -} - -@font-face { - font-family: system-ui; - font-style: italic; - font-weight: 400; - src: local(".SFNSText-Italic"), local(".HelveticaNeueDeskInterface-Italic"), local(".LucidaGrandeUI"), local("Segoe UI Italic"), local("Ubuntu Italic"), local("Roboto-Italic"), local("DroidSans"), local("Tahoma"); -} - -@font-face { - font-family: system-ui; - font-style: normal; - font-weight: 500; - src: local(".SFNSText-Medium"), local(".HelveticaNeueDeskInterface-MediumP4"), local(".LucidaGrandeUI"), local("Segoe UI Semibold"), local("Ubuntu Medium"), local("Roboto-Medium"), local("DroidSans-Bold"), local("Tahoma Bold"); -} - -@font-face { - font-family: system-ui; - font-style: italic; - font-weight: 500; - src: local(".SFNSText-MediumItalic"), local(".HelveticaNeueDeskInterface-MediumItalicP4"), local(".LucidaGrandeUI"), local("Segoe UI Semibold Italic"), local("Ubuntu Medium Italic"), local("Roboto-MediumItalic"), local("DroidSans-Bold"), local("Tahoma Bold"); -} - -@font-face { - font-family: system-ui; - font-style: normal; - font-weight: 700; - src: local(".SFNSText-Bold"), local(".HelveticaNeueDeskInterface-Bold"), local(".LucidaGrandeUI"), local("Segoe UI Bold"), local("Ubuntu Bold"), local("Roboto-Bold"), local("DroidSans-Bold"), local("Tahoma Bold"); -} - -@font-face { - font-family: system-ui; - font-style: italic; - font-weight: 700; - src: local(".SFNSText-BoldItalic"), local(".HelveticaNeueDeskInterface-BoldItalic"), local(".LucidaGrandeUI"), local("Segoe UI Bold Italic"), local("Ubuntu Bold Italic"), local("Roboto-BoldItalic"), local("DroidSans-Bold"), local("Tahoma Bold"); -} diff --git a/_sass/system-font-css/system-font.less b/_sass/system-font-css/system-font.less deleted file mode 100644 index fd07821988..0000000000 --- a/_sass/system-font-css/system-font.less +++ /dev/null @@ -1,57 +0,0 @@ -/*! system-font.css v2.0.2 | CC0-1.0 License | github.com/jonathantneal/system-font-css */ - -@font-face { - font-family: system-ui; - font-style: normal; - font-weight: 300; - src: local(".SFNSText-Light"), local(".HelveticaNeueDeskInterface-Light"), local(".LucidaGrandeUI"), local("Segoe UI Light"), local("Ubuntu Light"), local("Roboto-Light"), local("DroidSans"), local("Tahoma"); -} - -@font-face { - font-family: system-ui; - font-style: italic; - font-weight: 300; - src: local(".SFNSText-LightItalic"), local(".HelveticaNeueDeskInterface-Italic"), local(".LucidaGrandeUI"), local("Segoe UI Light Italic"), local("Ubuntu Light Italic"), local("Roboto-LightItalic"), local("DroidSans"), local("Tahoma"); -} - -@font-face { - font-family: system-ui; - font-style: normal; - font-weight: 400; - src: local(".SFNSText-Regular"), local(".HelveticaNeueDeskInterface-Regular"), local(".LucidaGrandeUI"), local("Segoe UI"), local("Ubuntu"), local("Roboto-Regular"), local("DroidSans"), local("Tahoma"); -} - -@font-face { - font-family: system-ui; - font-style: italic; - font-weight: 400; - src: local(".SFNSText-Italic"), local(".HelveticaNeueDeskInterface-Italic"), local(".LucidaGrandeUI"), local("Segoe UI Italic"), local("Ubuntu Italic"), local("Roboto-Italic"), local("DroidSans"), local("Tahoma"); -} - -@font-face { - font-family: system-ui; - font-style: normal; - font-weight: 500; - src: local(".SFNSText-Medium"), local(".HelveticaNeueDeskInterface-MediumP4"), local(".LucidaGrandeUI"), local("Segoe UI Semibold"), local("Ubuntu Medium"), local("Roboto-Medium"), local("DroidSans-Bold"), local("Tahoma Bold"); -} - -@font-face { - font-family: system-ui; - font-style: italic; - font-weight: 500; - src: local(".SFNSText-MediumItalic"), local(".HelveticaNeueDeskInterface-MediumItalicP4"), local(".LucidaGrandeUI"), local("Segoe UI Semibold Italic"), local("Ubuntu Medium Italic"), local("Roboto-MediumItalic"), local("DroidSans-Bold"), local("Tahoma Bold"); -} - -@font-face { - font-family: system-ui; - font-style: normal; - font-weight: 700; - src: local(".SFNSText-Bold"), local(".HelveticaNeueDeskInterface-Bold"), local(".LucidaGrandeUI"), local("Segoe UI Bold"), local("Ubuntu Bold"), local("Roboto-Bold"), local("DroidSans-Bold"), local("Tahoma Bold"); -} - -@font-face { - font-family: system-ui; - font-style: italic; - font-weight: 700; - src: local(".SFNSText-BoldItalic"), local(".HelveticaNeueDeskInterface-BoldItalic"), local(".LucidaGrandeUI"), local("Segoe UI Bold Italic"), local("Ubuntu Bold Italic"), local("Roboto-BoldItalic"), local("DroidSans-Bold"), local("Tahoma Bold"); -} diff --git a/_sass/theme/mixins.scss b/_sass/theme/mixins.scss deleted file mode 100644 index f225462032..0000000000 --- a/_sass/theme/mixins.scss +++ /dev/null @@ -1,469 +0,0 @@ - -@mixin hook-heading-hero(){ - font-weight: bold; -} - -@mixin hook-navbar(){ - font-weight: 500; - .uk-drop { - width: 360px; - } - .uk-search-navbar .uk-search-input { - height: 50px; - font-size: 1.0625rem; - padding-left: 1.1875rem; - background: #fff; - } -} -@mixin hook-button(){ - border-radius: 5px; -} - -// Doc navigation -$docs-sidebar-width: 260px; -$docs-sidebar-width-l: 360px; -.sidebar-fixed-width { - width: $docs-sidebar-width; -} -.sidebar-docs { - width: $docs-sidebar-width - $global-medium-gutter; - padding-right: $global-medium-gutter; - top: 112px; - bottom: 70px; - overflow-y: scroll; - overflow-x: hidden; - > h5 { - margin: 15px 0 0; - &:first-child { - margin-top: 17px - } - } -} - -@media (min-width: $breakpoint-large) { - .sidebar-fixed-width { - width: $docs-sidebar-width-l; - } - .sidebar-docs { - width: $docs-sidebar-width-l - $global-large-gutter; - padding-right: $global-large-gutter; - } -} -ul.doc-nav { - padding-left: 14px; - margin-top: 5px; -} -.doc-nav > li.uk-active > a { - position: relative; - &:before { - content: ""; - position: absolute; - top: 15px; - left: -14px; - width: 7px; - border-top: 1px solid $global-primary-background; - } -} - -// Home page hero -.hero-image img { - max-width: 200px; - max-height: 75px; -} -.button-cta:nth-child(2n), .button-cta:nth-child(3n) { - margin-top: $global-margin; -} -.heading-hero-2 { - font-size: 1.875rem -} -@media (min-width: $breakpoint-small) { - .heading-hero-2 { - // font-size: 2.375rem; - font-size: 1.375rem; - } -} -@media (min-width: $breakpoint-medium) { - .heading-hero-2 { - // font-size: 2.75rem; - font-size: 1.75rem; - } -} -@mixin hook-card-title(){ - font-size: 1.125rem; -} - - - -.list-featured>li:first-child { - margin-top: $list-large-margin-top; -} - - -@mixin hook-base-body(){ - //filter: blur(7px); - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - -} -@mixin hook-base-misc(){ - input[type="submit" i]{ - -webkit-appearance:none; - } -} -@mixin hook-card(){ - border: solid 1px lighten($global-border, 10%); - &:hover { - border-color: darken($global-border, 10%); - } -} -@mixin hook-card-body(){ - > p { - font-size: $global-small-font-size; - } - > span { - color: $global-primary-background; - } -} - -@mixin hook-card-misc(){ - .uk-position-cover { - z-index: 1; - } - .card-category { - h3:nth-child(2n) { - margin-top: 0 !important; - } - } - .card-post { - .uk-card-header{ - padding-top: $global-medium-margin; - padding-bottom: 0; - } - .uk-card-body{ - padding-top: $global-medium-margin / 2; - padding-bottom: $global-medium-margin / 2; - } - .uk-card-footer{ - padding-bottom: $global-medium-margin * 0.8; - padding-top: 0; - } - } -} - -@mixin hook-form(){ - border: solid 1px $global-border; -} - -@mixin hook-offcanvas-bar(){ - font-weight: 500; -} - -@mixin hook-list-misc(){ - .uk-list { - margin: 0; - } -} - -@mixin hook-article(){ - .share { - } - figure, .uk-slideshow { - margin-top: $global-medium-margin * 1.1; - margin-bottom: $global-medium-margin * 1.5; - } - figure { - img + div .uk-overlay-icon { - color: rgba(255, 255, 255, 0); - } - img:hover + div .uk-overlay-icon { - color: rgba(255, 255, 255, 1); - } - figcaption { - margin-left: 0; - span { - padding-right: 20px; - margin-bottom: -43px; - margin-top: 20px; - border-right: solid 2px $global-muted-color; - font-style: italic; - font-size: 0.8rem; - line-height: 1.8; - } - } - } - blockquote { - border-left: solid 2px $global-muted-color; - padding-left: $base-margin-vertical; - line-height: 1.7; - margin-top: $global-medium-margin; - margin-bottom: $global-medium-margin; - } - .highlight, - .highlighter-rouge { - margin-top: $global-medium-margin; - margin-bottom: $global-medium-margin; - } -} -@mixin hook-article-title(){ - margin-bottom: $global-margin; -} -@mixin hook-article-meta(){ - a { - color: $article-meta-color; - &:hover { - color: $global-primary-background; - } - } - .avatar { - margin-right: 10px; - float: left; - } -} -@mixin hook-article-misc(){ - .article-content { - line-height: 1.8; - } - .avatar { - border-radius: 50%; - } - .paginate-post .uk-text-small { - line-height: 1.75; - } -} - -// -// Utility -// -// ======================================================================== -@mixin hook-utility-misc(){ - .remove-underline, .remove-underline:hover{ - text-decoration: none; - } - .link-dark { - color: $color-main !important; - } - .uk-container.uk-container-xsmall { - max-width: 700px; - } - .hvr-forward { - display: inline-block; - vertical-align: middle; - -webkit-transform: perspective(1px) translateZ(0); - transform: perspective(1px) translateZ(0); - box-shadow: 0 0 1px transparent; - -webkit-transition-duration: 0.3s; - transition-duration: 0.3s; - -webkit-transition-property: transform; - transition-property: transform; - } - .hvr-forward:active, - .hvr-forward:focus, - .hvr-forward:hover { - -webkit-transform: translateX(6px); - transform: translateX(6px); - } - .hvr-back { - display: inline-block; - vertical-align: middle; - -webkit-transform: perspective(1px) translateZ(0); - transform: perspective(1px) translateZ(0); - box-shadow: 0 0 1px transparent; - -webkit-transition-duration: 0.3s; - transition-duration: 0.3s; - -webkit-transition-property: transform; - transition-property: transform; - } - .hvr-back:hover, - .hvr-back:focus, - .hvr-back:active { - -webkit-transform: translateX(-6px); - transform: translateX(-6px); - } - .social-networks { - margin-top: $global-large-margin; - } - header { - .uk-logo { - color: $global-primary-background; - &:hover { - color: $global-primary-background; - } - } - } - .section-title { - margin-bottom: $global-medium-margin; - ~ .section-title { - margin-top: $global-medium-margin; - } - } - @media (min-width: $breakpoint-medium) { - .section-title { - ~ .section-title { - margin-top: $global-large-margin; - } - } - } - .section-hero { - .searchBox { - max-width: 550px; - margin: 60px auto 0 auto; - .uk-search-input { - height: 50px; - border-radius: 50px; - color: $global-muted-color; - font-style: normal; - &:focus { - background: #ffffff; - } - } - .uk-search-icon { - width: 50px; - color: $global-muted-color; - } - - } - } - footer { - .uk-subnav>.uk-active>a { - color: $global-muted-color; - } - } - #markdown-toc { - padding: 0 0 0 $global-margin; - border-left: solid 2px $global-muted-color; - list-style: none; - margin-bottom: $global-medium-margin; - > li > :last-child { - margin-bottom: 0; - } - ul { - margin: 0; - padding-left: $global-margin; - list-style: none; - } - >li:nth-child(n+2), >li>ul { - margin-top: 5px; - } - a { - color: $global-muted-color; - } - - } - .uk-article-content .no_toc { - margin-top: $global-medium-margin; - margin-bottom: $global-medium-margin; - } - #searchBox-results, #searchBox-results { - margin: 10px 0 0 0; - z-index: 1; - li { - margin: 0; - padding: 20px 25px 0; - background: #fff; - line-height: 1.4; - border-left: solid 1px $global-border; - border-right: solid 1px $global-border; - &:first-child { - border-top-left-radius: 5px; - border-top-right-radius: 5px; - border-top: solid 1px $global-border; - } - &:last-child { - border-bottom-left-radius: 5px; - border-bottom-right-radius: 5px; - padding-bottom: 25px; - border-bottom: solid 1px $global-border; - } - a:hover { - text-decoration: none; - } - } - } - -} - - -$tm-timeline-border-width: 4px; -$tm-timeline-dot-diameter: 20px; - -@media (min-width: $breakpoint-small) { - .tm-timeline { - box-sizing: border-box; - * { - box-sizing: border-box; - } - position: relative; - - &:before { - content: ''; - position: absolute; - top: 0; - left: calc(30% - 2px); - bottom: 0; - width: $tm-timeline-border-width; - background: $color-main; - } - - &:after { - content: ""; - display: table; - clear: both; - } - } - - .tm-timeline-entry { - + .tm-timeline-entry{ - margin-top: $global-large-margin; - } - clear: both; - text-align: left; - position: relative; - - &:after { - display: block; - content: ""; - clear: both; - } - - .tm-timeline-time { - float: left; - width: 30%; - padding-right: $global-large-margin; - text-align: right; - position: relative; - - &:before { - content: ''; - position: absolute; - width: $tm-timeline-dot-diameter; - height: $tm-timeline-dot-diameter; - border: $tm-timeline-border-width solid $color-main; - background-color: #fff; - border-radius: 100%; - top: 0; - right: - 14px; - z-index: 99; - } - - h5 { - margin: 3px 0 0; - } - } - - .tm-timeline-body { - float: right; - width: 70%; - padding-left: $global-large-margin; - margin-top: -2px; - h3 { - margin: 0 0 15px; - span { - font-size: .7rem; - margin-bottom: 4px; - padding: 0 5px; - } - } - - } - } -} diff --git a/_sass/theme/uikit.scss b/_sass/theme/uikit.scss deleted file mode 100644 index 59e87618e9..0000000000 --- a/_sass/theme/uikit.scss +++ /dev/null @@ -1,92 +0,0 @@ -// Import UIkit components - -// Base -@import "../uikit/components/variables.scss"; -@import "../uikit/components/mixin.scss"; -@import "../uikit/components/base.scss"; - -// Elements -@import "../uikit/components/link.scss"; -@import "../uikit/components/heading.scss"; -@import "../uikit/components/divider.scss"; -@import "../uikit/components/list.scss"; -// @import "../uikit/components/description-list.scss"; -// @import "../uikit/components/table.scss"; -@import "../uikit/components/icon.scss"; -// @import "../uikit/components/form-range.scss"; -@import "../uikit/components/form.scss"; // After: Icon, Form Range -@import "../uikit/components/button.scss"; - -// Layout -@import "../uikit/components/section.scss"; -@import "../uikit/components/container.scss"; -@import "../uikit/components/grid.scss"; -@import "../uikit/components/tile.scss"; -@import "../uikit/components/card.scss"; - -// Common -@import "../uikit/components/close.scss"; // After: Icon -// @import "../uikit/components/spinner.scss"; // After: Icon -@import "../uikit/components/totop.scss"; // After: Icon -// @import "../uikit/components/marker.scss"; // After: Icon -// @import "../uikit/components/alert.scss"; // After: Close -// @import "../uikit/components/badge.scss"; -@import "../uikit/components/label.scss"; -@import "../uikit/components/overlay.scss"; // After: Icon -@import "../uikit/components/article.scss"; // After: Subnav -// @import "../uikit/components/comment.scss"; // After: Subnav -@import "../uikit/components/search.scss"; // After: Icon - -// Navs -@import "../uikit/components/nav.scss"; -@import "../uikit/components/navbar.scss"; // After: Card, Grid, Nav, Icon, Search -@import "../uikit/components/subnav.scss"; -// @import "../uikit/components/breadcrumb.scss"; -@import "../uikit/components/pagination.scss"; -// @import "../uikit/components/tab.scss"; -// @import "../uikit/components/slidenav.scss"; // After: Icon -// @import "../uikit/components/dotnav.scss"; -// @import "../uikit/components/thumbnav.scss"; - -// JavaScript -// @import "../uikit/components/accordion.scss"; -@import "../uikit/components/drop.scss"; // After: Card -@import "../uikit/components/dropdown.scss"; // After: Card -// @import "../uikit/components/modal.scss"; // After: Close -@import "../uikit/components/lightbox.scss"; // After: Close -@import "../uikit/components/slideshow.scss"; -@import "../uikit/components/sticky.scss"; -@import "../uikit/components/offcanvas.scss"; -// @import "../uikit/components/switcher.scss"; -// Scrollspy -// Toggle -// Scroll - -// Additional -// @import "../uikit/components/iconnav.scss"; -// @import "../uikit/components/notification.scss"; -// @import "../uikit/components/tooltip.scss"; -// @import "../uikit/components/placeholder.scss"; -// @import "../uikit/components/progress.scss"; -// @import "../uikit/components/sortable.scss"; -// @import "../uikit/components/countdown.scss"; - -// Utilities -@import "../uikit/components/animation.scss"; -@import "../uikit/components/width.scss"; -@import "../uikit/components/text.scss"; -@import "../uikit/components/column.scss"; -@import "../uikit/components/cover.scss"; -// @import "../uikit/components/background.scss"; -@import "../uikit/components/align.scss"; -@import "../uikit/components/utility.scss"; -@import "../uikit/components/flex.scss"; // After: Utility -@import "../uikit/components/margin.scss"; -@import "../uikit/components/padding.scss"; -@import "../uikit/components/position.scss"; -// @import "../uikit/components/transition.scss"; -@import "../uikit/components/visibility.scss"; -// @import "../uikit/components/inverse.scss"; - -// Need to be loaded last -@import "../uikit/components/print.scss"; diff --git a/_sass/theme/variables.scss b/_sass/theme/variables.scss deleted file mode 100644 index a4d98d34d5..0000000000 --- a/_sass/theme/variables.scss +++ /dev/null @@ -1,90 +0,0 @@ -// Color scheme variables - -// Main content -$color-main: #0F1214; - -// Secondary content -$global-muted-color: #7a838a; - -// Accent color -$global-primary-background: $color-main; - -// Link and botton hover -$color-hover: #000; - -// Standard UIkit -//$global-muted-background: ; -$global-border: #c4c7ca; - -// Global -$global-font-family: system-ui; -$global-link-color: $global-muted-color; -$global-link-hover-color: $color-hover; - -$global-xxlarge-font-size: 1.875rem;; -$global-xlarge-font-size: 1.625rem; -$global-large-font-size: 1.375rem; -$global-medium-font-size: 1.125rem; -$global-small-font-size: 0.875rem; - -// Base -$base-body-color: $color-main; -$base-heading-color: $color-main; -$link-muted-hover-color: $color-main; -$button-primary-hover-background: $color-hover; -$base-hr-border: lighten($global-border, 13%); -$text-lead-font-size: 1.125rem; -$base-heading-font-weight: bold; -$base-heading-margin-top: 50px; -// $base-body-font-weight: 300; -$base-h2-line-height: 1.4; -$base-link-hover-text-decoration: underline; -// $base-h1-font-size: $global-xxlarge-font-size !default; -// $base-h2-font-size: $global-xlarge-font-size !default; -// $base-h3-font-size: $global-large-font-size !default; -// $base-h4-font-size: $global-medium-font-size !default; -// $base-h5-font-size: $global-font-size !default; -// $base-h6-font-size: $global-small-font-size !default; - -// Navbar -$navbar-background: #fff; -$navbar-nav-item-color: $global-muted-color; -$navbar-nav-item-hover-color: $color-main; -$navbar-nav-item-active-color: $color-main; -$navbar-toggle-color: $global-muted-color; -$navbar-toggle-hover-color: $color-main; - -// Subnav -$subnav-item-color: $global-muted-color; -$subnav-item-hover-color: $color-main; - -// Pagination links -$pagination-item-color: $color-main; -$pagination-item-hover-color: $color-main; - -$article-meta-font-size: 0.8125rem; -$article-meta-line-height: 1.3; - -$container-max-width: 1100px; -$container-small-max-width: 800px; - -$card-small-body-padding-horizontal: 25px; -$card-small-body-padding-vertical: 25px; - -$article-title-line-height: 1.4; -$article-title-font-size: 1.875rem; -$offcanvas-bar-color-mode: dark; -$offcanvas-bar-background: #fff; - -$heading-hero-font-size: 2.375rem; -$heading-hero-line-height: 1.3; -$heading-hero-font-size-s: 2.9375rem; -$heading-hero-line-height-s: 1.3; -$heading-hero-font-size-m: 3.625rem; -$heading-hero-line-height-m: 1.4; - -$navbar-nav-item-text-transform: none; - -$subnav-item-text-transform: none; - -$search-default-border: darken($global-border, 15%); diff --git a/_sass/uikit/components/_import.components.scss b/_sass/uikit/components/_import.components.scss deleted file mode 100644 index 445384fa54..0000000000 --- a/_sass/uikit/components/_import.components.scss +++ /dev/null @@ -1,56 +0,0 @@ -// Base -@import "variables.scss"; -@import "mixin.scss"; -@import "base.scss"; - -// Elements -@import "link.scss"; -@import "heading.scss"; -@import "divider.scss"; -@import "list.scss"; -@import "description-list.scss"; -@import "table.scss"; -@import "icon.scss"; -@import "form.scss"; // After: Icon -@import "button.scss"; - -// Layout -@import "section.scss"; -@import "container.scss"; -@import "grid.scss"; -@import "tile.scss"; -@import "card.scss"; - -// Common -@import "close.scss"; // After: Icon -@import "spinner.scss"; // After: Icon -@import "totop.scss"; // After: Icon -@import "alert.scss"; // After: Close -@import "badge.scss"; -@import "label.scss"; -@import "overlay.scss"; // After: Icon -@import "article.scss"; // After: Subnav -@import "comment.scss"; // After: Subnav -@import "search.scss"; // After: Icon - -// Navs -@import "nav.scss"; -@import "navbar.scss"; // After: Card, Grid, Nav, Icon, Search -@import "subnav.scss"; -@import "breadcrumb.scss"; -@import "pagination.scss"; -@import "tab.scss"; -@import "slidenav.scss"; // After: Icon -@import "dotnav.scss"; - -// JavaScript -@import "accordion.scss"; -@import "drop.scss"; // After: Card -@import "dropdown.scss"; // After: Card -@import "modal.scss"; // After: Close -@import "sticky.scss"; -@import "offcanvas.scss"; -@import "switcher.scss"; -// Scrollspy -// Toggle -// Scroll diff --git a/_sass/uikit/components/_import.scss b/_sass/uikit/components/_import.scss deleted file mode 100644 index b3749a2984..0000000000 --- a/_sass/uikit/components/_import.scss +++ /dev/null @@ -1,91 +0,0 @@ -// Base -@import "variables.scss"; -@import "mixin.scss"; -@import "base.scss"; - -// Elements -@import "link.scss"; -@import "heading.scss"; -@import "divider.scss"; -@import "list.scss"; -@import "description-list.scss"; -@import "table.scss"; -@import "icon.scss"; -@import "form-range.scss"; -@import "form.scss"; // After: Icon, Form Range -@import "button.scss"; - -// Layout -@import "section.scss"; -@import "container.scss"; -@import "grid.scss"; -@import "tile.scss"; -@import "card.scss"; - -// Common -@import "close.scss"; // After: Icon -@import "spinner.scss"; // After: Icon -@import "totop.scss"; // After: Icon -@import "marker.scss"; // After: Icon -@import "alert.scss"; // After: Close -@import "badge.scss"; -@import "label.scss"; -@import "overlay.scss"; // After: Icon -@import "article.scss"; // After: Subnav -@import "comment.scss"; // After: Subnav -@import "search.scss"; // After: Icon - -// Navs -@import "nav.scss"; -@import "navbar.scss"; // After: Card, Grid, Nav, Icon, Search -@import "subnav.scss"; -@import "breadcrumb.scss"; -@import "pagination.scss"; -@import "tab.scss"; -@import "slidenav.scss"; // After: Icon -@import "dotnav.scss"; -@import "thumbnav.scss"; - -// JavaScript -@import "accordion.scss"; -@import "drop.scss"; // After: Card -@import "dropdown.scss"; // After: Card -@import "modal.scss"; // After: Close -@import "lightbox.scss"; // After: Close -@import "slideshow.scss"; -@import "slider.scss"; -@import "sticky.scss"; -@import "offcanvas.scss"; -@import "switcher.scss"; -// Scrollspy -// Toggle -// Scroll - -// Additional -@import "iconnav.scss"; -@import "notification.scss"; -@import "tooltip.scss"; -@import "placeholder.scss"; -@import "progress.scss"; -@import "sortable.scss"; -@import "countdown.scss"; - -// Utilities -@import "animation.scss"; -@import "width.scss"; -@import "text.scss"; -@import "column.scss"; -@import "cover.scss"; -@import "background.scss"; -@import "align.scss"; -@import "utility.scss"; -@import "flex.scss"; // After: Utility -@import "margin.scss"; -@import "padding.scss"; -@import "position.scss"; -@import "transition.scss"; -@import "visibility.scss"; -@import "inverse.scss"; - -// Need to be loaded last -@import "print.scss"; diff --git a/_sass/uikit/components/_import.utilities.scss b/_sass/uikit/components/_import.utilities.scss deleted file mode 100644 index bea39e5c72..0000000000 --- a/_sass/uikit/components/_import.utilities.scss +++ /dev/null @@ -1,19 +0,0 @@ -// Utilities -@import "animation.scss"; -@import "width.scss"; -@import "text.scss"; -@import "column.scss"; -@import "cover.scss"; -@import "background.scss"; -@import "align.scss"; -@import "utility.scss"; -@import "flex.scss"; // After: Utility -@import "margin.scss"; -@import "padding.scss"; -@import "position.scss"; -@import "transition.scss"; -@import "visibility.scss"; -@import "inverse.scss"; - -// Need to be loaded last -@import "print.scss"; diff --git a/_sass/uikit/components/accordion.scss b/_sass/uikit/components/accordion.scss deleted file mode 100644 index bfb0e815d9..0000000000 --- a/_sass/uikit/components/accordion.scss +++ /dev/null @@ -1,118 +0,0 @@ -// Name: Accordion -// Description: Component to create accordions -// -// Component: `uk-accordion` -// -// Sub-objects: `uk-accordion-title` -// `uk-accordion-content` -// -// States: `uk-open` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$accordion-item-margin-top: $global-margin !default; - -$accordion-title-font-size: $global-medium-font-size !default; -$accordion-title-line-height: 1.4 !default; -$accordion-title-color: $global-emphasis-color !default; -$accordion-title-hover-color: $global-color !default; - -$accordion-content-margin-top: $global-margin !default; - - -/* ======================================================================== - Component: Accordion - ========================================================================== */ - -.uk-accordion { - padding: 0; - list-style: none; - @if(mixin-exists(hook-accordion)) {@include hook-accordion();} -} - - -/* Item - ========================================================================== */ - -.uk-accordion > :nth-child(n+2) { - margin-top: $accordion-item-margin-top; - @if(mixin-exists(hook-accordion-item)) {@include hook-accordion-item();} -} - - -/* Title - ========================================================================== */ - -.uk-accordion-title { - display: block; - font-size: $accordion-title-font-size; - line-height: $accordion-title-line-height; - color: $accordion-title-color; - @if(mixin-exists(hook-accordion-title)) {@include hook-accordion-title();} -} - -/* Hover + Focus */ -.uk-accordion-title:hover, -.uk-accordion-title:focus { - color: $accordion-title-hover-color; - text-decoration: none; - outline: none; - @if(mixin-exists(hook-accordion-title-hover)) {@include hook-accordion-title-hover();} -} - - -/* Content - ========================================================================== */ - -.uk-accordion-content { - margin-top: $accordion-content-margin-top; - @if(mixin-exists(hook-accordion-content)) {@include hook-accordion-content();} -} - -/* - * Micro clearfix - */ - -.uk-accordion-content::before, -.uk-accordion-content::after { - content: ""; - display: table; -} - -.uk-accordion-content::after { clear: both; } - -/* - * Remove margin from the last-child - */ - - .uk-accordion-content > :last-child { margin-bottom: 0; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-accordion-misc)) {@include hook-accordion-misc();} - -// @mixin hook-accordion(){} -// @mixin hook-accordion-item(){} -// @mixin hook-accordion-title(){} -// @mixin hook-accordion-title-hover(){} -// @mixin hook-accordion-content(){} -// @mixin hook-accordion-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-accordion-title-color: $inverse-global-emphasis-color !default; -$inverse-accordion-title-hover-color: $inverse-global-inverse-color !default; - - - -// @mixin hook-inverse-accordion-item(){} -// @mixin hook-inverse-accordion-title(){} -// @mixin hook-inverse-accordion-title-hover(){} \ No newline at end of file diff --git a/_sass/uikit/components/alert.scss b/_sass/uikit/components/alert.scss deleted file mode 100644 index 8922cc8543..0000000000 --- a/_sass/uikit/components/alert.scss +++ /dev/null @@ -1,147 +0,0 @@ -// Name: Alert -// Description: Component to create alert messages -// -// Component: `uk-alert` -// -// Adopted: `uk-alert-close` -// -// Modifiers: `uk-alert-primary` -// `uk-alert-success` -// `uk-alert-warning` -// `uk-alert-danger` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$alert-margin-vertical: $global-margin !default; -$alert-padding: $global-small-gutter !default; -$alert-padding-right: $alert-padding + 14px !default; -$alert-background: $global-muted-background !default; -$alert-color: $global-color !default; - -$alert-close-top: $alert-padding + 5px !default; -$alert-close-right: $alert-padding !default; - -$alert-primary-background: lighten(mix(white, $global-primary-background, 40%), 20%) !default; -$alert-primary-color: $global-primary-background !default; - -$alert-success-background: lighten(mix(white, $global-success-background, 40%), 25%) !default; -$alert-success-color: $global-success-background !default; - -$alert-warning-background: lighten(mix(white, $global-warning-background, 45%), 15%) !default; -$alert-warning-color: $global-warning-background !default; - -$alert-danger-background: lighten(mix(white, $global-danger-background, 40%), 20%) !default; -$alert-danger-color: $global-danger-background !default; - - -/* ======================================================================== - Component: Alert - ========================================================================== */ - -.uk-alert { - position: relative; - margin-bottom: $alert-margin-vertical; - padding: $alert-padding $alert-padding-right $alert-padding $alert-padding; - background: $alert-background; - color: $alert-color; - @if(mixin-exists(hook-alert)) {@include hook-alert();} -} - -/* Add margin if adjacent element */ -* + .uk-alert { margin-top: $alert-margin-vertical; } - -/* - * Remove margin from the last-child - */ - -.uk-alert > :last-child { margin-bottom: 0; } - - -/* Close - * Adopts `uk-close` - ========================================================================== */ - -.uk-alert-close { - position: absolute; - top: $alert-close-top; - right: $alert-close-right; - @if(mixin-exists(hook-alert-close)) {@include hook-alert-close();} -} - -/* - * Remove margin from adjacent element - */ - -.uk-alert-close:first-child + * { margin-top: 0; } - -/* - * Hover + Focus - */ - -.uk-alert-close:hover, -.uk-alert-close:focus { - @if(mixin-exists(hook-alert-close-hover)) {@include hook-alert-close-hover();} -} - - -/* Style modifiers - ========================================================================== */ - -/* - * Primary - */ - -.uk-alert-primary { - background: $alert-primary-background; - color: $alert-primary-color; - @if(mixin-exists(hook-alert-primary)) {@include hook-alert-primary();} -} - -/* - * Success - */ - -.uk-alert-success { - background: $alert-success-background; - color: $alert-success-color; - @if(mixin-exists(hook-alert-success)) {@include hook-alert-success();} -} - -/* - * Warning - */ - -.uk-alert-warning { - background: $alert-warning-background; - color: $alert-warning-color; - @if(mixin-exists(hook-alert-warning)) {@include hook-alert-warning();} -} - -/* - * Danger - */ - -.uk-alert-danger { - background: $alert-danger-background; - color: $alert-danger-color; - @if(mixin-exists(hook-alert-danger)) {@include hook-alert-danger();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-alert-misc)) {@include hook-alert-misc();} - -// @mixin hook-alert(){} -// @mixin hook-alert-close(){} -// @mixin hook-alert-close-hover(){} -// @mixin hook-alert-primary(){} -// @mixin hook-alert-success(){} -// @mixin hook-alert-warning(){} -// @mixin hook-alert-danger(){} -// @mixin hook-alert-misc(){} \ No newline at end of file diff --git a/_sass/uikit/components/align.scss b/_sass/uikit/components/align.scss deleted file mode 100644 index bee6702bca..0000000000 --- a/_sass/uikit/components/align.scss +++ /dev/null @@ -1,142 +0,0 @@ -// Name: Align -// Description: Utilities to align embedded content -// -// Component: `uk-align-left-*` -// `uk-align-right-*` -// `uk-align-center` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$align-margin-horizontal: $global-gutter !default; -$align-margin-vertical: $global-gutter !default; - -$align-margin-horizontal-l: $global-medium-gutter !default; - - -/* ======================================================================== - Component: Align - ========================================================================== */ - -/* - * Default - */ - -[class*='uk-align'] { - display: block; - margin-bottom: $align-margin-vertical; -} - -* + [class*='uk-align'] { margin-top: $align-margin-vertical; } - -/* - * Center - */ - -.uk-align-center { - margin-left: auto; - margin-right: auto; -} - -/* - * Left/Right - */ - -.uk-align-left { - margin-top: 0; - margin-right: $align-margin-horizontal; - float: left; -} - -.uk-align-right { - margin-top: 0; - margin-left: $align-margin-horizontal; - float: right; -} - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - .uk-align-left\@s { - margin-top: 0; - margin-right: $align-margin-horizontal; - float: left; - } - - .uk-align-right\@s { - margin-top: 0; - margin-left: $align-margin-horizontal; - float: right; - } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-align-left\@m { - margin-top: 0; - margin-right: $align-margin-horizontal; - float: left; - } - - .uk-align-right\@m { - margin-top: 0; - margin-left: $align-margin-horizontal; - float: right; - } - -} - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-align-left\@l { - margin-top: 0; - float: left; - } - - .uk-align-right\@l { - margin-top: 0; - float: right; - } - - .uk-align-left, - .uk-align-left\@s, - .uk-align-left\@m, - .uk-align-left\@l { margin-right: $align-margin-horizontal-l; } - - .uk-align-right, - .uk-align-right\@s, - .uk-align-right\@m, - .uk-align-right\@l { margin-left: $align-margin-horizontal-l; } - -} - -/* Large screen and bigger */ -@media (min-width: $breakpoint-xlarge) { - - .uk-align-left\@xl { - margin-top: 0; - margin-right: $align-margin-horizontal-l; - float: left; - } - - .uk-align-right\@xl { - margin-top: 0; - margin-left: $align-margin-horizontal-l; - float: right; - } - -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-align-misc)) {@include hook-align-misc();} - -// @mixin hook-align-misc(){} \ No newline at end of file diff --git a/_sass/uikit/components/animation.scss b/_sass/uikit/components/animation.scss deleted file mode 100644 index c1d91928fa..0000000000 --- a/_sass/uikit/components/animation.scss +++ /dev/null @@ -1,390 +0,0 @@ -// Name: Animation -// Description: Utilities for keyframe animations -// -// Component: `uk-animation-*` -// -// Modifiers: `uk-animation-reverse` -// `uk-animation-fast` -// `uk-animation-fade` -// `uk-animation-scale-up` -// `uk-animation-scale-down` -// `uk-animation-slide-top-*` -// `uk-animation-slide-bottom-*` -// `uk-animation-slide-left-*` -// `uk-animation-slide-right-*` -// `uk-animation-kenburns` -// `uk-animation-shake` -// -// Sub-objects: `uk-animation-toggle` -// -// States: `uk-hover` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$animation-duration: 0.5s !default; -$animation-fade-duration: 0.8s !default; -$animation-kenburns-duration: 15s !default; -$animation-fast-duration: 0.1s !default; - -$animation-slide-small-translate: 10px !default; -$animation-slide-medium-translate: 50px !default; - - -/* ======================================================================== - Component: Animation - ========================================================================== */ - -[class*='uk-animation-'] { - animation-duration: $animation-duration; - animation-timing-function: ease-out; - animation-fill-mode: both; -} - - -/* Direction modifier - ========================================================================== */ - -.uk-animation-reverse { - animation-direction: reverse; - animation-timing-function: ease-in; -} - - -/* Animations for scrollspy - ========================================================================== */ - -/* - * Fade - */ - -.uk-animation-fade { - animation-name: uk-fade; - animation-duration: $animation-fade-duration; - animation-timing-function: linear; -} - -/* - * Scale - */ - -.uk-animation-scale-up { animation-name: uk-fade-scale-02; } -.uk-animation-scale-down { animation-name: uk-fade-scale-18; } - -/* - * Slide - */ - -.uk-animation-slide-top { animation-name: uk-fade-top; } -.uk-animation-slide-bottom { animation-name: uk-fade-bottom; } -.uk-animation-slide-left { animation-name: uk-fade-left; } -.uk-animation-slide-right { animation-name: uk-fade-right; } - -/* - * Slide Small - */ - -.uk-animation-slide-top-small { animation-name: uk-fade-top-small; } -.uk-animation-slide-bottom-small { animation-name: uk-fade-bottom-small; } -.uk-animation-slide-left-small { animation-name: uk-fade-left-small; } -.uk-animation-slide-right-small { animation-name: uk-fade-right-small; } - -/* - * Slide Medium - */ - -.uk-animation-slide-top-medium { animation-name: uk-fade-top-medium; } -.uk-animation-slide-bottom-medium { animation-name: uk-fade-bottom-medium; } -.uk-animation-slide-left-medium { animation-name: uk-fade-left-medium; } -.uk-animation-slide-right-medium { animation-name: uk-fade-right-medium; } - -/* - * Kenburns - */ - -.uk-animation-kenburns { - animation-name: uk-scale-kenburns; - animation-duration: $animation-kenburns-duration; -} - -/* - * Shake - */ - -.uk-animation-shake { animation-name: uk-shake; } - - -/* Duration modifier - ========================================================================== */ - -.uk-animation-fast { animation-duration: $animation-fast-duration; } - - -/* Enable animation only on hover -========================================================================== */ - -/* - * Note: Firefox and IE needs this because animations are not triggered when switching between display `none` and `block` - */ - -.uk-animation-toggle:not(:hover):not(.uk-hover) [class*='uk-animation-'] { animation-name: none; } - - -/* Keyframes used by animation classes - ========================================================================== */ - -/* - * Fade - */ - -@keyframes uk-fade { - 0% { opacity: 0; } - 100% { opacity: 1; } -} - -/* - * Slide Top - */ - -@keyframes uk-fade-top { - 0% { - opacity: 0; - transform: translateY(-100%); - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -/* - * Slide Bottom - */ - -@keyframes uk-fade-bottom { - 0% { - opacity: 0; - transform: translateY(100%); - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -/* - * Slide Left - */ - -@keyframes uk-fade-left { - 0% { - opacity: 0; - transform: translateX(-100%); - } - 100% { - opacity: 1; - transform: translateX(0); - } -} - -/* - * Slide Right - */ - -@keyframes uk-fade-right { - 0% { - opacity: 0; - transform: translateX(100%); - } - 100% { - opacity: 1; - transform: translateX(0); - } -} - -/* - * Slide Top Small - */ - -@keyframes uk-fade-top-small { - 0% { - opacity: 0; - transform: translateY(-$animation-slide-small-translate); - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -/* - * Slide Bottom Small - */ - -@keyframes uk-fade-bottom-small { - 0% { - opacity: 0; - transform: translateY($animation-slide-small-translate); - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -/* - * Slide Left Small - */ - -@keyframes uk-fade-left-small { - 0% { - opacity: 0; - transform: translateX(-$animation-slide-small-translate); - } - 100% { - opacity: 1; - transform: translateX(0); - } -} - -/* - * Slide Right Small - */ - -@keyframes uk-fade-right-small { - 0% { - opacity: 0; - transform: translateX($animation-slide-small-translate); - } - 100% { - opacity: 1; - transform: translateX(0); - } -} - -/* - * Slide Top Medium - */ - -@keyframes uk-fade-top-medium { - 0% { - opacity: 0; - transform: translateY(-$animation-slide-medium-translate); - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -/* - * Slide Bottom Medium - */ - -@keyframes uk-fade-bottom-medium { - 0% { - opacity: 0; - transform: translateY($animation-slide-medium-translate); - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -/* - * Slide Left Medium - */ - -@keyframes uk-fade-left-medium { - 0% { - opacity: 0; - transform: translateX(-$animation-slide-medium-translate); - } - 100% { - opacity: 1; - transform: translateX(0); - } -} - -/* - * Slide Right Medium - */ - -@keyframes uk-fade-right-medium { - 0% { - opacity: 0; - transform: translateX($animation-slide-medium-translate); - } - 100% { - opacity: 1; - transform: translateX(0); - } -} - -/* - * Scale Up - */ - -@keyframes uk-fade-scale-02 { - 0% { - opacity: 0; - transform: scale(0.2); - } - 100% { - opacity: 1; - transform: scale(1); - } -} - -/* - * Scale Down - */ - -@keyframes uk-fade-scale-18 { - 0% { - opacity: 0; - transform: scale(1.8); - } - 100% { - opacity: 1; - transform: scale(1); - } -} - -/* - * Kenburns - */ - -@keyframes uk-scale-kenburns { - 0% { transform: scale(1); } - 100% { transform: scale(1.2); } -} - -/* - * Shake - */ - -@keyframes uk-shake { - 0%, 100% { transform: translateX(0); } - 10% { transform: translateX(-9px); } - 20% { transform: translateX(8px); } - 30% { transform: translateX(-7px); } - 40% { transform: translateX(6px); } - 50% { transform: translateX(-5px); } - 60% { transform: translateX(4px); } - 70% { transform: translateX(-3px); } - 80% { transform: translateX(2px); } - 90% { transform: translateX(-1px); } -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-animation-misc)) {@include hook-animation-misc();} - -// @mixin hook-animation-misc(){} diff --git a/_sass/uikit/components/article.scss b/_sass/uikit/components/article.scss deleted file mode 100644 index 9cc470f2c7..0000000000 --- a/_sass/uikit/components/article.scss +++ /dev/null @@ -1,102 +0,0 @@ -// Name: Article -// Description: Component to create articles -// -// Component: `uk-article` -// -// Sub-objects: `uk-article-title` -// `uk-article-meta` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$article-margin-top: $global-large-margin !default; - -$article-title-font-size: $global-xxlarge-font-size !default; -$article-title-line-height: 1.2 !default; - -$article-meta-font-size: $global-small-font-size !default; -$article-meta-line-height: 1.4 !default; -$article-meta-color: $global-muted-color !default; - - -/* ======================================================================== - Component: Article - ========================================================================== */ - -.uk-article { - @if(mixin-exists(hook-article)) {@include hook-article();} -} - -/* - * Micro clearfix - */ - -.uk-article::before, -.uk-article::after { - content: ""; - display: table; -} - -.uk-article::after { clear: both; } - -/* - * Remove margin from the last-child - */ - -.uk-article > :last-child { margin-bottom: 0; } - - -/* Adjacent sibling - ========================================================================== */ - -.uk-article + .uk-article { - margin-top: $article-margin-top; - @if(mixin-exists(hook-article-adjacent)) {@include hook-article-adjacent();} -} - - -/* Title - ========================================================================== */ - -.uk-article-title { - font-size: $article-title-font-size; - line-height: $article-title-line-height; - @if(mixin-exists(hook-article-title)) {@include hook-article-title();} -} - - -/* Meta - ========================================================================== */ - -.uk-article-meta { - font-size: $article-meta-font-size; - line-height: $article-meta-line-height; - color: $article-meta-color; - @if(mixin-exists(hook-article-meta)) {@include hook-article-meta();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-article-misc)) {@include hook-article-misc();} - -// @mixin hook-article(){} -// @mixin hook-article-adjacent(){} -// @mixin hook-article-title(){} -// @mixin hook-article-meta(){} -// @mixin hook-article-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-article-meta-color: $inverse-global-muted-color !default; - - - -// @mixin hook-inverse-article-title(){} -// @mixin hook-inverse-article-meta(){} \ No newline at end of file diff --git a/_sass/uikit/components/background.scss b/_sass/uikit/components/background.scss deleted file mode 100644 index ca7a344a10..0000000000 --- a/_sass/uikit/components/background.scss +++ /dev/null @@ -1,136 +0,0 @@ -// Name: Background -// Description: Utilities for backgrounds -// -// Component: `uk-background-*` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$background-default-background: $global-background !default; -$background-muted-background: $global-muted-background !default; -$background-primary-background: $global-primary-background !default; -$background-secondary-background: $global-secondary-background !default; - - -/* ======================================================================== - Component: Background - ========================================================================== */ - - -/* Color - ========================================================================== */ - -.uk-background-default { background-color: $background-default-background; } -.uk-background-muted { background-color: $background-muted-background; } -.uk-background-primary { background-color: $background-primary-background; } -.uk-background-secondary { background-color: $background-secondary-background; } - - -/* Size - ========================================================================== */ - -.uk-background-cover, -.uk-background-contain { - background-position: 50% 50%; - background-repeat: no-repeat; -} - -.uk-background-cover { background-size: cover; } -.uk-background-contain { background-size: contain; } - - -/* Position - ========================================================================== */ - -.uk-background-top-left { background-position: 0 0; } -.uk-background-top-center { background-position: 50% 0; } -.uk-background-top-right { background-position: 100% 0; } -.uk-background-center-left { background-position: 0 50%; } -.uk-background-center-center { background-position: 50% 50%; } -.uk-background-center-right { background-position: 100% 50%; } -.uk-background-bottom-left { background-position: 0 100%; } -.uk-background-bottom-center { background-position: 50% 100%; } -.uk-background-bottom-right { background-position: 100% 100%; } - - -/* Repeat - ========================================================================== */ - -.uk-background-norepeat { background-repeat: no-repeat; } - - -/* Attachment - ========================================================================== */ - -.uk-background-fixed { background-attachment: fixed; } - -/* - * Exclude touch devices because `fixed` doesn't work on iOS and Android - */ - -@media (pointer: coarse) { - .uk-background-fixed { background-attachment: scroll; } -} - - -/* Image - ========================================================================== */ - -/* Phone portrait and smaller */ -@media (max-width: $breakpoint-xsmall-max) { - - .uk-background-image\@s { background-image: none !important; } - -} - -/* Phone landscape and smaller */ -@media (max-width: $breakpoint-small-max) { - - .uk-background-image\@m { background-image: none !important; } - -} - -/* Tablet landscape and smaller */ -@media (max-width: $breakpoint-medium-max) { - - .uk-background-image\@l { background-image: none !important; } - -} - -/* Desktop and smaller */ -@media (max-width: $breakpoint-large-max) { - - .uk-background-image\@xl {background-image: none !important; } - -} - - -/* Blend modes - ========================================================================== */ - -.uk-background-blend-multiply { background-blend-mode: multiply; } -.uk-background-blend-screen { background-blend-mode: screen; } -.uk-background-blend-overlay { background-blend-mode: overlay; } -.uk-background-blend-darken { background-blend-mode: darken; } -.uk-background-blend-lighten { background-blend-mode: lighten; } -.uk-background-blend-color-dodge { background-blend-mode: color-dodge; } -.uk-background-blend-color-burn { background-blend-mode: color-burn; } -.uk-background-blend-hard-light { background-blend-mode: hard-light; } -.uk-background-blend-soft-light { background-blend-mode: soft-light; } -.uk-background-blend-difference { background-blend-mode: difference; } -.uk-background-blend-exclusion { background-blend-mode: exclusion; } -.uk-background-blend-hue { background-blend-mode: hue; } -.uk-background-blend-saturation { background-blend-mode: saturation; } -.uk-background-blend-color { background-blend-mode: color; } -.uk-background-blend-luminosity { background-blend-mode: luminosity; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-background-misc)) {@include hook-background-misc();} - -// @mixin hook-background-misc(){} diff --git a/_sass/uikit/components/badge.scss b/_sass/uikit/components/badge.scss deleted file mode 100644 index d076c5d06b..0000000000 --- a/_sass/uikit/components/badge.scss +++ /dev/null @@ -1,83 +0,0 @@ -// Name: Badge -// Description: Component to create notification badges - -// Component: `uk-badge` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$badge-size: 22px !default; -$badge-padding-vertical: 0 !default; -$badge-padding-horizontal: 5px !default; -$badge-border-radius: 500px !default; -$badge-background: $global-primary-background !default; -$badge-color: $global-inverse-color !default; -$badge-font-size: $global-small-font-size !default; - -$badge-hover-color: $global-inverse-color !default; - - -/* ======================================================================== - Component: Badge - ========================================================================== */ - -/* - * 1. Style - * 2. Center child vertically and horizontally - */ - -.uk-badge { - box-sizing: border-box; - min-width: $badge-size; - height: $badge-size; - padding: $badge-padding-vertical $badge-padding-horizontal; - border-radius: $badge-border-radius; - vertical-align: middle; - /* 1 */ - background: $badge-background; - color: $badge-color; - font-size: $badge-font-size; - /* 2 */ - display: inline-flex; - justify-content: center; - align-items: center; - @if(mixin-exists(hook-badge)) {@include hook-badge();} -} - -/* - * Required for `a` - */ - -.uk-badge:hover, -.uk-badge:focus { - color: $badge-hover-color; - text-decoration: none; - outline: none; - @if(mixin-exists(hook-badge-hover)) {@include hook-badge-hover();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-badge-misc)) {@include hook-badge-misc();} - -// @mixin hook-badge(){} -// @mixin hook-badge-hover(){} -// @mixin hook-badge-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-badge-background: $inverse-global-primary-background !default; -$inverse-badge-color: $inverse-global-inverse-color !default; -$inverse-badge-hover-color: $inverse-global-inverse-color !default; - - - -// @mixin hook-inverse-badge(){} -// @mixin hook-inverse-badge-hover(){} \ No newline at end of file diff --git a/_sass/uikit/components/base.scss b/_sass/uikit/components/base.scss deleted file mode 100644 index 138ecbb404..0000000000 --- a/_sass/uikit/components/base.scss +++ /dev/null @@ -1,612 +0,0 @@ -// Name: Base -// Description: Default values for HTML elements -// -// Component: `uk-link` -// `uk-h1`, `uk-h2`, `uk-h3`, `uk-h4`, `uk-h5`, `uk-h6` -// `uk-hr` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$base-body-background: $global-background !default; -$base-body-font-family: $global-font-family !default; -$base-body-font-weight: normal !default; -$base-body-font-size: $global-font-size !default; -$base-body-line-height: $global-line-height !default; -$base-body-color: $global-color !default; - -$base-link-color: $global-link-color !default; -$base-link-text-decoration: none !default; -$base-link-hover-color: $global-link-hover-color !default; -$base-link-hover-text-decoration: underline !default; - -$base-strong-font-weight: bolder !default; -$base-code-font-size: $global-small-font-size !default; -$base-code-font-family: Consolas, monaco, monospace !default; -$base-code-color: $global-danger-background !default; -$base-em-color: $global-danger-background !default; -$base-ins-background: #ffd !default; -$base-ins-color: $global-color !default; -$base-mark-background: #ffd !default; -$base-mark-color: $global-color !default; -$base-quote-font-style: italic !default; -$base-small-font-size: 80% !default; - -$base-margin-vertical: $global-margin !default; - -$base-heading-font-family: $global-font-family !default; -$base-heading-font-weight: normal !default; -$base-heading-color: $global-emphasis-color !default; -$base-heading-text-transform: none !default; -$base-heading-margin-top: $global-medium-margin !default; -$base-h1-font-size: $global-xxlarge-font-size !default; -$base-h1-line-height: 1.2 !default; -$base-h2-font-size: $global-xlarge-font-size !default; -$base-h2-line-height: 1.3 !default; -$base-h3-font-size: $global-large-font-size !default; -$base-h3-line-height: 1.4 !default; -$base-h4-font-size: $global-medium-font-size !default; -$base-h4-line-height: 1.4 !default; -$base-h5-font-size: $global-font-size !default; -$base-h5-line-height: 1.4 !default; -$base-h6-font-size: $global-small-font-size !default; -$base-h6-line-height: 1.4 !default; - -$base-list-padding-left: 30px !default; - -$base-hr-margin-vertical: $global-margin !default; -$base-hr-border-width: $global-border-width !default; -$base-hr-border: $global-border !default; - -$base-blockquote-font-size: $global-medium-font-size !default; -$base-blockquote-line-height: 1.5 !default; -$base-blockquote-font-style: italic !default; -$base-blockquote-margin-vertical: $global-margin !default; -$base-blockquote-footer-margin-top: $global-small-margin !default; -$base-blockquote-footer-font-size: $global-small-font-size !default; -$base-blockquote-footer-line-height: 1.5 !default; - -$base-pre-font-size: $global-small-font-size !default; -$base-pre-line-height: 1.5 !default; -$base-pre-font-family: $base-code-font-family !default; -$base-pre-color: $global-color !default; - -$base-selection-background: #39f !default; -$base-selection-color: $global-inverse-color !default; - - -/* ======================================================================== - Component: Base - ========================================================================== */ - -/* - * 1. Set `font-size` to support `rem` units - * Not using `font` property because a leading hyphen (e.g. -apple-system) causes the font to break in IE11 and Edge - * 2. Prevent adjustments of font size after orientation changes in iOS. - * 3. Style - */ - -html { - /* 1 */ - font-family: $base-body-font-family; - font-size: $base-body-font-size; - font-weight: $base-body-font-weight; - line-height: $base-body-line-height; - /* 2 */ - -webkit-text-size-adjust: 100%; - /* 3 */ - background: $base-body-background; - color: $base-body-color; - @if(mixin-exists(hook-base-body)) {@include hook-base-body();} -} - -/* - * Remove the margin in all browsers. - */ - -body { margin: 0; } - - -/* Links - ========================================================================== */ - -/* - * Remove gaps in links underline in iOS 8+ and Safari 8+. - */ - -a { -webkit-text-decoration-skip: objects; } - -/* - * Remove the outline on focused links when they are also active or hovered - */ - -a:active, -a:hover { outline: none; } - -/* - * Style - */ - -a, -.uk-link { - color: $base-link-color; - text-decoration: $base-link-text-decoration; - cursor: pointer; - @if(mixin-exists(hook-base-link)) {@include hook-base-link();} -} - -a:hover, -.uk-link:hover { - color: $base-link-hover-color; - text-decoration: $base-link-hover-text-decoration; - @if(mixin-exists(hook-base-link-hover)) {@include hook-base-link-hover();} -} - - -/* Text-level semantics - ========================================================================== */ - -/* - * 1. Remove the bottom border in Chrome 57-. - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ - -abbr[title] { - /* 1 */ - border-bottom: none; - /* 2 */ - text-decoration: underline; - text-decoration: underline dotted; -} - - -/* - * Add the correct font weight in Chrome, Edge, and Safari. - */ - -b, -strong { font-weight: $base-strong-font-weight; } - -/* - * 1. Consolas has a better baseline in running text compared to `Courier` - * 2. Correct the odd `em` font sizing in all browsers. - * 3. Style - */ - -:not(pre) > code, -:not(pre) > kbd, -:not(pre) > samp { - /* 1 */ - font-family: $base-code-font-family; - /* 2 */ - font-size: $base-code-font-size; - /* 3 */ - color: $base-code-color; - white-space: nowrap; - @if(mixin-exists(hook-base-code)) {@include hook-base-code();} -} - -/* - * Emphasize - */ - -em { color: $base-em-color; } - -/* - * Insert - */ - -ins { - background: $base-ins-background; - color: $base-ins-color; - text-decoration: none; -} - -/* - * Mark - */ - -mark { - background: $base-mark-background; - color: $base-mark-color; -} - -/* - * Quote - */ - -q { font-style: $base-quote-font-style; } - -/* - * Add the correct font size in all browsers. - */ - -small { font-size: $base-small-font-size; } - -/* - * Prevents `sub` and `sup` affecting `line-height` in all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sup { top: -0.5em; } -sub { bottom: -0.25em; } - - -/* Embedded content - ========================================================================== */ - -/* - * Remove the gap between embedded content and the bottom of their containers. - */ - -audio, -canvas, -iframe, -img, -svg, -video { vertical-align: middle; } - -/* - * Responsiveness - * 1. Set a maximum width - * 2. Auto scale the height. Only needed if `height` attribute is present - * 2. Corrects `max-width` behavior if padding and border are used - */ - -audio, -canvas, -img, -video { - /* 1 */ - max-width: 100%; - /* 2 */ - height: auto; - /* 3 */ - box-sizing: border-box; -} - -/* - * Hide the overflow in IE. - */ - -svg:not(:root) { overflow: hidden; } - - -/* Block elements - ========================================================================== */ - -/* - * Margins - */ - -p, -ul, -ol, -dl, -pre, -address, -fieldset, -figure { margin: 0 0 $base-margin-vertical 0; } - -/* Add margin if adjacent element */ -* + p, -* + ul, -* + ol, -* + dl, -* + pre, -* + address, -* + fieldset, -* + figure { margin-top: $base-margin-vertical; } - - -/* Headings - ========================================================================== */ - -h1, .uk-h1, -h2, .uk-h2, -h3, .uk-h3, -h4, .uk-h4, -h5, .uk-h5, -h6, .uk-h6 { - margin: 0 0 $base-margin-vertical 0; - font-family: $base-heading-font-family; - font-weight: $base-heading-font-weight; - color: $base-heading-color; - text-transform: $base-heading-text-transform; - @if(mixin-exists(hook-base-heading)) {@include hook-base-heading();} -} - -/* Add margin if adjacent element */ -* + h1, * + .uk-h1, -* + h2, * + .uk-h2, -* + h3, * + .uk-h3, -* + h4, * + .uk-h4, -* + h5, * + .uk-h5, -* + h6, * + .uk-h6 { margin-top: $base-heading-margin-top; } - -/* - * Sizes - */ - -h1, .uk-h1 { - font-size: $base-h1-font-size; - line-height: $base-h1-line-height; - @if(mixin-exists(hook-base-h1)) {@include hook-base-h1();} -} - -h2, .uk-h2 { - font-size: $base-h2-font-size; - line-height: $base-h2-line-height; - @if(mixin-exists(hook-base-h2)) {@include hook-base-h2();} -} - -h3, .uk-h3 { - font-size: $base-h3-font-size; - line-height: $base-h3-line-height; - @if(mixin-exists(hook-base-h3)) {@include hook-base-h3();} -} - -h4, .uk-h4 { - font-size: $base-h4-font-size; - line-height: $base-h4-line-height; - @if(mixin-exists(hook-base-h4)) {@include hook-base-h4();} -} - -h5, .uk-h5 { - font-size: $base-h5-font-size; - line-height: $base-h5-line-height; - @if(mixin-exists(hook-base-h5)) {@include hook-base-h5();} -} - -h6, .uk-h6 { - font-size: $base-h6-font-size; - line-height: $base-h6-line-height; - @if(mixin-exists(hook-base-h6)) {@include hook-base-h6();} -} - - -/* Lists - ========================================================================== */ - -ul, -ol { padding-left: $base-list-padding-left; } - -/* - * Reset margin for nested lists - */ - -ul > li > ul, -ul > li > ol, -ol > li > ol, -ol > li > ul { margin: 0; } - - -/* Description lists - ========================================================================== */ - -dt { font-weight: bold; } -dd { margin-left: 0; } - - -/* Horizontal rules - ========================================================================== */ - -/* - * 1. Add the correct box sizing and height in Firefox. - * 2. Show the overflow in Edge and IE. - * 3. Add the correct text-align in Edge and IE. - * 4. Style - */ - -hr, .uk-hr { - /* 1 */ - box-sizing: content-box; - height: 0; - /* 2 */ - overflow: visible; - /* 3 */ - text-align: inherit; - /* 4 */ - margin: 0 0 $base-hr-margin-vertical 0; - border: 0; - border-top: $base-hr-border-width solid $base-hr-border; - @if(mixin-exists(hook-base-hr)) {@include hook-base-hr();} -} - -/* Add margin if adjacent element */ -* + hr, -* + .uk-hr { margin-top: $base-hr-margin-vertical } - - -/* Address - ========================================================================== */ - -address { font-style: normal; } - - -/* Blockquotes - ========================================================================== */ - -blockquote { - margin: 0 0 $base-blockquote-margin-vertical 0; - font-size: $base-blockquote-font-size; - line-height: $base-blockquote-line-height; - font-style: $base-blockquote-font-style; - @if(mixin-exists(hook-base-blockquote)) {@include hook-base-blockquote();} -} - -/* Add margin if adjacent element */ -* + blockquote { margin-top: $base-blockquote-margin-vertical; } - -/* - * Content - */ - -blockquote p:last-of-type { margin-bottom: 0; } - -blockquote footer { - margin-top: $base-blockquote-footer-margin-top; - font-size: $base-blockquote-footer-font-size; - line-height: $base-blockquote-footer-line-height; - @if(mixin-exists(hook-base-blockquote-footer)) {@include hook-base-blockquote-footer();} -} - - -/* Preformatted text - ========================================================================== */ - -/* - * 1. Contain overflow in all browsers. - */ - -pre { - font: $base-pre-font-size unquote("/") $base-pre-line-height $base-pre-font-family; - color: $base-pre-color; - -moz-tab-size: 4; - tab-size: 4; - /* 1 */ - overflow: auto; - @if(mixin-exists(hook-base-pre)) {@include hook-base-pre();} -} - -pre code { font-family: $base-pre-font-family; } - - -/* Selection pseudo-element - ========================================================================== */ - -::-moz-selection { - background: $base-selection-background; - color: $base-selection-color; - text-shadow: none; -} - -::selection { - background: $base-selection-background; - color: $base-selection-color; - text-shadow: none; -} - - -/* HTML5 elements - ========================================================================== */ - -/* - * 1. Add the correct display in Edge, IE 10+, and Firefox. - * 2. Add the correct display in IE. - */ - -details, /* 1 */ -main { /* 2 */ - display: block; -} - -/* - * Add the correct display in all browsers. - */ - -summary { display: list-item; } - -/* - * Add the correct display in IE. - */ - -template { display: none; } - - -/* Iframe - ========================================================================== */ - -iframe { border: 0; } - - -/* Prevent the 300ms delay for touchscreen interactions - ========================================================================== */ - -/* - * Most browsers prevent the 300ms delay automatically for sites that use the `width=device-width` property. - * For Safari on iOS 9.3+, IE 11 and Edge on desktops and IE 11 on Windows Phone 8.1 it must be applied manually. - */ - -a, -area, -button, -input, -label, -select, -summary, -textarea { touch-action: manipulation; } - - -/* Pass media breakpoints to JS - ========================================================================== */ - -/* - * Breakpoints - */ - -.var-media-s::before { content: '#{$breakpoint-small}'; } -.var-media-m::before { content: '#{$breakpoint-medium}'; } -.var-media-l::before { content: '#{$breakpoint-large}'; } -.var-media-xl::before { content: '#{$breakpoint-xlarge}'; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-base-misc)) {@include hook-base-misc();} - -// @mixin hook-base-body(){} -// @mixin hook-base-link(){} -// @mixin hook-base-link-hover(){} -// @mixin hook-base-code(){} -// @mixin hook-base-heading(){} -// @mixin hook-base-h1(){} -// @mixin hook-base-h2(){} -// @mixin hook-base-h3(){} -// @mixin hook-base-h4(){} -// @mixin hook-base-h5(){} -// @mixin hook-base-h6(){} -// @mixin hook-base-hr(){} -// @mixin hook-base-blockquote(){} -// @mixin hook-base-blockquote-footer(){} -// @mixin hook-base-pre(){} -// @mixin hook-base-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-base-color: $inverse-global-color !default; -$inverse-base-link-color: $inverse-global-emphasis-color !default; -$inverse-base-link-hover-color: $inverse-global-emphasis-color !default; -$inverse-base-code-color: $inverse-global-color !default; -$inverse-base-em-color: $inverse-global-emphasis-color !default; -$inverse-base-heading-color: $inverse-global-emphasis-color !default; -$inverse-base-hr-border: $inverse-global-border !default; - - - -// @mixin hook-inverse-base-link(){} -// @mixin hook-inverse-base-link-hover(){} -// @mixin hook-inverse-base-code(){} -// @mixin hook-inverse-base-heading(){} -// @mixin hook-inverse-base-h1(){} -// @mixin hook-inverse-base-h2(){} -// @mixin hook-inverse-base-h3(){} -// @mixin hook-inverse-base-h4(){} -// @mixin hook-inverse-base-h5(){} -// @mixin hook-inverse-base-h6(){} -// @mixin hook-inverse-base-blockquote(){} -// @mixin hook-inverse-base-blockquote-footer(){} -// @mixin hook-inverse-base-hr(){} diff --git a/_sass/uikit/components/breadcrumb.scss b/_sass/uikit/components/breadcrumb.scss deleted file mode 100644 index 1f4ca50238..0000000000 --- a/_sass/uikit/components/breadcrumb.scss +++ /dev/null @@ -1,122 +0,0 @@ -// Name: Breadcrumb -// Description: Component to create a breadcrumb navigation -// -// Component: `uk-breadcrumb` -// -// States: `uk-disabled` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$breadcrumb-item-font-size: $global-small-font-size !default; -$breadcrumb-item-color: $global-muted-color !default; -$breadcrumb-item-hover-color: $global-color !default; -$breadcrumb-item-hover-text-decoration: none !default; -$breadcrumb-item-active-color: $global-color !default; - -$breadcrumb-divider: "/" !default; -$breadcrumb-divider-margin-horizontal: 20px !default; -$breadcrumb-divider-color: $global-muted-color !default; - - -/* ======================================================================== - Component: Breadcrumb - ========================================================================== */ - -/* - * 1. Allow items to wrap into the next line - * 2. Reset list - */ - -.uk-breadcrumb { - display: flex; - /* 1 */ - flex-wrap: wrap; - /* 2 */ - padding: 0; - list-style: none; - @if(mixin-exists(hook-breadcrumb)) {@include hook-breadcrumb();} -} - -/* - * Space is allocated solely based on content dimensions: 0 0 auto - */ - -.uk-breadcrumb > * { flex: none; } - - -/* Items - ========================================================================== */ - -.uk-breadcrumb > * > * { - display: inline-block; - font-size: $breadcrumb-item-font-size; - color: $breadcrumb-item-color; - @if(mixin-exists(hook-breadcrumb-item)) {@include hook-breadcrumb-item();} -} - -/* Hover + Focus */ -.uk-breadcrumb > * > :hover, -.uk-breadcrumb > * > :focus { - color: $breadcrumb-item-hover-color; - text-decoration: $breadcrumb-item-hover-text-decoration; - @if(mixin-exists(hook-breadcrumb-item-hover)) {@include hook-breadcrumb-item-hover();} -} - -/* Disabled */ -.uk-breadcrumb > .uk-disabled > * { - @if(mixin-exists(hook-breadcrumb-item-disabled)) {@include hook-breadcrumb-item-disabled();} -} - -/* Active */ -.uk-breadcrumb > :last-child > * { - color: $breadcrumb-item-active-color; - @if(mixin-exists(hook-breadcrumb-item-active)) {@include hook-breadcrumb-item-active();} -} - -/* - * Divider - * `nth-child` makes it also work without JS if it's only one row - */ - -.uk-breadcrumb > :nth-child(n+2):not(.uk-first-column)::before { - content: $breadcrumb-divider; - display: inline-block; - margin: 0 $breadcrumb-divider-margin-horizontal; - color: $breadcrumb-divider-color; - @if(mixin-exists(hook-breadcrumb-divider)) {@include hook-breadcrumb-divider();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-breadcrumb-misc)) {@include hook-breadcrumb-misc();} - -// @mixin hook-breadcrumb(){} -// @mixin hook-breadcrumb-item(){} -// @mixin hook-breadcrumb-item-hover(){} -// @mixin hook-breadcrumb-item-disabled(){} -// @mixin hook-breadcrumb-item-active(){} -// @mixin hook-breadcrumb-divider(){} -// @mixin hook-breadcrumb-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-breadcrumb-item-color: $inverse-global-muted-color !default; -$inverse-breadcrumb-item-hover-color: $inverse-global-color !default; -$inverse-breadcrumb-item-active-color: $inverse-global-color !default; -$inverse-breadcrumb-divider-color: $inverse-global-muted-color !default; - - - -// @mixin hook-inverse-breadcrumb-item(){} -// @mixin hook-inverse-breadcrumb-item-hover(){} -// @mixin hook-inverse-breadcrumb-item-disabled(){} -// @mixin hook-inverse-breadcrumb-item-active(){} -// @mixin hook-inverse-breadcrumb-divider(){} diff --git a/_sass/uikit/components/button.scss b/_sass/uikit/components/button.scss deleted file mode 100644 index f65e1c2230..0000000000 --- a/_sass/uikit/components/button.scss +++ /dev/null @@ -1,451 +0,0 @@ -// Name: Button -// Description: Styles for buttons -// -// Component: `uk-button` -// -// Sub-objects: `uk-button-group` -// -// Modifiers: `uk-button-default` -// `uk-button-primary` -// `uk-button-secondary` -// `uk-button-danger` -// `uk-button-text` -// `uk-button-link` -// `uk-button-small` -// `uk-button-large` -// -// States: `uk-active` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$button-line-height: $global-control-height !default; -$button-small-line-height: $global-control-small-height !default; -$button-large-line-height: $global-control-large-height !default; - -$button-font-size: $global-font-size !default; -$button-small-font-size: $global-small-font-size !default; -$button-large-font-size: $global-medium-font-size !default; - -$button-padding-horizontal: $global-gutter !default; -$button-small-padding-horizontal: $global-small-gutter !default; -$button-large-padding-horizontal: $global-medium-gutter !default; - -$button-default-background: $global-muted-background !default; -$button-default-color: $global-emphasis-color !default; -$button-default-hover-background: darken($button-default-background, 5%) !default; -$button-default-hover-color: $global-emphasis-color !default; -$button-default-active-background: darken($button-default-background, 10%) !default; -$button-default-active-color: $global-emphasis-color !default; - -$button-primary-background: $global-primary-background !default; -$button-primary-color: $global-inverse-color !default; -$button-primary-hover-background: darken($button-primary-background, 5%) !default; -$button-primary-hover-color: $global-inverse-color !default; -$button-primary-active-background: darken($button-primary-background, 10%) !default; -$button-primary-active-color: $global-inverse-color !default; - -$button-secondary-background: $global-secondary-background !default; -$button-secondary-color: $global-inverse-color !default; -$button-secondary-hover-background: darken($button-secondary-background, 5%) !default; -$button-secondary-hover-color: $global-inverse-color !default; -$button-secondary-active-background: darken($button-secondary-background, 10%) !default; -$button-secondary-active-color: $global-inverse-color !default; - -$button-danger-background: $global-danger-background !default; -$button-danger-color: $global-inverse-color !default; -$button-danger-hover-background: darken($button-danger-background, 5%) !default; -$button-danger-hover-color: $global-inverse-color !default; -$button-danger-active-background: darken($button-danger-background, 10%) !default; -$button-danger-active-color: $global-inverse-color !default; - -$button-disabled-background: $global-muted-background !default; -$button-disabled-color: $global-muted-color !default; - -$button-text-line-height: $global-line-height !default; -$button-text-color: $global-muted-color !default; -$button-text-hover-color: $global-color !default; -$button-text-disabled-color: $global-muted-color !default; - -$button-link-line-height: $global-line-height !default; -$button-link-color: $global-link-color !default; -$button-link-hover-color: $global-link-hover-color !default; -$button-link-hover-text-decoration: underline !default; -$button-link-disabled-color: $global-muted-color !default; - - -/* ======================================================================== - Component: Button - ========================================================================== */ - -/* - * 1. Remove margins in Chrome, Safari and Opera. - * 2. Remove borders for `button`. - * 3. Remove border-radius in Chrome. - * 4. Address `overflow` set to `hidden` in IE. - * 5. Correct `font` properties and `color` not being inherited for `button`. - * 6. Remove the inheritance of text transform in Edge, Firefox, and IE. - * 7. Style - * 8. `line-height` is used to create a height because it also centers the text vertically for `a` elements. - * Better would be to use height and flexbox to center the text vertically but flexbox doesn't work in Firefox on `button` elements. - * 9. Align text if button has a width - * 10. Required for `a`. - */ - -.uk-button { - /* 1 */ - margin: 0; - /* 2 */ - border: none; - /* 3 */ - border-radius: 0; - /* 4 */ - overflow: visible; - /* 5 */ - font: inherit; - color: inherit; - /* 6 */ - text-transform: none; - /* 7 */ - display: inline-block; - box-sizing: border-box; - padding: 0 $button-padding-horizontal; - vertical-align: middle; - font-size: $button-font-size; - /* 8 */ - line-height: $button-line-height; - /* 9 */ - text-align: center; - /* 10 */ - text-decoration: none; - @if(mixin-exists(hook-button)) {@include hook-button();} -} - -.uk-button:not(:disabled) { cursor: pointer; } - -/* - * Remove the inner border and padding in Firefox. - */ - -.uk-button::-moz-focus-inner { - border: 0; - padding: 0; -} - -/* Hover */ -.uk-button:hover { - /* 8 */ - text-decoration: none; - @if(mixin-exists(hook-button-hover)) {@include hook-button-hover();} -} - -/* Focus */ -.uk-button:focus { - outline: none; - @if(mixin-exists(hook-button-focus)) {@include hook-button-focus();} -} - -/* OnClick + Active */ -.uk-button:active, -.uk-button.uk-active { - @if(mixin-exists(hook-button-active)) {@include hook-button-active();} -} - - -/* Style modifiers - ========================================================================== */ - -/* - * Default - */ - -.uk-button-default { - background-color: $button-default-background; - color: $button-default-color; - @if(mixin-exists(hook-button-default)) {@include hook-button-default();} -} - -/* Hover + Focus */ -.uk-button-default:hover, -.uk-button-default:focus { - background-color: $button-default-hover-background; - color: $button-default-hover-color; - @if(mixin-exists(hook-button-default-hover)) {@include hook-button-default-hover();} -} - -/* OnClick + Active */ -.uk-button-default:active, -.uk-button-default.uk-active { - background-color: $button-default-active-background; - color: $button-default-active-color; - @if(mixin-exists(hook-button-default-active)) {@include hook-button-default-active();} -} - -/* - * Primary - */ - -.uk-button-primary { - background-color: $button-primary-background; - color: $button-primary-color; - @if(mixin-exists(hook-button-primary)) {@include hook-button-primary();} -} - -/* Hover + Focus */ -.uk-button-primary:hover, -.uk-button-primary:focus { - background-color: $button-primary-hover-background; - color: $button-primary-hover-color; - @if(mixin-exists(hook-button-primary-hover)) {@include hook-button-primary-hover();} -} - -/* OnClick + Active */ -.uk-button-primary:active, -.uk-button-primary.uk-active { - background-color: $button-primary-active-background; - color: $button-primary-active-color; - @if(mixin-exists(hook-button-primary-active)) {@include hook-button-primary-active();} -} - -/* - * Secondary - */ - -.uk-button-secondary { - background-color: $button-secondary-background; - color: $button-secondary-color; - @if(mixin-exists(hook-button-secondary)) {@include hook-button-secondary();} -} - -/* Hover + Focus */ -.uk-button-secondary:hover, -.uk-button-secondary:focus { - background-color: $button-secondary-hover-background; - color: $button-secondary-hover-color; - @if(mixin-exists(hook-button-secondary-hover)) {@include hook-button-secondary-hover();} -} - -/* OnClick + Active */ -.uk-button-secondary:active, -.uk-button-secondary.uk-active { - background-color: $button-secondary-active-background; - color: $button-secondary-active-color; - @if(mixin-exists(hook-button-secondary-active)) {@include hook-button-secondary-active();} -} - -/* - * Danger - */ - -.uk-button-danger { - background-color: $button-danger-background; - color: $button-danger-color; - @if(mixin-exists(hook-button-danger)) {@include hook-button-danger();} -} - -/* Hover + Focus */ -.uk-button-danger:hover, -.uk-button-danger:focus { - background-color: $button-danger-hover-background; - color: $button-danger-hover-color; - @if(mixin-exists(hook-button-danger-hover)) {@include hook-button-danger-hover();} -} - -/* OnClick + Active */ -.uk-button-danger:active, -.uk-button-danger.uk-active { - background-color: $button-danger-active-background; - color: $button-danger-active-color; - @if(mixin-exists(hook-button-danger-active)) {@include hook-button-danger-active();} -} - -/* - * Disabled - * The same for all style modifiers - */ - -.uk-button-default:disabled, -.uk-button-primary:disabled, -.uk-button-secondary:disabled, -.uk-button-danger:disabled { - background-color: $button-disabled-background; - color: $button-disabled-color; - @if(mixin-exists(hook-button-disabled)) {@include hook-button-disabled();} -} - - -/* Size modifiers - ========================================================================== */ - -.uk-button-small { - padding: 0 $button-small-padding-horizontal; - line-height: $button-small-line-height; - font-size: $button-small-font-size; - @if(mixin-exists(hook-button-small)) {@include hook-button-small();} -} - -.uk-button-large { - padding: 0 $button-large-padding-horizontal; - line-height: $button-large-line-height; - font-size: $button-large-font-size; - @if(mixin-exists(hook-button-large)) {@include hook-button-large();} -} - - -/* Text modifiers - ========================================================================== */ - -/* - * Text - * 1. Reset - * 2. Style - */ - -.uk-button-text { - /* 1 */ - padding: 0; - line-height: $button-text-line-height; - background: none; - /* 2 */ - color: $button-text-color; - @if(mixin-exists(hook-button-text)) {@include hook-button-text();} -} - -/* Hover + Focus */ -.uk-button-text:hover, -.uk-button-text:focus { - color: $button-text-hover-color; - @if(mixin-exists(hook-button-text-hover)) {@include hook-button-text-hover();} -} - -/* Disabled */ -.uk-button-text:disabled { - color: $button-text-disabled-color; - @if(mixin-exists(hook-button-text-disabled)) {@include hook-button-text-disabled();} -} - -/* - * Link - * 1. Reset - * 2. Style - */ - -.uk-button-link { - /* 1 */ - padding: 0; - line-height: $button-link-line-height; - background: none; - /* 2 */ - color: $button-link-color; - @if(mixin-exists(hook-button-link)) {@include hook-button-link();} -} - -/* Hover + Focus */ -.uk-button-link:hover, -.uk-button-link:focus { - color: $button-link-hover-color; - text-decoration: $button-link-hover-text-decoration; -} - -/* Disabled */ -.uk-button-link:disabled { - color: $button-link-disabled-color; - text-decoration: none; -} - - -/* Group - ========================================================================== */ - -/* - * 1. Using `flex` instead of `inline-block` to prevent whitespace betweent child elements - * 2. Behave like button - * 3. Create position context - */ - -.uk-button-group { - /* 1 */ - display: inline-flex; - /* 2 */ - vertical-align: middle; - /* 3 */ - position: relative; -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-button-misc)) {@include hook-button-misc();} - -// @mixin hook-button(){} -// @mixin hook-button-hover(){} -// @mixin hook-button-focus(){} -// @mixin hook-button-active(){} -// @mixin hook-button-default(){} -// @mixin hook-button-default-hover(){} -// @mixin hook-button-default-active(){} -// @mixin hook-button-primary(){} -// @mixin hook-button-primary-hover(){} -// @mixin hook-button-primary-active(){} -// @mixin hook-button-secondary(){} -// @mixin hook-button-secondary-hover(){} -// @mixin hook-button-secondary-active(){} -// @mixin hook-button-danger(){} -// @mixin hook-button-danger-hover(){} -// @mixin hook-button-danger-active(){} -// @mixin hook-button-disabled(){} -// @mixin hook-button-small(){} -// @mixin hook-button-large(){} -// @mixin hook-button-text(){} -// @mixin hook-button-text-hover(){} -// @mixin hook-button-text-disabled(){} -// @mixin hook-button-link(){} -// @mixin hook-button-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-button-default-background: $inverse-global-primary-background !default; -$inverse-button-default-color: $inverse-global-inverse-color !default; -$inverse-button-default-hover-background: darken($inverse-button-default-background, 5%) !default; -$inverse-button-default-hover-color: $inverse-global-inverse-color !default; -$inverse-button-default-active-background: darken($inverse-button-default-background, 10%) !default; -$inverse-button-default-active-color: $inverse-global-inverse-color !default; -$inverse-button-primary-background: $inverse-global-primary-background !default; -$inverse-button-primary-color: $inverse-global-inverse-color !default; -$inverse-button-primary-hover-background: darken($inverse-button-primary-background, 5%) !default; -$inverse-button-primary-hover-color: $inverse-global-inverse-color !default; -$inverse-button-primary-active-background: darken($inverse-button-primary-background, 10%) !default; -$inverse-button-primary-active-color: $inverse-global-inverse-color !default; -$inverse-button-secondary-background: $inverse-global-primary-background !default; -$inverse-button-secondary-color: $inverse-global-inverse-color !default; -$inverse-button-secondary-hover-background: darken($inverse-button-secondary-background, 5%) !default; -$inverse-button-secondary-hover-color: $inverse-global-inverse-color !default; -$inverse-button-secondary-active-background: darken($inverse-button-secondary-background, 10%) !default; -$inverse-button-secondary-active-color: $inverse-global-inverse-color !default; -$inverse-button-text-color: $inverse-global-muted-color !default; -$inverse-button-text-hover-color: $inverse-global-color !default; -$inverse-button-text-disabled-color: $inverse-global-muted-color !default; -$inverse-button-link-color: $inverse-global-muted-color !default; -$inverse-button-link-hover-color: $inverse-global-color !default; - - - -// @mixin hook-inverse-button-default(){} -// @mixin hook-inverse-button-default-hover(){} -// @mixin hook-inverse-button-default-active(){} -// @mixin hook-inverse-button-primary(){} -// @mixin hook-inverse-button-primary-hover(){} -// @mixin hook-inverse-button-primary-active(){} -// @mixin hook-inverse-button-secondary(){} -// @mixin hook-inverse-button-secondary-hover(){} -// @mixin hook-inverse-button-secondary-active(){} -// @mixin hook-inverse-button-text(){} -// @mixin hook-inverse-button-text-hover(){} -// @mixin hook-inverse-button-text-disabled(){} -// @mixin hook-inverse-button-link(){} diff --git a/_sass/uikit/components/card.scss b/_sass/uikit/components/card.scss deleted file mode 100644 index e332a03f54..0000000000 --- a/_sass/uikit/components/card.scss +++ /dev/null @@ -1,363 +0,0 @@ -// Name: Card -// Description: Component to create boxed content containers -// -// Component: `uk-card` -// -// Sub-objects: `uk-card-body` -// `uk-card-header` -// `uk-card-footer` -// `uk-card-media-*` -// `uk-card-title` -// `uk-card-badge` -// -// Modifiers: `uk-card-hover` -// `uk-card-default` -// `uk-card-primary` -// `uk-card-secondary` -// `uk-card-small` -// `uk-card-large` -// -// Uses: `uk-grid-stack` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$card-body-padding-horizontal: $global-gutter !default; -$card-body-padding-vertical: $global-gutter !default; - -$card-body-padding-horizontal-l: $global-medium-gutter !default; -$card-body-padding-vertical-l: $global-medium-gutter !default; - -$card-header-padding-horizontal: $global-gutter !default; -$card-header-padding-vertical: round($global-gutter / 2) !default; - -$card-header-padding-horizontal-l: $global-medium-gutter !default; -$card-header-padding-vertical-l: round($global-medium-gutter / 2) !default; - -$card-footer-padding-horizontal: $global-gutter !default; -$card-footer-padding-vertical: ($global-gutter / 2) !default; - -$card-footer-padding-horizontal-l: $global-medium-gutter !default; -$card-footer-padding-vertical-l: round($global-medium-gutter / 2) !default; - -$card-title-font-size: $global-large-font-size !default; -$card-title-line-height: 1.4 !default; - -$card-badge-top: $global-gutter !default; -$card-badge-right: $card-badge-top !default; - -$card-hover-background: $global-muted-background !default; - -$card-default-background: $global-muted-background !default; -$card-default-color: $global-color !default; -$card-default-title-color: $global-emphasis-color !default; -$card-default-hover-background: darken($card-default-background, 5%) !default; - -$card-primary-background: $global-primary-background !default; -$card-primary-color: $global-inverse-color !default; -$card-primary-title-color: $card-primary-color !default; -$card-primary-hover-background: darken($card-primary-background, 5%) !default; -$card-primary-color-mode: light !default; - -$card-secondary-background: $global-secondary-background !default; -$card-secondary-color: $global-inverse-color !default; -$card-secondary-title-color: $card-secondary-color !default; -$card-secondary-hover-background: darken($card-secondary-background, 5%) !default; -$card-secondary-color-mode: light !default; - -$card-small-body-padding-horizontal: $global-margin !default; -$card-small-body-padding-vertical: $global-margin !default; -$card-small-header-padding-horizontal: $global-margin !default; -$card-small-header-padding-vertical: round($global-margin / 1.5) !default; -$card-small-footer-padding-horizontal: $global-margin !default; -$card-small-footer-padding-vertical: round($global-margin / 1.5) !default; - -$card-large-body-padding-horizontal-l: $global-large-gutter !default; -$card-large-body-padding-vertical-l: $global-large-gutter !default; -$card-large-header-padding-horizontal-l: $global-large-gutter !default; -$card-large-header-padding-vertical-l: round($global-large-gutter / 2) !default; -$card-large-footer-padding-horizontal-l: $global-large-gutter !default; -$card-large-footer-padding-vertical-l: round($global-large-gutter / 2) !default; - - -/* ======================================================================== - Component: Card - ========================================================================== */ - -.uk-card { - position: relative; - box-sizing: border-box; - @if(mixin-exists(hook-card)) {@include hook-card();} -} - - -/* Sections - ========================================================================== */ - -.uk-card-body { - padding: $card-body-padding-vertical $card-body-padding-horizontal; - @if(mixin-exists(hook-card-body)) {@include hook-card-body();} -} - -.uk-card-header { - padding: $card-header-padding-vertical $card-header-padding-horizontal; - @if(mixin-exists(hook-card-header)) {@include hook-card-header();} -} - -.uk-card-footer { - padding: $card-footer-padding-vertical $card-footer-padding-horizontal; - @if(mixin-exists(hook-card-footer)) {@include hook-card-footer();} -} - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-card-body { padding: $card-body-padding-vertical-l $card-body-padding-horizontal-l; } - - .uk-card-header { padding: $card-header-padding-vertical-l $card-header-padding-horizontal-l; } - - .uk-card-footer { padding: $card-footer-padding-vertical-l $card-footer-padding-horizontal-l; } - -} - -/* - * Micro clearfix - */ - -.uk-card-body::before, -.uk-card-body::after, -.uk-card-header::before, -.uk-card-header::after, -.uk-card-footer::before, -.uk-card-footer::after { - content: ""; - display: table; -} - -.uk-card-body::after, -.uk-card-header::after, -.uk-card-footer::after { clear: both; } - -/* - * Remove margin from the last-child - */ - -.uk-card-body > :last-child, -.uk-card-header > :last-child, -.uk-card-footer > :last-child { margin-bottom: 0; } - - -/* Media - ========================================================================== */ - -/* - * Reserved alignment modifier to style the media element, e.g. with `border-radius` - * Implemented by the theme - */ - -[class*='uk-card-media'] { - @if(mixin-exists(hook-card-media)) {@include hook-card-media();} -} - -.uk-card-media-top, -.uk-grid-stack > .uk-card-media-left, -.uk-grid-stack > .uk-card-media-right { - @if(mixin-exists(hook-card-media-top)) {@include hook-card-media-top();} -} - -.uk-card-media-bottom { - @if(mixin-exists(hook-card-media-bottom)) {@include hook-card-media-bottom();} -} - -:not(.uk-grid-stack) > .uk-card-media-left { - @if(mixin-exists(hook-card-media-left)) {@include hook-card-media-left();} -} - -:not(.uk-grid-stack) > .uk-card-media-right { - @if(mixin-exists(hook-card-media-right)) {@include hook-card-media-right();} -} - - -/* Title - ========================================================================== */ - -.uk-card-title { - font-size: $card-title-font-size; - line-height: $card-title-line-height; - @if(mixin-exists(hook-card-title)) {@include hook-card-title();} -} - - -/* Badge - ========================================================================== */ - -.uk-card-badge { - position: absolute; - top: $card-badge-top; - right: $card-badge-right; - z-index: 1; - @if(mixin-exists(hook-card-badge)) {@include hook-card-badge();} -} - -/* - * Remove margin from adjacent element - */ - -.uk-card-badge:first-child + * { margin-top: 0; } - - -/* Hover modifier - ========================================================================== */ - -.uk-card-hover:not(.uk-card-default):not(.uk-card-primary):not(.uk-card-secondary):hover { - background: $card-hover-background; - @if(mixin-exists(hook-card-hover)) {@include hook-card-hover();} -} - - -/* Style modifiers - ========================================================================== */ - -/* - * Default - * Note: Header and Footer are only implemented for the default style - */ - -.uk-card-default { - background: $card-default-background; - color: $card-default-color; - @if(mixin-exists(hook-card-default)) {@include hook-card-default();} -} - -.uk-card-default .uk-card-title { - color: $card-default-title-color; - @if(mixin-exists(hook-card-default-title)) {@include hook-card-default-title();} -} - -.uk-card-default.uk-card-hover:hover { - background-color: $card-default-hover-background; - @if(mixin-exists(hook-card-default-hover)) {@include hook-card-default-hover();} -} - -.uk-card-default .uk-card-header { - @if(mixin-exists(hook-card-default-header)) {@include hook-card-default-header();} -} - -.uk-card-default .uk-card-footer { - @if(mixin-exists(hook-card-default-footer)) {@include hook-card-default-footer();} -} - -/* - * Primary - */ - -.uk-card-primary { - background: $card-primary-background; - color: $card-primary-color; - @if(mixin-exists(hook-card-primary)) {@include hook-card-primary();} -} - -.uk-card-primary .uk-card-title { - color: $card-primary-title-color; - @if(mixin-exists(hook-card-primary-title)) {@include hook-card-primary-title();} -} - -.uk-card-primary.uk-card-hover:hover { - background-color: $card-primary-hover-background; - @if(mixin-exists(hook-card-primary-hover)) {@include hook-card-primary-hover();} -} - -// Color Mode -@if ( $card-primary-color-mode == light ) { .uk-card-primary.uk-card-body { @extend .uk-light !optional;} } -@if ( $card-primary-color-mode == light ) { .uk-card-primary > :not([class*='uk-card-media']) { @extend .uk-light !optional;} } -@if ( $card-primary-color-mode == dark ) { .uk-card-primary.uk-card-body { @extend .uk-dark !optional;} } -@if ( $card-primary-color-mode == dark ) { .uk-card-primary > :not([class*='uk-card-media']) { @extend .uk-dark !optional;} } - -/* - * Secondary - */ - -.uk-card-secondary { - background: $card-secondary-background; - color: $card-secondary-color; - @if(mixin-exists(hook-card-secondary)) {@include hook-card-secondary();} -} - -.uk-card-secondary .uk-card-title { - color: $card-secondary-title-color; - @if(mixin-exists(hook-card-secondary-title)) {@include hook-card-secondary-title();} -} - -.uk-card-secondary.uk-card-hover:hover { - background-color: $card-secondary-hover-background; - @if(mixin-exists(hook-card-secondary-hover)) {@include hook-card-secondary-hover();} -} - -// Color Mode -@if ( $card-secondary-color-mode == light ) { .uk-card-secondary.uk-card-body { @extend .uk-light !optional;} } -@if ( $card-secondary-color-mode == light ) { .uk-card-secondary > :not([class*='uk-card-media']) { @extend .uk-light !optional;} } -@if ( $card-secondary-color-mode == dark ) { .uk-card-secondary.uk-card-body { @extend .uk-dark !optional;} } -@if ( $card-secondary-color-mode == dark ) { .uk-card-secondary > :not([class*='uk-card-media']) { @extend .uk-dark !optional;} } - - -/* Size modifier - ========================================================================== */ - -/* - * Small - */ - -.uk-card-small.uk-card-body, -.uk-card-small .uk-card-body { padding: $card-small-body-padding-vertical $card-small-body-padding-horizontal; } - -.uk-card-small .uk-card-header { padding: $card-small-header-padding-vertical $card-small-header-padding-horizontal; } -.uk-card-small .uk-card-footer { padding: $card-small-footer-padding-vertical $card-small-footer-padding-horizontal; } - -/* - * Large - */ - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-card-large.uk-card-body, - .uk-card-large .uk-card-body { padding: $card-large-body-padding-vertical-l $card-large-body-padding-horizontal-l; } - - .uk-card-large .uk-card-header { padding: $card-large-header-padding-vertical-l $card-large-header-padding-horizontal-l; } - .uk-card-large .uk-card-footer { padding: $card-large-footer-padding-vertical-l $card-large-footer-padding-horizontal-l; } - -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-card-misc)) {@include hook-card-misc();} - -// @mixin hook-card(){} -// @mixin hook-card-body(){} -// @mixin hook-card-header(){} -// @mixin hook-card-footer(){} -// @mixin hook-card-media(){} -// @mixin hook-card-media-top(){} -// @mixin hook-card-media-bottom(){} -// @mixin hook-card-media-left(){} -// @mixin hook-card-media-right(){} -// @mixin hook-card-title(){} -// @mixin hook-card-badge(){} -// @mixin hook-card-hover(){} -// @mixin hook-card-default(){} -// @mixin hook-card-default-title(){} -// @mixin hook-card-default-hover(){} -// @mixin hook-card-default-header(){} -// @mixin hook-card-default-footer(){} -// @mixin hook-card-primary(){} -// @mixin hook-card-primary-title(){} -// @mixin hook-card-primary-hover(){} -// @mixin hook-card-secondary(){} -// @mixin hook-card-secondary-title(){} -// @mixin hook-card-secondary-hover(){} -// @mixin hook-card-misc(){} diff --git a/_sass/uikit/components/close.scss b/_sass/uikit/components/close.scss deleted file mode 100644 index 32e2775642..0000000000 --- a/_sass/uikit/components/close.scss +++ /dev/null @@ -1,57 +0,0 @@ -// Name: Close -// Description: Component to create a close button -// -// Component: `uk-close` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$close-color: $global-muted-color !default; -$close-hover-color: $global-color !default; - - -/* ======================================================================== - Component: Close - ========================================================================== */ - -/* - * Adopts `uk-icon` - */ - -.uk-close { - color: $close-color; - @if(mixin-exists(hook-close)) {@include hook-close();} -} - -/* Hover + Focus */ -.uk-close:hover, -.uk-close:focus { - color: $close-hover-color; - outline: none; - @if(mixin-exists(hook-close-hover)) {@include hook-close-hover();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-close-misc)) {@include hook-close-misc();} - -// @mixin hook-close(){} -// @mixin hook-close-hover(){} -// @mixin hook-close-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-close-color: $inverse-global-muted-color !default; -$inverse-close-hover-color: $inverse-global-color !default; - - - -// @mixin hook-inverse-close(){} -// @mixin hook-inverse-close-hover(){} diff --git a/_sass/uikit/components/column.scss b/_sass/uikit/components/column.scss deleted file mode 100644 index 54bae26e37..0000000000 --- a/_sass/uikit/components/column.scss +++ /dev/null @@ -1,138 +0,0 @@ -// Name: Column -// Description: Utilities for text columns -// -// Component: `uk-column-*` -// -// Sub-objects: `uk-column-span` -// -// Modifiers: `uk-column-divider` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$column-gutter: $global-gutter !default; -$column-gutter-l: $global-medium-gutter !default; - -$column-divider-rule-color: $global-border !default; -$column-divider-rule-width: 1px !default; - - -/* ======================================================================== - Component: Column - ========================================================================== */ - -[class*='uk-column-'] { column-gap: $column-gutter; } - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - [class*='uk-column-'] { column-gap: $column-gutter-l; } - -} - -/* - * Fix image 1px line wrapping into the next column in Chrome - */ - -[class*='uk-column-'] img { transform: translate3d(0,0,0); } - - -/* Divider - ========================================================================== */ - -/* - * 1. Double the column gap - */ - -.uk-column-divider { - column-rule: $column-divider-rule-width solid $column-divider-rule-color; - /* 1 */ - column-gap: ($column-gutter * 2); -} - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-column-divider { - column-gap: ($column-gutter-l * 2); - } - -} - - -/* Width modifiers - ========================================================================== */ - -.uk-column-1-2 { column-count: 2;} -.uk-column-1-3 { column-count: 3; } -.uk-column-1-4 { column-count: 4; } -.uk-column-1-5 { column-count: 5; } -.uk-column-1-6 { column-count: 6; } - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - .uk-column-1-2\@s { column-count: 2; } - .uk-column-1-3\@s { column-count: 3; } - .uk-column-1-4\@s { column-count: 4; } - .uk-column-1-5\@s { column-count: 5; } - .uk-column-1-6\@s { column-count: 6; } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-column-1-2\@m { column-count: 2; } - .uk-column-1-3\@m { column-count: 3; } - .uk-column-1-4\@m { column-count: 4; } - .uk-column-1-5\@m { column-count: 5; } - .uk-column-1-6\@m { column-count: 6; } - -} - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-column-1-2\@l { column-count: 2; } - .uk-column-1-3\@l { column-count: 3; } - .uk-column-1-4\@l { column-count: 4; } - .uk-column-1-5\@l { column-count: 5; } - .uk-column-1-6\@l { column-count: 6; } - -} - -/* Large screen and bigger */ -@media (min-width: $breakpoint-xlarge) { - - .uk-column-1-2\@xl { column-count: 2; } - .uk-column-1-3\@xl { column-count: 3; } - .uk-column-1-4\@xl { column-count: 4; } - .uk-column-1-5\@xl { column-count: 5; } - .uk-column-1-6\@xl { column-count: 6; } - -} - -/* Make element span across all columns - * Does not work in Firefox yet - ========================================================================== */ - -.uk-column-span { column-span: all; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-column-misc)) {@include hook-column-misc();} - -// @mixin hook-column-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-column-divider-rule-color: $inverse-global-border !default; - diff --git a/_sass/uikit/components/comment.scss b/_sass/uikit/components/comment.scss deleted file mode 100644 index 8583dd8a0d..0000000000 --- a/_sass/uikit/components/comment.scss +++ /dev/null @@ -1,171 +0,0 @@ -// Name: Comment -// Description: Component to create nested comments -// -// Component: `uk-comment` -// -// Sub-objects: `uk-comment-body` -// `uk-comment-header` -// `uk-comment-title` -// `uk-comment-meta` -// `uk-comment-avatar` -// `uk-comment-list` -// -// Modifier: `uk-comment-primary` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$comment-header-margin-bottom: $global-margin !default; - -$comment-title-font-size: $global-medium-font-size !default; -$comment-title-line-height: 1.4 !default; - -$comment-meta-font-size: $global-small-font-size !default; -$comment-meta-line-height: 1.4 !default; -$comment-meta-color: $global-muted-color !default; - -$comment-list-margin-top: $global-large-margin !default; -$comment-list-padding-left: 30px !default; -$comment-list-padding-left-m: 100px !default; - - -/* ======================================================================== - Component: Comment - ========================================================================== */ - -.uk-comment { - @if(mixin-exists(hook-comment)) {@include hook-comment();} -} - - -/* Sections - ========================================================================== */ - -.uk-comment-body { - @if(mixin-exists(hook-comment-body)) {@include hook-comment-body();} -} - -.uk-comment-header { - margin-bottom: $comment-header-margin-bottom; - @if(mixin-exists(hook-comment-header)) {@include hook-comment-header();} -} - -/* - * Micro clearfix - */ - -.uk-comment-body::before, -.uk-comment-body::after, -.uk-comment-header::before, -.uk-comment-header::after { - content: ""; - display: table; -} - -.uk-comment-body::after, -.uk-comment-header::after { clear: both; } - -/* - * Remove margin from the last-child - */ - -.uk-comment-body > :last-child, -.uk-comment-header > :last-child { margin-bottom: 0; } - - -/* Title - ========================================================================== */ - -.uk-comment-title { - font-size: $comment-title-font-size; - line-height: $comment-title-line-height; - @if(mixin-exists(hook-comment-title)) {@include hook-comment-title();} -} - - -/* Meta - ========================================================================== */ - -.uk-comment-meta { - font-size: $comment-meta-font-size; - line-height: $comment-meta-line-height; - color: $comment-meta-color; - @if(mixin-exists(hook-comment-meta)) {@include hook-comment-meta();} -} - - -/* Avatar - ========================================================================== */ - -.uk-comment-avatar { - @if(mixin-exists(hook-comment-avatar)) {@include hook-comment-avatar();} -} - - -/* List - ========================================================================== */ - -.uk-comment-list { - padding: 0; - list-style: none; -} - -/* Adjacent siblings */ -.uk-comment-list > :nth-child(n+2) { - margin-top: $comment-list-margin-top; - @if(mixin-exists(hook-comment-list-adjacent)) {@include hook-comment-list-adjacent();} -} - -/* - * Sublists - * Note: General sibling selector allows reply block between comment and sublist - */ - -.uk-comment-list .uk-comment ~ ul { - margin: $comment-list-margin-top 0 0 0; - padding-left: $comment-list-padding-left; - list-style: none; - @if(mixin-exists(hook-comment-list-sub)) {@include hook-comment-list-sub();} -} - -/* Tablet and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-comment-list .uk-comment ~ ul { padding-left: $comment-list-padding-left-m; } - -} - -/* Adjacent siblings */ -.uk-comment-list .uk-comment ~ ul > :nth-child(n+2) { - margin-top: $comment-list-margin-top; - @if(mixin-exists(hook-comment-list-sub-adjacent)) {@include hook-comment-list-sub-adjacent();} -} - - -/* Style modifier - ========================================================================== */ - -.uk-comment-primary { - @if(mixin-exists(hook-comment-primary)) {@include hook-comment-primary();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-comment-misc)) {@include hook-comment-misc();} - -// @mixin hook-comment(){} -// @mixin hook-comment-body(){} -// @mixin hook-comment-header(){} -// @mixin hook-comment-title(){} -// @mixin hook-comment-meta(){} -// @mixin hook-comment-avatar(){} -// @mixin hook-comment-list-adjacent(){} -// @mixin hook-comment-list-sub(){} -// @mixin hook-comment-list-sub-adjacent(){} -// @mixin hook-comment-primary(){} -// @mixin hook-comment-misc(){} \ No newline at end of file diff --git a/_sass/uikit/components/container.scss b/_sass/uikit/components/container.scss deleted file mode 100644 index c518d6d7f6..0000000000 --- a/_sass/uikit/components/container.scss +++ /dev/null @@ -1,106 +0,0 @@ -// Name: Container -// Description: Component to align and center your site and grid content -// -// Component: `uk-container` -// -// Modifier: `uk-container-small` -// `uk-container-large` -// `uk-container-expand` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$container-max-width: 1200px !default; -$container-small-max-width: 900px !default; -$container-large-max-width: 1600px !default; - -$container-padding-horizontal: 15px !default; -$container-padding-horizontal-s: $global-gutter !default; -$container-padding-horizontal-m: $global-medium-gutter !default; - - -/* ======================================================================== - Component: Container - ========================================================================== */ - -/* - * 1. Box sizing has to be `content-box` so the max-width is always the same and - * unaffected by the padding on different breakpoints. It's important for the size modifiers. - */ - -.uk-container { - box-sizing: content-box; /* 1 */ - max-width: $container-max-width; - margin-left: auto; - margin-right: auto; - padding-left: $container-padding-horizontal; - padding-right: $container-padding-horizontal; -} - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - .uk-container { - padding-left: $container-padding-horizontal-s; - padding-right: $container-padding-horizontal-s; - } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-container { - padding-left: $container-padding-horizontal-m; - padding-right: $container-padding-horizontal-m; - } - -} - -/* - * Micro clearfix - */ - -.uk-container::before, -.uk-container::after { - content: ""; - display: table; -} - -.uk-container::after { clear: both; } - -/* - * Remove margin from the last-child - */ - -.uk-container > :last-child { margin-bottom: 0; } - -/* - * Remove padding from nested containers - */ - -.uk-container .uk-container { - padding-left: 0; - padding-right: 0; -} - - -/* Size modifier - ========================================================================== */ - -.uk-container-small { max-width: $container-small-max-width; } - -.uk-container-large { max-width: $container-large-max-width; } - -.uk-container-expand { max-width: none; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-container-misc)) {@include hook-container-misc();} - -// @mixin hook-container-misc(){} \ No newline at end of file diff --git a/_sass/uikit/components/countdown.scss b/_sass/uikit/components/countdown.scss deleted file mode 100644 index 3f53e70c71..0000000000 --- a/_sass/uikit/components/countdown.scss +++ /dev/null @@ -1,126 +0,0 @@ -// Name: Countdown -// Description: Component to create countdown timers -// -// Component: `uk-countdown` -// -// Sub-objects: `uk-countdown-number` -// `uk-countdown-separator` -// `uk-countdown-label` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$countdown-item-line-height: 70px !default; - -$countdown-number-font-size: 2rem !default; // 32px -$countdown-number-font-size-s: 4rem !default; // 64px -$countdown-number-font-size-m: 6rem !default; // 96px - -$countdown-separator-font-size: 1rem !default; // 16px -$countdown-separator-font-size-s: 2rem !default; // 32px -$countdown-separator-font-size-m: 3rem !default; // 48px - - -/* ======================================================================== - Component: Countdown - ========================================================================== */ - -.uk-countdown { - @if(mixin-exists(hook-countdown)) {@include hook-countdown();} -} - - -/* Item - ========================================================================== */ - -/* - * 1. Center numbers and separators vertically - */ - -.uk-countdown-number, -.uk-countdown-separator { - /* 1 */ - line-height: $countdown-item-line-height; - @if(mixin-exists(hook-countdown-item)) {@include hook-countdown-item();} -} - - -/* Number - ========================================================================== */ - -.uk-countdown-number { - font-size: $countdown-number-font-size; - @if(mixin-exists(hook-countdown-number)) {@include hook-countdown-number();} -} - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - .uk-countdown-number { font-size: $countdown-number-font-size-s; } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-countdown-number { font-size: $countdown-number-font-size-m; } - -} - - -/* Separator - ========================================================================== */ - -.uk-countdown-separator { - font-size: $countdown-separator-font-size; - @if(mixin-exists(hook-countdown-separator)) {@include hook-countdown-separator();} -} - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - .uk-countdown-separator { font-size: $countdown-separator-font-size-s; } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-countdown-separator { font-size: $countdown-separator-font-size-m; } - -} - - -/* Label - ========================================================================== */ - -.uk-countdown-label { - @if(mixin-exists(hook-countdown-label)) {@include hook-countdown-label();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-countdown-misc)) {@include hook-countdown-misc();} - -// @mixin hook-countdown(){} -// @mixin hook-countdown-item(){} -// @mixin hook-countdown-number(){} -// @mixin hook-countdown-separator(){} -// @mixin hook-countdown-label(){} -// @mixin hook-countdown-misc(){} - - -// Inverse -// ======================================================================== - - - -// @mixin hook-inverse-countdown-item(){} -// @mixin hook-inverse-countdown-number(){} -// @mixin hook-inverse-countdown-separator(){} -// @mixin hook-inverse-countdown-label(){} diff --git a/_sass/uikit/components/cover.scss b/_sass/uikit/components/cover.scss deleted file mode 100644 index b44a6847d5..0000000000 --- a/_sass/uikit/components/cover.scss +++ /dev/null @@ -1,57 +0,0 @@ -// Name: Cover -// Description: Utilities to let embedded content cover their container in a centered position -// -// Component: `uk-cover` -// -// Sub-object: `uk-cover-container` -// -// ======================================================================== - - -/* ======================================================================== - Component: Cover - ========================================================================== */ - -/* - * Works with iframes and embedded content - * 1. Reset responsiveness for embedded content - * 2. Center object - * Note: Percent values on the `top` property only works if this element - * is absolute positioned or if the container has a height - */ - -.uk-cover { - /* 1 */ - max-width: none; - /* 2 */ - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%,-50%); -} - -iframe.uk-cover { pointer-events: none; } - - -/* Container - ========================================================================== */ - -/* - * 1. Parent container which clips resized object - * 2. Needed if the child is positioned absolute. See note above - */ - -.uk-cover-container { - /* 1 */ - overflow: hidden; - /* 2 */ - position: relative; -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-cover-misc)) {@include hook-cover-misc();} - -// @mixin hook-cover-misc(){} \ No newline at end of file diff --git a/_sass/uikit/components/description-list.scss b/_sass/uikit/components/description-list.scss deleted file mode 100644 index 6683286dfd..0000000000 --- a/_sass/uikit/components/description-list.scss +++ /dev/null @@ -1,71 +0,0 @@ -// Name: Description list -// Description: Styles for description lists -// -// Component: `uk-description-list` -// -// Modifiers: `uk-description-list-divider` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$description-list-term-color: $global-emphasis-color !default; -$description-list-term-margin-top: $global-margin !default; - -$description-list-divider-term-margin-top: $global-margin !default; -$description-list-divider-term-border-width: $global-border-width !default; -$description-list-divider-term-border: $global-border !default; - - -/* ======================================================================== - Component: Description list - ========================================================================== */ - -/* - * Term - */ - -.uk-description-list > dt { - color: $description-list-term-color; - @if(mixin-exists(hook-description-list-term)) {@include hook-description-list-term();} -} - -.uk-description-list > dt:nth-child(n+2) { - margin-top: $description-list-term-margin-top; -} - -/* - * Description - */ - -.uk-description-list > dd { - @if(mixin-exists(hook-description-list-description)) {@include hook-description-list-description();} -} - - -/* Style modifier - ========================================================================== */ - -/* - * Line - */ - -.uk-description-list-divider > dt:nth-child(n+2) { - margin-top: $description-list-divider-term-margin-top; - padding-top: $description-list-divider-term-margin-top; - border-top: $description-list-divider-term-border-width solid $description-list-divider-term-border; - @if(mixin-exists(hook-description-list-divider-term)) {@include hook-description-list-divider-term();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-description-list-misc)) {@include hook-description-list-misc();} - -// @mixin hook-description-list-term(){} -// @mixin hook-description-list-description(){} -// @mixin hook-description-list-divider-term(){} -// @mixin hook-description-list-misc(){} \ No newline at end of file diff --git a/_sass/uikit/components/divider.scss b/_sass/uikit/components/divider.scss deleted file mode 100644 index b51708cf43..0000000000 --- a/_sass/uikit/components/divider.scss +++ /dev/null @@ -1,129 +0,0 @@ -// Name: Divider -// Description: Styles for dividers -// -// Component: `uk-divider-icon` -// `uk-divider-small` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$divider-margin-vertical: $global-margin !default; - -$divider-icon-width: 50px !default; -$divider-icon-height: 20px !default; -$divider-icon-color: $global-border !default; -$divider-icon-line-top: 50% !default; -$divider-icon-line-width: 100% !default; -$divider-icon-line-border-width: $global-border-width !default; -$divider-icon-line-border: $global-border !default; - -$internal-divider-icon-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22none%22%20stroke%3D%22#000%22%20stroke-width%3D%222%22%20cx%3D%2210%22%20cy%3D%2210%22%20r%3D%227%22%20%2F%3E%0A%3C%2Fsvg%3E%0A" !default; - -$divider-small-width: 100px !default; -$divider-small-border-width: $global-border-width !default; -$divider-small-border: $global-border !default; - - -/* ======================================================================== - Component: Divider - ========================================================================== */ - -/* - * 1. Reset default `hr` - * 2. Set margin if a `div` is used for semantical reason - */ - -[class*='uk-divider'] { - /* 1 */ - border: none; - /* 2 */ - margin-bottom: $divider-margin-vertical; -} - -/* Add margin if adjacent element */ -* + [class*='uk-divider'] { margin-top: $divider-margin-vertical; } - - -/* Icon - ========================================================================== */ - -.uk-divider-icon { - position: relative; - height: $divider-icon-height; - @include svg-fill($internal-divider-icon-image, "#000", $divider-icon-color); - background-repeat: no-repeat; - background-position: 50% 50%; - @if(mixin-exists(hook-divider-icon)) {@include hook-divider-icon();} -} - -.uk-divider-icon::before, -.uk-divider-icon::after { - content: ""; - position: absolute; - top: $divider-icon-line-top; - max-width: unquote('calc(50% - (#{$divider-icon-width} / 2))'); - border-bottom: $divider-icon-line-border-width solid $divider-icon-line-border; - @if(mixin-exists(hook-divider-icon-line)) {@include hook-divider-icon-line();} -} - -.uk-divider-icon::before { - right: unquote('calc(50% + (#{$divider-icon-width} / 2))'); - width: $divider-icon-line-width; - @if(mixin-exists(hook-divider-icon-line-left)) {@include hook-divider-icon-line-left();} -} - -.uk-divider-icon::after { - left: unquote('calc(50% + (#{$divider-icon-width} / 2))'); - width: $divider-icon-line-width; - @if(mixin-exists(hook-divider-icon-line-right)) {@include hook-divider-icon-line-right();} -} - - -/* Small - ========================================================================== */ - -/* - * Reset child height, caused by `inline-block` - */ - -.uk-divider-small { line-height: 0; } - -.uk-divider-small::after { - content: ""; - display: inline-block; - width: $divider-small-width; - max-width: 100%; - border-top: $divider-small-border-width solid $divider-small-border; - vertical-align: top; - @if(mixin-exists(hook-divider-small)) {@include hook-divider-small();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-divider-misc)) {@include hook-divider-misc();} - -// @mixin hook-divider-icon(){} -// @mixin hook-divider-icon-line(){} -// @mixin hook-divider-icon-line-left(){} -// @mixin hook-divider-icon-line-right(){} -// @mixin hook-divider-small(){} -// @mixin hook-divider-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-divider-icon-color: $inverse-global-border !default; -$inverse-divider-icon-line-border: $inverse-global-border !default; -$inverse-divider-small-border: $inverse-global-border !default; - - - -// @mixin hook-inverse-divider-icon(){} -// @mixin hook-inverse-divider-icon-line(){} -// @mixin hook-inverse-divider-small(){} diff --git a/_sass/uikit/components/dotnav.scss b/_sass/uikit/components/dotnav.scss deleted file mode 100644 index f1f2a4029c..0000000000 --- a/_sass/uikit/components/dotnav.scss +++ /dev/null @@ -1,157 +0,0 @@ -// Name: Dotnav -// Description: Component to create dot navigations -// -// Component: `uk-dotnav` -// -// Modifier: `uk-dotnav-vertical` -// -// States: `uk-active` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$dotnav-margin-horizontal: 12px !default; -$dotnav-margin-vertical: $dotnav-margin-horizontal !default; - -$dotnav-item-width: 10px !default; -$dotnav-item-height: $dotnav-item-width !default; -$dotnav-item-border-radius: 50% !default; - -$dotnav-item-background: rgba($global-color, 0.2) !default; -$dotnav-item-hover-background: rgba($global-color, 0.6) !default; -$dotnav-item-onclick-background: rgba($global-color, 0.2) !default; -$dotnav-item-active-background: rgba($global-color, 0.6) !default; - - -/* ======================================================================== - Component: Dotnav - ========================================================================== */ - -/* - * 1. Allow items to wrap into the next line - * 2. Reset list - * 3. Gutter - */ - -.uk-dotnav { - display: flex; - /* 1 */ - flex-wrap: wrap; - /* 2 */ - margin: 0; - padding: 0; - list-style: none; - /* 3 */ - margin-left: (-$dotnav-margin-horizontal); - @if(mixin-exists(hook-dotnav)) {@include hook-dotnav();} -} - -/* - * 1. Space is allocated solely based on content dimensions: 0 0 auto - * 2. Gutter - */ - -.uk-dotnav > * { - /* 1 */ - flex: none; - /* 2 */ - padding-left: $dotnav-margin-horizontal; -} - - -/* Items - ========================================================================== */ - -/* - * Items - * 1. Hide text if present - */ - -.uk-dotnav > * > * { - display: block; - box-sizing: border-box; - width: $dotnav-item-width; - height: $dotnav-item-height; - border-radius: $dotnav-item-border-radius; - background: $dotnav-item-background; - /* 1 */ - text-indent: 100%; - overflow: hidden; - white-space: nowrap; - @if(mixin-exists(hook-dotnav-item)) {@include hook-dotnav-item();} -} - -/* Hover + Focus */ -.uk-dotnav > * > :hover, -.uk-dotnav > * > :focus { - background-color: $dotnav-item-hover-background; - outline: none; - @if(mixin-exists(hook-dotnav-item-hover)) {@include hook-dotnav-item-hover();} -} - -/* OnClick */ -.uk-dotnav > * > :active { - background-color: $dotnav-item-onclick-background; - @if(mixin-exists(hook-dotnav-item-onclick)) {@include hook-dotnav-item-onclick();} -} - -/* Active */ -.uk-dotnav > .uk-active > * { - background-color: $dotnav-item-active-background; - @if(mixin-exists(hook-dotnav-item-active)) {@include hook-dotnav-item-active();} -} - - -/* Modifier: 'uk-dotnav-vertical' - ========================================================================== */ - -/* - * 1. Change direction - * 2. Gutter - */ - -.uk-dotnav-vertical { - /* 1 */ - flex-direction: column; - /* 2 */ - margin-left: 0; - margin-top: (-$dotnav-margin-vertical); -} - -/* 2 */ -.uk-dotnav-vertical > * { - padding-left: 0; - padding-top: $dotnav-margin-vertical; -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-dotnav-misc)) {@include hook-dotnav-misc();} - -// @mixin hook-dotnav(){} -// @mixin hook-dotnav-item(){} -// @mixin hook-dotnav-item-hover(){} -// @mixin hook-dotnav-item-onclick(){} -// @mixin hook-dotnav-item-active(){} -// @mixin hook-dotnav-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-dotnav-item-background: rgba($inverse-global-color, 0.5) !default; -$inverse-dotnav-item-hover-background: rgba($inverse-global-color, 0.9) !default; -$inverse-dotnav-item-onclick-background: rgba($inverse-global-color, 0.5) !default; -$inverse-dotnav-item-active-background: rgba($inverse-global-color, 0.9) !default; - - - -// @mixin hook-inverse-dotnav-item(){} -// @mixin hook-inverse-dotnav-item-hover(){} -// @mixin hook-inverse-dotnav-item-onclick(){} -// @mixin hook-inverse-dotnav-item-active(){} \ No newline at end of file diff --git a/_sass/uikit/components/drop.scss b/_sass/uikit/components/drop.scss deleted file mode 100644 index fb5e9e8c5a..0000000000 --- a/_sass/uikit/components/drop.scss +++ /dev/null @@ -1,74 +0,0 @@ -// Name: Drop -// Description: Component to position any element next to any other element. -// -// Component: `uk-drop` -// -// Modifiers: `uk-drop-top-*` -// `uk-drop-bottom-*` -// `uk-drop-left-*` -// `uk-drop-right-*` -// `uk-drop-stack` -// `uk-drop-grid` -// -// States: `uk-open` -// -// Uses: Animation -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$drop-z-index: $global-z-index + 20 !default; -$drop-width: 300px !default; -$drop-margin: $global-margin !default; - - -/* ======================================================================== - Component: Drop - ========================================================================== */ - -/* - * 1. Hide by default - * 2. Set position - * 3. Set a default width - */ - -.uk-drop { - /* 1 */ - display: none; - /* 2 */ - position: absolute; - z-index: $drop-z-index; - /* 3 */ - box-sizing: border-box; - width: $drop-width; -} - -/* Show */ -.uk-drop.uk-open { display: block; } - - -/* Direction / Alignment modifiers - ========================================================================== */ - -/* Direction */ -[class*='uk-drop-top'] { margin-top: (-$drop-margin); } -[class*='uk-drop-bottom'] { margin-top: $drop-margin; } -[class*='uk-drop-left'] { margin-left: (-$drop-margin); } -[class*='uk-drop-right'] { margin-left: $drop-margin; } - - -/* Grid modifiers - ========================================================================== */ - -.uk-drop-stack .uk-drop-grid > * { width: 100% !important; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-drop-misc)) {@include hook-drop-misc();} - -// @mixin hook-drop-misc(){} diff --git a/_sass/uikit/components/dropdown.scss b/_sass/uikit/components/dropdown.scss deleted file mode 100644 index 49bb12776a..0000000000 --- a/_sass/uikit/components/dropdown.scss +++ /dev/null @@ -1,150 +0,0 @@ -// Name: Dropdown -// Description: Component to create dropdown menus -// -// Component: `uk-dropdown` -// -// Adopted: `uk-dropdown-nav` -// -// Modifiers: `uk-dropdown-top-*` -// `uk-dropdown-bottom-*` -// `uk-dropdown-left-*` -// `uk-dropdown-right-*` -// `uk-dropdown-stack` -// `uk-dropdown-grid` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$dropdown-z-index: $global-z-index + 20 !default; -$dropdown-min-width: 200px !default; -$dropdown-padding: 15px !default; -$dropdown-background: $global-muted-background !default; -$dropdown-color: $global-color !default; -$dropdown-margin: $global-small-margin !default; - -$dropdown-nav-item-color: $global-muted-color !default; -$dropdown-nav-item-hover-color: $global-color !default; -$dropdown-nav-header-color: $global-emphasis-color !default; -$dropdown-nav-divider-border-width: $global-border-width !default; -$dropdown-nav-divider-border: $global-border !default; -$dropdown-nav-sublist-item-color: $global-muted-color !default; -$dropdown-nav-sublist-item-hover-color: $global-color !default; - - -/* ======================================================================== - Component: Dropdown - ========================================================================== */ - -/* - * 1. Hide by default - * 2. Set position - * 3. Set a default width - * 4. Style - */ - -.uk-dropdown { - /* 1 */ - display: none; - /* 2 */ - position: absolute; - z-index: $dropdown-z-index; - /* 3 */ - box-sizing: border-box; - min-width: $dropdown-min-width; - /* 4 */ - padding: $dropdown-padding; - background: $dropdown-background; - color: $dropdown-color; - @if(mixin-exists(hook-dropdown)) {@include hook-dropdown();} -} - -/* Show */ -.uk-dropdown.uk-open { display: block; } - - -/* Nav - * Adopts `uk-nav` - ========================================================================== */ - -.uk-dropdown-nav { - white-space: nowrap; - @if(mixin-exists(hook-dropdown-nav)) {@include hook-dropdown-nav();} -} - -/* - * Items - */ - -.uk-dropdown-nav > li > a { - color: $dropdown-nav-item-color; - @if(mixin-exists(hook-dropdown-nav-item)) {@include hook-dropdown-nav-item();} -} - -/* Hover + Focus + Active */ -.uk-dropdown-nav > li > a:hover, -.uk-dropdown-nav > li > a:focus, -.uk-dropdown-nav > li.uk-active > a { - color: $dropdown-nav-item-hover-color; - @if(mixin-exists(hook-dropdown-nav-item-hover)) {@include hook-dropdown-nav-item-hover();} -} - -/* - * Header - */ - -.uk-dropdown-nav .uk-nav-header { - color: $dropdown-nav-header-color; - @if(mixin-exists(hook-dropdown-nav-header)) {@include hook-dropdown-nav-header();} -} - -/* - * Divider - */ - -.uk-dropdown-nav .uk-nav-divider { - border-top: $dropdown-nav-divider-border-width solid $dropdown-nav-divider-border; - @if(mixin-exists(hook-dropdown-nav-divider)) {@include hook-dropdown-nav-divider();} -} - -/* - * Sublists - */ - -.uk-dropdown-nav .uk-nav-sub a { color: $dropdown-nav-sublist-item-color; } - -.uk-dropdown-nav .uk-nav-sub a:hover, -.uk-dropdown-nav .uk-nav-sub a:focus { color: $dropdown-nav-sublist-item-hover-color; } - - -/* Direction / Alignment modifiers - ========================================================================== */ - -/* Direction */ -[class*='uk-dropdown-top'] { margin-top: (-$dropdown-margin); } -[class*='uk-dropdown-bottom'] { margin-top: $dropdown-margin; } -[class*='uk-dropdown-left'] { margin-left: (-$dropdown-margin); } -[class*='uk-dropdown-right'] { margin-left: $dropdown-margin; } - - -/* Grid modifiers - ========================================================================== */ - -.uk-dropdown-stack .uk-dropdown-grid > * { width: 100% !important; } - - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-dropdown-misc)) {@include hook-dropdown-misc();} - -// @mixin hook-dropdown(){} -// @mixin hook-dropdown-nav(){} -// @mixin hook-dropdown-nav-item(){} -// @mixin hook-dropdown-nav-item-hover(){} -// @mixin hook-dropdown-nav-header(){} -// @mixin hook-dropdown-nav-divider(){} -// @mixin hook-dropdown-misc(){} diff --git a/_sass/uikit/components/flex.scss b/_sass/uikit/components/flex.scss deleted file mode 100644 index 1301fc4335..0000000000 --- a/_sass/uikit/components/flex.scss +++ /dev/null @@ -1,209 +0,0 @@ -// Name: Flex -// Description: Utilities for layouts based on flexbox -// -// Component: `uk-flex-*` -// -// ======================================================================== - - -/* ======================================================================== - Component: Flex - ========================================================================== */ - -.uk-flex { display: flex; } -.uk-flex-inline { display: inline-flex; } - -/* - * Remove pseudo elements created by micro clearfix as precaution - */ - -.uk-flex::before, -.uk-flex::after, -.uk-flex-inline::before, -.uk-flex-inline::after { display: none; } - - -/* Alignment - ========================================================================== */ - -/* - * Align items along the main axis of the current line of the flex container - * Row: Horizontal - */ - -// Default -.uk-flex-left { justify-content: flex-start; } -.uk-flex-center { justify-content: center; } -.uk-flex-right { justify-content: flex-end; } -.uk-flex-between { justify-content: space-between; } -.uk-flex-around { justify-content: space-around; } - - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - .uk-flex-left\@s { justify-content: flex-start; } - .uk-flex-center\@s { justify-content: center; } - .uk-flex-right\@s { justify-content: flex-end; } - .uk-flex-between\@s { justify-content: space-between; } - .uk-flex-around\@s { justify-content: space-around; } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-flex-left\@m { justify-content: flex-start; } - .uk-flex-center\@m { justify-content: center; } - .uk-flex-right\@m { justify-content: flex-end; } - .uk-flex-between\@m { justify-content: space-between; } - .uk-flex-around\@m { justify-content: space-around; } - -} - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-flex-left\@l { justify-content: flex-start; } - .uk-flex-center\@l { justify-content: center; } - .uk-flex-right\@l { justify-content: flex-end; } - .uk-flex-between\@l { justify-content: space-between; } - .uk-flex-around\@l { justify-content: space-around; } - -} - -/* Large screen and bigger */ -@media (min-width: $breakpoint-xlarge) { - - .uk-flex-left\@xl { justify-content: flex-start; } - .uk-flex-center\@xl { justify-content: center; } - .uk-flex-right\@xl { justify-content: flex-end; } - .uk-flex-between\@xl { justify-content: space-between; } - .uk-flex-around\@xl { justify-content: space-around; } - -} - -/* - * Align items in the cross axis of the current line of the flex container - * Row: Vertical - */ - -// Default -.uk-flex-stretch { align-items: stretch; } -.uk-flex-top { align-items: flex-start; } -.uk-flex-middle { align-items: center; } -.uk-flex-bottom { align-items: flex-end; } - - -/* Direction - ========================================================================== */ - -// Default -.uk-flex-row { flex-direction: row; } -.uk-flex-row-reverse { flex-direction: row-reverse; } -.uk-flex-column { flex-direction: column; } -.uk-flex-column-reverse { flex-direction: column-reverse; } - - -/* Wrap - ========================================================================== */ - -// Default -.uk-flex-nowrap { flex-wrap: nowrap; } -.uk-flex-wrap { flex-wrap: wrap; } -.uk-flex-wrap-reverse { flex-wrap: wrap-reverse; } - -/* - * Aligns items within the flex container when there is extra space in the cross-axis - * Only works if there is more than one line of flex items - */ - -// Default -.uk-flex-wrap-stretch { align-content: stretch; } -.uk-flex-wrap-top { align-content: flex-start; } -.uk-flex-wrap-middle { align-content: center; } -.uk-flex-wrap-bottom { align-content: flex-end; } -.uk-flex-wrap-between { align-content: space-between; } -.uk-flex-wrap-around { align-content: space-around; } - - -/* Item ordering - ========================================================================== */ - -/* - * Default is 0 - */ - -.uk-flex-first { order: -1;} -.uk-flex-last { order: 99;} - - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - .uk-flex-first\@s { order: -1; } - .uk-flex-last\@s { order: 99; } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-flex-first\@m { order: -1; } - .uk-flex-last\@m { order: 99; } - -} - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-flex-first\@l { order: -1; } - .uk-flex-last\@l { order: 99; } - -} - -/* Large screen and bigger */ -@media (min-width: $breakpoint-xlarge) { - - .uk-flex-first\@xl { order: -1; } - .uk-flex-last\@xl { order: 99; } - -} - - -/* Item dimensions - ========================================================================== */ - -/* - * Initial: 0 1 auto - * Content dimensions, but shrinks - */ - -/* - * No Flex: 0 0 auto - * Content dimensions - */ - -.uk-flex-none { flex: none; } - -/* - * Relative Flex: 1 1 auto - * Space is allocated considering content - */ - -.uk-flex-auto { flex: auto; } - -/* - * Absolute Flex: 1 1 0% - * Space is allocated solely based on flex - */ - -.uk-flex-1 { flex: 1; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-flex-misc)) {@include hook-flex-misc();} - -// @mixin hook-flex-misc(){} \ No newline at end of file diff --git a/_sass/uikit/components/form-range.scss b/_sass/uikit/components/form-range.scss deleted file mode 100644 index f7a22a5d50..0000000000 --- a/_sass/uikit/components/form-range.scss +++ /dev/null @@ -1,185 +0,0 @@ -// Name: Form Range -// Description: Styles for the range input type -// -// Component: `uk-range` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$form-range-thumb-height: 15px !default; -$form-range-thumb-border-radius: 500px !default; -$form-range-thumb-background: $global-color !default; - -$form-range-track-height: 3px !default; -$form-range-track-background: darken($global-muted-background, 5%) !default; -$form-range-track-focus-background: darken($global-muted-background, 15%) !default; - - -/* ======================================================================== - Component: Form Range - ========================================================================== */ - -/* - * 1. Normalize and defaults - * 2. Prevent content overflow if a fixed width is used - * 3. Take the full width - * 4. Remove default style - * 5. Remove white background in Chrome - * 6. Remove padding in IE11 - */ - -.uk-range { - /* 1 */ - box-sizing: border-box; - margin: 0; - vertical-align: middle; - /* 2 */ - max-width: 100%; - /* 3 */ - width: 100%; - /* 4 */ - -webkit-appearance: none; - /* 5 */ - background: transparent; - /* 6 */ - padding: 0; - @if(mixin-exists(hook-form-range)) {@include hook-form-range();} -} - -/* Focus */ -.uk-range:focus { outline: none; } -.uk-range::-moz-focus-outer { border: none; } - -/* IE11 Reset */ -.uk-range::-ms-track { - height: $form-range-thumb-height; - background: transparent; - border-color: transparent; - color: transparent; -} - -/* - * Improves consistency of cursor style for clickable elements - */ - -.uk-range:not(:disabled)::-webkit-slider-thumb { cursor: pointer; } -.uk-range:not(:disabled)::-moz-range-thumb { cursor: pointer; } -.uk-range:not(:disabled)::-ms-thumb { cursor: pointer; } - - -/* Thumb - ========================================================================== */ - -/* - * 1. Reset - * 2. Style - */ - -/* Webkit */ -.uk-range::-webkit-slider-thumb { - /* 1 */ - -webkit-appearance: none; - margin-top: (floor($form-range-thumb-height / 2) * -1); - /* 2 */ - height: $form-range-thumb-height; - width: $form-range-thumb-height; - border-radius: $form-range-thumb-border-radius; - background: $form-range-thumb-background; - @if(mixin-exists(hook-form-range-thumb)) {@include hook-form-range-thumb();} -} - -/* Firefox */ -.uk-range::-moz-range-thumb { - /* 1 */ - border: none; - /* 2 */ - height: $form-range-thumb-height; - width: $form-range-thumb-height; - border-radius: $form-range-thumb-border-radius; - background: $form-range-thumb-background; - @if(mixin-exists(hook-form-range-thumb)) {@include hook-form-range-thumb();} -} - -/* Edge */ -.uk-range::-ms-thumb { - /* 1 */ - margin-top: 0; -} - -/* IE11 */ -.uk-range::-ms-thumb { - /* 1 */ - border: none; - /* 2 */ - height: $form-range-thumb-height; - width: $form-range-thumb-height; - border-radius: $form-range-thumb-border-radius; - background: $form-range-thumb-background; - @if(mixin-exists(hook-form-range-thumb)) {@include hook-form-range-thumb();} -} - -/* Edge + IE11 */ -.uk-range::-ms-tooltip { display: none; } - - -/* Track - ========================================================================== */ - -/* - * 1. Safari doesn't have a focus state. Using active instead. - */ - -/* Webkit */ -.uk-range::-webkit-slider-runnable-track { - height: $form-range-track-height; - background: $form-range-track-background; - @if(mixin-exists(hook-form-range-track)) {@include hook-form-range-track();} -} - -.uk-range:focus::-webkit-slider-runnable-track, -/* 1 */ -.uk-range:active::-webkit-slider-runnable-track { - background: $form-range-track-focus-background; - @if(mixin-exists(hook-form-range-track-focus)) {@include hook-form-range-track-focus();} -} - -/* Firefox */ -.uk-range::-moz-range-track { - height: $form-range-track-height; - background: $form-range-track-background; - @if(mixin-exists(hook-form-range-track)) {@include hook-form-range-track();} -} - -.uk-range:focus::-moz-range-track { - background: $form-range-track-focus-background; - @if(mixin-exists(hook-form-range-track-focus)) {@include hook-form-range-track-focus();} -} - -/* Edge */ -.uk-range::-ms-fill-lower, -.uk-range::-ms-fill-upper { - height: $form-range-track-height; - background: $form-range-track-background; - @if(mixin-exists(hook-form-range-track)) {@include hook-form-range-track();} -} - -.uk-range:focus::-ms-fill-lower, -.uk-range:focus::-ms-fill-upper { - background: $form-range-track-focus-background; - @if(mixin-exists(hook-form-range-track-focus)) {@include hook-form-range-track-focus();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-form-range-misc)) {@include hook-form-range-misc();} - -// @mixin hook-form-range(){} -// @mixin hook-form-range-thumb(){} -// @mixin hook-form-range-track(){} -// @mixin hook-form-range-track-focus(){} -// @mixin hook-form-range-misc(){} diff --git a/_sass/uikit/components/form.scss b/_sass/uikit/components/form.scss deleted file mode 100644 index e731cffb63..0000000000 --- a/_sass/uikit/components/form.scss +++ /dev/null @@ -1,756 +0,0 @@ -// Name: Form -// Description: Styles for forms -// -// Component: `uk-form-*` -// `uk-input` -// `uk-select` -// `uk-textarea` -// `uk-radio` -// `uk-checkbox` -// `uk-legend` -// `uk-fieldset` -// -// Sub-objects: `uk-form-custom` -// `uk-form-stacked` -// `uk-form-horizontal` -// `uk-form-label` -// `uk-form-controls` -// `uk-form-icon` -// `uk-form-icon-flip` -// -// Modifiers: `uk-form-small` -// `uk-form-large` -// `uk-form-danger` -// `uk-form-success` -// `uk-form-blank` -// `uk-form-width-xsmall` -// `uk-form-width-small` -// `uk-form-width-medium` -// `uk-form-width-large` -// `uk-form-controls-text` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$form-height: $global-control-height !default; -$form-line-height: $form-height !default; -$form-padding-horizontal: 10px !default; -$form-padding-vertical: 4px !default; - -$form-background: $global-muted-background !default; -$form-color: $global-color !default; - -$form-focus-background: $global-muted-background !default; -$form-focus-color: $global-color !default; - -$form-disabled-background: $global-muted-background !default; -$form-disabled-color: $global-muted-color !default; - -$form-placeholder-color: $global-muted-color !default; - -$form-small-height: $global-control-small-height !default; -$form-small-padding-horizontal: 8px !default; -$form-small-line-height: $form-small-height !default; -$form-small-font-size: $global-small-font-size !default; - -$form-large-height: $global-control-large-height !default; -$form-large-padding-horizontal: 12px !default; -$form-large-line-height: $form-large-height !default; -$form-large-font-size: $global-medium-font-size !default; - -$form-danger-color: $global-danger-background !default; -$form-success-color: $global-success-background !default; - -$form-width-xsmall: 50px !default; -$form-width-small: 130px !default; -$form-width-medium: 200px !default; -$form-width-large: 500px !default; - -$form-select-padding-right: 20px !default; -$form-select-icon-color: $global-color !default; -$form-select-disabled-icon-color: $global-muted-color !default; - -$form-radio-size: 16px !default; -$form-radio-margin-top: -4px !default; -$form-radio-background: darken($global-muted-background, 5%) !default; - -$form-radio-checked-background: $global-primary-background !default; -$form-radio-checked-icon-color: $global-inverse-color !default; - -$form-radio-checked-focus-background: darken($global-primary-background, 10%) !default; - -$form-radio-disabled-background: $global-muted-background !default; -$form-radio-disabled-icon-color: $global-muted-color !default; - -$form-legend-font-size: $global-large-font-size !default; -$form-legend-line-height: 1.4 !default; - -$form-stacked-margin-bottom: $global-small-margin !default; - -$form-horizontal-label-width: 200px !default; -$form-horizontal-label-margin-top: 7px !default; -$form-horizontal-controls-margin-left: 215px !default; -$form-horizontal-controls-text-padding-top: 7px !default; - -$form-icon-width: $form-height !default; -$form-icon-font-size: $global-font-size !default; -$form-icon-color: $global-muted-color !default; -$form-icon-hover-color: $global-color !default; - -$internal-form-select-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2224%22%20height%3D%2216%22%20viewBox%3D%220%200%2024%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22#000%22%20points%3D%2212%201%209%206%2015%206%22%20%2F%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22#000%22%20points%3D%2212%2013%209%208%2015%208%22%20%2F%3E%0A%3C%2Fsvg%3E%0A" !default; -$internal-form-radio-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22#000%22%20cx%3D%228%22%20cy%3D%228%22%20r%3D%222%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$internal-form-checkbox-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2211%22%20viewBox%3D%220%200%2014%2011%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22#000%22%20points%3D%2212%201%205%207.5%202%205%201%205.5%205%2010%2013%201.5%22%20%2F%3E%0A%3C%2Fsvg%3E%0A" !default; -$internal-form-checkbox-indeterminate-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22#000%22%20x%3D%223%22%20y%3D%228%22%20width%3D%2210%22%20height%3D%221%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; - - -/* ======================================================================== - Component: Form - ========================================================================== */ - -/* - * 1. Define consistent box sizing. - * Default is `content-box` with following exceptions set to `border-box` - * `select`, `input[type="checkbox"]` and `input[type="radio"]` - * `input[type="search"]` in Chrome, Safari and Opera - * `input[type="color"]` in Firefox - * 2. Address margins set differently in Firefox/IE and Chrome/Safari/Opera. - * 3. Remove `border-radius` in iOS. - * 4. Change font properties to `inherit` in all browsers. - */ - -.uk-input, -.uk-select, -.uk-textarea, -.uk-radio, -.uk-checkbox { - /* 1 */ - box-sizing: border-box; - /* 2 */ - margin: 0; - /* 3 */ - border-radius: 0; - /* 4 */ - font: inherit; -} - -/* - * Show the overflow in Edge. - */ - -.uk-input { overflow: visible; } - -/* - * Remove the inheritance of text transform in Firefox. - */ - -.uk-select { text-transform: none; } - -/* - * 1. Change font properties to `inherit` in all browsers - * 2. Don't inherit the `font-weight` and use `bold` instead. - * NOTE: Both declarations don't work in Chrome, Safari and Opera. - */ - -.uk-select optgroup { - /* 1 */ - font: inherit; - /* 2 */ - font-weight: bold; -} - -/* - * Remove the default vertical scrollbar in IE 10+. - */ - -.uk-textarea { overflow: auto; } - -/* - * Remove the inner padding and cancel buttons in Chrome on OS X and Safari on OS X. - */ - -.uk-input[type="search"]::-webkit-search-cancel-button, -.uk-input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } - - -/* - * Correct the cursor style of increment and decrement buttons in Chrome. - */ - -.uk-input[type="number"]::-webkit-inner-spin-button, -.uk-input[type="number"]::-webkit-outer-spin-button { height: auto; } - -/* - * Removes placeholder transparency in Firefox. - */ - -.uk-input::-moz-placeholder, -.uk-textarea::-moz-placeholder { opacity: 1; } - -/* - * Improves consistency of cursor style for clickable elements - */ - -.uk-radio:not(:disabled), -.uk-checkbox:not(:disabled) { cursor: pointer; } - -/* - * Define consistent border, margin, and padding. - */ - -.uk-fieldset { - border: none; - margin: 0; - padding: 0; -} - - -/* Input, select and textarea - * Allowed: `text`, `password`, `datetime`, `datetime-local`, `date`, `month`, - `time`, `week`, `number`, `email`, `url`, `search`, `tel`, `color` - * Disallowed: `range`, `radio`, `checkbox`, `file`, `submit`, `reset` and `image` - ========================================================================== */ - -/* - * Remove default style in iOS. - */ - -.uk-input, -.uk-textarea { -webkit-appearance: none; } - -/* - * 1. Prevent content overflow if a fixed width is used - * 2. Take the full width - * 3. Reset default - * 4. Style - */ - -.uk-input, -.uk-select, -.uk-textarea { - /* 1 */ - max-width: 100%; - /* 2 */ - width: 100%; - /* 3 */ - border: 0 none; - /* 4 */ - padding: 0 $form-padding-horizontal; - background: $form-background; - color: $form-color; - @if(mixin-exists(hook-form)) {@include hook-form();} -} - -/* - * Single-line - * 1. Allow any element to look like an `input` or `select` element - * 2. Make sure line-height is not larger than height - * Also needed to center the text vertically - */ - -.uk-input, -.uk-select:not([multiple]):not([size]) { - height: $form-height; - vertical-align: middle; - /* 1 */ - display: inline-block; - @if(mixin-exists(hook-form-single-line)) {@include hook-form-single-line();} -} - -/* 2 */ -.uk-input:not(input), -.uk-select:not(select) { line-height: $form-line-height; } - -/* - * Multi-line - */ - -.uk-select[multiple], -.uk-select[size], -.uk-textarea { - padding-top: $form-padding-vertical; - padding-bottom: $form-padding-vertical; - vertical-align: top; - @if(mixin-exists(hook-form-multi-line)) {@include hook-form-multi-line();} -} - -/* Focus */ -.uk-input:focus, -.uk-select:focus, -.uk-textarea:focus { - outline: none; - background-color: $form-focus-background; - color: $form-focus-color; - @if(mixin-exists(hook-form-focus)) {@include hook-form-focus();} -} - -/* Disabled */ -.uk-input:disabled, -.uk-select:disabled, -.uk-textarea:disabled { - background-color: $form-disabled-background; - color: $form-disabled-color; - @if(mixin-exists(hook-form-disabled)) {@include hook-form-disabled();} -} - -/* - * Placeholder - */ - -.uk-input:-ms-input-placeholder { color: $form-placeholder-color !important; } -.uk-input::placeholder { color: $form-placeholder-color; } - -.uk-textarea:-ms-input-placeholder { color: $form-placeholder-color !important; } -.uk-textarea::placeholder { color: $form-placeholder-color; } - - -/* Style modifier (`uk-input`, `uk-select` and `uk-textarea`) - ========================================================================== */ - -/* - * Small - */ - -.uk-form-small { font-size: $form-small-font-size; } - -.uk-form-small:not(textarea):not([multiple]):not([size]) { - height: $form-small-height; - padding-left: $form-small-padding-horizontal; - padding-right: $form-small-padding-horizontal; -} - -.uk-form-small:not(select):not(input):not(textarea) { line-height: $form-small-line-height; } - -/* - * Large - */ - -.uk-form-large { font-size: $form-large-font-size; } - -.uk-form-large:not(textarea):not([multiple]):not([size]) { - height: $form-large-height; - padding-left: $form-large-padding-horizontal; - padding-right: $form-large-padding-horizontal; -} - -.uk-form-large:not(select):not(input):not(textarea) { line-height: $form-large-line-height; } - - -/* Style modifier (`uk-input`, `uk-select` and `uk-textarea`) - ========================================================================== */ - -/* - * Error - */ - -.uk-form-danger, -.uk-form-danger:focus { - color: $form-danger-color; - @if(mixin-exists(hook-form-danger)) {@include hook-form-danger();} -} - -/* - * Success - */ - -.uk-form-success, -.uk-form-success:focus { - color: $form-success-color; - @if(mixin-exists(hook-form-success)) {@include hook-form-success();} -} - -/* - * Blank - */ - -.uk-form-blank { - background: none; - @if(mixin-exists(hook-form-blank)) {@include hook-form-blank();} -} - -.uk-form-blank:focus { - @if(mixin-exists(hook-form-blank-focus)) {@include hook-form-blank-focus();} -} - - -/* Width modifiers (`uk-input`, `uk-select` and `uk-textarea`) - ========================================================================== */ - -/* - * Fixed widths - * Different widths for mini sized `input` and `select` elements - */ - -input.uk-form-width-xsmall { width: $form-width-xsmall; } - -select.uk-form-width-xsmall { width: ($form-width-xsmall + 25px); } - -.uk-form-width-small { width: $form-width-small; } - -.uk-form-width-medium { width: $form-width-medium; } - -.uk-form-width-large { width: $form-width-large; } - - -/* Select - ========================================================================== */ - -/* - * 1. Remove default style. Also works in Firefox - * 2. Style - * 3. Remove default style in IE 10/11 - */ - -.uk-select:not([multiple]):not([size]) { - /* 1 */ - -webkit-appearance: none; - -moz-appearance: none; - /* 2 */ - padding-right: $form-select-padding-right; - @include svg-fill($internal-form-select-image, "#000", $form-select-icon-color); - background-repeat: no-repeat; - background-position: 100% 50%; -} - -/* 3 */ -.uk-select:not([multiple]):not([size])::-ms-expand { display: none; } - -/* - * Disabled - */ - -.uk-select:not([multiple]):not([size]):disabled { @include svg-fill($internal-form-select-image, "#000", $form-select-disabled-icon-color); } - - -/* Radio and checkbox - * Note: Does not work in IE11 - ========================================================================== */ - -/* - * 1. Style - * 2. Make box more robust so it clips the child element - * 3. Vertical alignment - * 4. Remove default style - * 5. Fix black background on iOS - * 6. Center icons - */ - -.uk-radio, -.uk-checkbox { - /* 1 */ - display: inline-block; - height: $form-radio-size; - width: $form-radio-size; - /* 2 */ - overflow: hidden; - /* 3 */ - margin-top: $form-radio-margin-top; - vertical-align: middle; - /* 4 */ - -webkit-appearance: none; - -moz-appearance: none; - /* 5 */ - background-color: $form-radio-background; - /* 6 */ - background-repeat: no-repeat; - background-position: 50% 50%; - @if(mixin-exists(hook-form-radio)) {@include hook-form-radio();} -} - -.uk-radio { border-radius: 50%; } - -/* Focus */ -.uk-radio:focus, -.uk-checkbox:focus { - outline: none; - @if(mixin-exists(hook-form-radio-focus)) {@include hook-form-radio-focus();} -} - -/* - * Checked - */ - -.uk-radio:checked, -.uk-checkbox:checked, -.uk-checkbox:indeterminate { - background-color: $form-radio-checked-background; - @if(mixin-exists(hook-form-radio-checked)) {@include hook-form-radio-checked();} -} - -/* Focus */ -.uk-radio:checked:focus, -.uk-checkbox:checked:focus, -.uk-checkbox:indeterminate:focus { - background-color: $form-radio-checked-focus-background; - @if(mixin-exists(hook-form-radio-checked-focus)) {@include hook-form-radio-checked-focus();} -} - -/* - * Icons - */ - -.uk-radio:checked { @include svg-fill($internal-form-radio-image, "#000", $form-radio-checked-icon-color); } -.uk-checkbox:checked { @include svg-fill($internal-form-checkbox-image, "#000", $form-radio-checked-icon-color); } -.uk-checkbox:indeterminate { @include svg-fill($internal-form-checkbox-indeterminate-image, "#000", $form-radio-checked-icon-color); } - -/* - * Disabled - */ - -.uk-radio:disabled, -.uk-checkbox:disabled { - background-color: $form-radio-disabled-background; - @if(mixin-exists(hook-form-radio-disabled)) {@include hook-form-radio-disabled();} -} - -.uk-radio:disabled:checked { @include svg-fill($internal-form-radio-image, "#000", $form-radio-disabled-icon-color); } -.uk-checkbox:disabled:checked { @include svg-fill($internal-form-checkbox-image, "#000", $form-radio-disabled-icon-color); } -.uk-checkbox:disabled:indeterminate { @include svg-fill($internal-form-checkbox-indeterminate-image, "#000", $form-radio-disabled-icon-color); } - - -/* Legend - ========================================================================== */ - -/* - * Legend - * 1. Behave like block element - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove padding so people aren't caught out if they zero out fieldsets. - * 4. Style - */ - -.uk-legend { - /* 1 */ - width: 100%; - /* 2 */ - color: inherit; - /* 3 */ - padding: 0; - /* 4 */ - font-size: $form-legend-font-size; - line-height: $form-legend-line-height; - @if(mixin-exists(hook-form-legend)) {@include hook-form-legend();} -} - - -/* Custom controls - ========================================================================== */ - -/* - * 1. Container fits its content - * 2. Create position context - * 3. Prevent content overflow - * 4. Behave like most inline-block elements - */ - -.uk-form-custom { - /* 1 */ - display: inline-block; - /* 2 */ - position: relative; - /* 3 */ - max-width: 100%; - /* 4 */ - vertical-align: middle; -} - -/* - * 1. Position and resize the form control to always cover its container - * 2. Required for Firefox for positioning to the left - * 3. Required for Webkit to make `height` work - * 4. Hide controle and show cursor - * 5. Needed for the cursor - * 6. Clip height caused by 5. Needed for Webkit only - */ - -.uk-form-custom select, -.uk-form-custom input[type="file"] { - /* 1 */ - position: absolute; - top: 0; - z-index: 1; - width: 100%; - height: 100%; - /* 2 */ - left: 0; - /* 3 */ - -webkit-appearance: none; - /* 4 */ - opacity: 0; - cursor: pointer; -} - -.uk-form-custom input[type="file"] { - /* 5 */ - font-size: 500px; - /* 6 */ - overflow: hidden; -} - - -/* Label - ========================================================================== */ - -.uk-form-label { - @if(mixin-exists(hook-form-label)) {@include hook-form-label();} -} - - -/* Layout - ========================================================================== */ - -/* - * Stacked - */ - -.uk-form-stacked .uk-form-label { - display: block; - margin-bottom: $form-stacked-margin-bottom; - @if(mixin-exists(hook-form-stacked-label)) {@include hook-form-stacked-label();} -} - -/* - * Horizontal - */ - -/* Tablet portrait and smaller */ -@media (max-width: $breakpoint-small-max) { - - /* Behave like `uk-form-stacked` */ - .uk-form-horizontal .uk-form-label { - display: block; - margin-bottom: $form-stacked-margin-bottom; - @if(mixin-exists(hook-form-stacked-label)) {@include hook-form-stacked-label();} - } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-form-horizontal .uk-form-label { - width: $form-horizontal-label-width; - margin-top: $form-horizontal-label-margin-top; - float: left; - @if(mixin-exists(hook-form-horizontal-label)) {@include hook-form-horizontal-label();} - } - - .uk-form-horizontal .uk-form-controls { margin-left: $form-horizontal-controls-margin-left; } - - /* Better vertical alignment if controls are checkboxes and radio buttons with text */ - .uk-form-horizontal .uk-form-controls-text { padding-top: $form-horizontal-controls-text-padding-top; } - -} - - -/* Icons - ========================================================================== */ - -/* - * 1. Set position - * 2. Set width - * 3. Center icon vertically and horizontally - * 4. Style - */ - -.uk-form-icon { - /* 1 */ - position: absolute; - top: 0; - bottom: 0; - left: 0; - /* 2 */ - width: $form-icon-width; - /* 3 */ - display: inline-flex; - justify-content: center; - align-items: center; - /* 4 */ - color: $form-icon-color; -} - -/* - * Required for `a`. - */ - -.uk-form-icon:hover { color: $form-icon-hover-color; } - -/* - * Make `input` element clickable through icon, e.g. if it's a `span` - */ - -.uk-form-icon:not(a):not(button):not(input) { pointer-events: none; } - -/* - * Input padding - */ - -.uk-form-icon:not(.uk-form-icon-flip) + .uk-input { padding-left: $form-icon-width !important; } - -/* - * Position modifier - */ - -.uk-form-icon-flip { - right: 0; - left: auto; -} - -.uk-form-icon-flip + .uk-input { padding-right: $form-icon-width !important; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-form-misc)) {@include hook-form-misc();} - -// @mixin hook-form(){} -// @mixin hook-form-single-line(){} -// @mixin hook-form-multi-line(){} -// @mixin hook-form-focus(){} -// @mixin hook-form-disabled(){} -// @mixin hook-form-danger(){} -// @mixin hook-form-success(){} -// @mixin hook-form-blank(){} -// @mixin hook-form-blank-focus(){} -// @mixin hook-form-radio(){} -// @mixin hook-form-radio-focus(){} -// @mixin hook-form-radio-checked(){} -// @mixin hook-form-radio-checked-focus(){} -// @mixin hook-form-radio-disabled(){} -// @mixin hook-form-legend(){} -// @mixin hook-form-label(){} -// @mixin hook-form-stacked-label(){} -// @mixin hook-form-horizontal-label(){} -// @mixin hook-form-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-form-background: $inverse-global-muted-background !default; -$inverse-form-color: $inverse-global-color !default; -$inverse-form-focus-background: $inverse-global-muted-background !default; -$inverse-form-focus-color: $inverse-global-color !default; -$inverse-form-placeholder-color: $inverse-global-muted-color !default; - -$inverse-form-select-icon-color: $inverse-global-color !default; - -$inverse-form-radio-background: darken($inverse-global-muted-background, 5%) !default; - -$inverse-form-radio-checked-background: $inverse-global-primary-background !default; -$inverse-form-radio-checked-icon-color: $inverse-global-inverse-color !default; - -$inverse-form-radio-checked-focus-background: darken($inverse-global-primary-background, 10%) !default; - - - -// @mixin hook-inverse-form(){} -// @mixin hook-inverse-form-focus(){} -// @mixin hook-inverse-form-radio(){} -// @mixin hook-inverse-form-radio-focus(){} -// @mixin hook-inverse-form-radio-checked(){} -// @mixin hook-inverse-form-radio-checked-focus(){} -// @mixin hook-inverse-form-label(){} diff --git a/_sass/uikit/components/grid.scss b/_sass/uikit/components/grid.scss deleted file mode 100644 index 455033c72b..0000000000 --- a/_sass/uikit/components/grid.scss +++ /dev/null @@ -1,352 +0,0 @@ -// Name: Grid -// Description: Component to create responsive, fluid and nestable grids -// -// Component: `uk-grid` -// -// Modifiers: `uk-grid-small` -// `uk-grid-medium` -// `uk-grid-large` -// `uk-grid-collapse` -// `uk-grid-divider` -// `uk-grid-match` -// `uk-grid-stack` -// `uk-grid-margin` -// `uk-grid-margin-small` -// `uk-grid-margin-medium` -// `uk-grid-margin-large` -// `uk-grid-margin-collapse` -// -// Sub-modifier: `uk-grid-item-match` -// -// States: `uk-first-column` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$grid-gutter-horizontal: $global-gutter !default; -$grid-gutter-vertical: $grid-gutter-horizontal !default; -$grid-gutter-horizontal-l: $global-medium-gutter !default; -$grid-gutter-vertical-l: $grid-gutter-horizontal-l !default; - -$grid-small-gutter-horizontal: $global-small-gutter !default; -$grid-small-gutter-vertical: $grid-small-gutter-horizontal !default; - -$grid-medium-gutter-horizontal: $global-gutter !default; -$grid-medium-gutter-vertical: $grid-medium-gutter-horizontal !default; - -$grid-large-gutter-horizontal: $global-medium-gutter !default; -$grid-large-gutter-vertical: $grid-large-gutter-horizontal !default; -$grid-large-gutter-horizontal-l: $global-large-gutter !default; -$grid-large-gutter-vertical-l: $grid-large-gutter-horizontal-l !default; - -$grid-divider-border-width: $global-border-width !default; -$grid-divider-border: $global-border !default; - - -/* ======================================================================== - Component: Grid - ========================================================================== */ - -/* - * 1. Allow cells to wrap into the next line - * 2. Reset list - */ - -.uk-grid { - display: flex; - /* 1 */ - flex-wrap: wrap; - /* 2 */ - margin: 0; - padding: 0; - list-style: none; -} - -/* - * Grid cell - * Note: Space is allocated solely based on content dimensions, but shrinks: 0 1 auto - * Reset margin for e.g. paragraphs - */ - -.uk-grid > * { margin: 0; } - -/* - * Remove margin from the last-child - */ - -.uk-grid > * > :last-child { margin-bottom: 0; } - - -/* Gutter - ========================================================================== */ - -/* - * Default - */ - -/* Horizontal */ -.uk-grid { margin-left: (-$grid-gutter-horizontal); } -.uk-grid > * { padding-left: $grid-gutter-horizontal; } - -/* Vertical */ -.uk-grid + .uk-grid, -.uk-grid > .uk-grid-margin, -* + .uk-grid-margin { margin-top: $grid-gutter-vertical; } - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - /* Horizontal */ - .uk-grid { margin-left: (-$grid-gutter-horizontal-l); } - .uk-grid > * { padding-left: $grid-gutter-horizontal-l; } - - /* Vertical */ - .uk-grid + .uk-grid, - .uk-grid > .uk-grid-margin, - * + .uk-grid-margin { margin-top: $grid-gutter-vertical-l; } - -} - -/* - * Small - */ - -/* Horizontal */ -.uk-grid-small { margin-left: (-$grid-small-gutter-horizontal); } -.uk-grid-small > * { padding-left: $grid-small-gutter-horizontal; } - -/* Vertical */ -.uk-grid + .uk-grid-small, -.uk-grid-small > .uk-grid-margin, -* + .uk-grid-margin-small { margin-top: $grid-small-gutter-vertical; } - -/* - * Medium - */ - -/* Horizontal */ -.uk-grid-medium { margin-left: (-$grid-medium-gutter-horizontal); } -.uk-grid-medium > * { padding-left: $grid-medium-gutter-horizontal; } - -/* Vertical */ -.uk-grid + .uk-grid-medium, -.uk-grid-medium > .uk-grid-margin, -* + .uk-grid-margin-medium { margin-top: $grid-medium-gutter-vertical; } - -/* - * Large - */ - -/* Horizontal */ -.uk-grid-large { margin-left: (-$grid-large-gutter-horizontal); } -.uk-grid-large > * { padding-left: $grid-large-gutter-horizontal; } - -/* Vertical */ -.uk-grid + .uk-grid-large, -.uk-grid-large > .uk-grid-margin, -* + .uk-grid-margin-large { margin-top: $grid-large-gutter-vertical; } - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - /* Horizontal */ - .uk-grid-large { margin-left: (-$grid-large-gutter-horizontal-l); } - .uk-grid-large > * { padding-left: $grid-large-gutter-horizontal-l; } - - /* Vertical */ - .uk-grid + .uk-grid-large, - .uk-grid-large > .uk-grid-margin, - * + .uk-grid-margin-large { margin-top: $grid-large-gutter-vertical-l; } - -} - -/* - * Collapse - */ - -/* Horizontal */ -.uk-grid-collapse { margin-left: 0; } -.uk-grid-collapse > * { padding-left: 0; } - -/* Vertical */ -.uk-grid + .uk-grid-collapse, -.uk-grid-collapse > .uk-grid-margin { margin-top: 0; } - - -/* Divider - ========================================================================== */ - -.uk-grid-divider > * { position: relative; } - -.uk-grid-divider > :not(.uk-first-column)::before { - content: ""; - position: absolute; - top: 0; - bottom: 0; - border-left: $grid-divider-border-width solid $grid-divider-border; -} - -/* Vertical */ -.uk-grid-divider.uk-grid-stack > .uk-grid-margin::before { - content: ""; - position: absolute; - left: 0; - right: 0; - border-top: $grid-divider-border-width solid $grid-divider-border; -} - -/* - * Default - */ - -/* Horizontal */ -.uk-grid-divider { margin-left: -($grid-gutter-horizontal * 2); } -.uk-grid-divider > * { padding-left: ($grid-gutter-horizontal * 2); } - -.uk-grid-divider > :not(.uk-first-column)::before { left: $grid-gutter-horizontal; } - -/* Vertical */ -.uk-grid-divider.uk-grid-stack > .uk-grid-margin { margin-top: ($grid-gutter-vertical * 2); } - -.uk-grid-divider.uk-grid-stack > .uk-grid-margin::before { - top: (-$grid-gutter-vertical); - left: ($grid-gutter-horizontal * 2); -} - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - /* Horizontal */ - .uk-grid-divider { margin-left: -($grid-gutter-horizontal-l * 2); } - .uk-grid-divider > * { padding-left: ($grid-gutter-horizontal-l * 2); } - - .uk-grid-divider > :not(.uk-first-column)::before { left: $grid-gutter-horizontal-l; } - - /* Vertical */ - .uk-grid-divider.uk-grid-stack > .uk-grid-margin { margin-top: ($grid-gutter-vertical-l * 2); } - - .uk-grid-divider.uk-grid-stack > .uk-grid-margin::before { - top: (-$grid-gutter-vertical-l); - left: ($grid-gutter-horizontal-l * 2); - } - -} - -/* - * Small - */ - -/* Horizontal */ -.uk-grid-divider.uk-grid-small { margin-left: -($grid-small-gutter-horizontal * 2); } -.uk-grid-divider.uk-grid-small > * { padding-left: ($grid-small-gutter-horizontal * 2); } - -.uk-grid-divider.uk-grid-small > :not(.uk-first-column)::before { left: $grid-small-gutter-horizontal; } - -/* Vertical */ -.uk-grid-divider.uk-grid-small.uk-grid-stack > .uk-grid-margin { margin-top: ($grid-small-gutter-vertical * 2); } - -.uk-grid-divider.uk-grid-small.uk-grid-stack > .uk-grid-margin::before { - top: (-$grid-small-gutter-vertical); - left: ($grid-small-gutter-horizontal * 2); -} - -/* - * Medium - */ - -/* Horizontal */ -.uk-grid-divider.uk-grid-medium { margin-left: -($grid-medium-gutter-horizontal * 2); } -.uk-grid-divider.uk-grid-medium > * { padding-left: ($grid-medium-gutter-horizontal * 2); } - -.uk-grid-divider.uk-grid-medium > :not(.uk-first-column)::before { left: $grid-medium-gutter-horizontal; } - -/* Vertical */ -.uk-grid-divider.uk-grid-medium.uk-grid-stack > .uk-grid-margin { margin-top: ($grid-medium-gutter-vertical * 2); } - -.uk-grid-divider.uk-grid-medium.uk-grid-stack > .uk-grid-margin::before { - top: (-$grid-medium-gutter-vertical); - left: ($grid-medium-gutter-horizontal * 2); -} - -/* - * Large - */ - -/* Horizontal */ -.uk-grid-divider.uk-grid-large { margin-left: -($grid-large-gutter-horizontal * 2); } -.uk-grid-divider.uk-grid-large > * { padding-left: ($grid-large-gutter-horizontal * 2); } - -.uk-grid-divider.uk-grid-large > :not(.uk-first-column)::before { left: $grid-large-gutter-horizontal; } - -/* Vertical */ -.uk-grid-divider.uk-grid-large.uk-grid-stack > .uk-grid-margin { margin-top: ($grid-large-gutter-vertical * 2); } - -.uk-grid-divider.uk-grid-large.uk-grid-stack > .uk-grid-margin::before { - top: (-$grid-large-gutter-vertical); - left: ($grid-large-gutter-horizontal * 2); -} - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - /* Horizontal */ - .uk-grid-divider.uk-grid-large { margin-left: -($grid-large-gutter-horizontal-l * 2); } - .uk-grid-divider.uk-grid-large > * { padding-left: ($grid-large-gutter-horizontal-l * 2); } - - .uk-grid-divider.uk-grid-large > :not(.uk-first-column)::before { left: $grid-large-gutter-horizontal-l; } - - /* Vertical */ - .uk-grid-divider.uk-grid-large.uk-grid-stack > .uk-grid-margin { margin-top: ($grid-large-gutter-vertical-l * 2); } - - .uk-grid-divider.uk-grid-large.uk-grid-stack > .uk-grid-margin::before { - top: (-$grid-large-gutter-vertical-l); - left: ($grid-large-gutter-horizontal-l * 2); - } - -} - - -/* Match child of a grid cell - ========================================================================== */ - -/* - * Behave like a block element - * 1. Wrap into the next line - * 2. Take the full width, at least 100%. Only if no class from the Width component is set. - * 3. Expand width even if larger than 100%, e.g. because of negative margin (Needed for nested grids) - */ - -.uk-grid-match > *, -.uk-grid-item-match { - display: flex; - /* 1 */ - flex-wrap: wrap; -} - -.uk-grid-match > * > :not([class*='uk-width']), -.uk-grid-item-match > :not([class*='uk-width']) { - /* 2 */ - box-sizing: border-box; - width: 100%; - /* 3 */ - flex: auto; -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-grid-misc)) {@include hook-grid-misc();} - -// @mixin hook-grid-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-grid-divider-border: $inverse-global-border !default; - - diff --git a/_sass/uikit/components/heading.scss b/_sass/uikit/components/heading.scss deleted file mode 100644 index 97f1c18517..0000000000 --- a/_sass/uikit/components/heading.scss +++ /dev/null @@ -1,214 +0,0 @@ -// Name: Heading -// Description: Styles for headings -// -// Component: `uk-heading-primary` -// `uk-heading-hero` -// `uk-heading-divider` -// `uk-heading-bullet` -// `uk-heading-line` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$heading-primary-font-size: $global-xxlarge-font-size !default; -$heading-primary-line-height: 1.2 !default; - -$heading-primary-font-size-m: 3.75rem !default; // 54px -$heading-primary-line-height-m: 1.1 !default; - -$heading-hero-font-size: 4rem !default; // 64px -$heading-hero-line-height: 1.1 !default; - -$heading-hero-font-size-s: 6rem !default; // 96px -$heading-hero-line-height-s: 1 !default; - -$heading-hero-font-size-m: 8rem !default; // 128px -$heading-hero-line-height-m: 1 !default; - -$heading-divider-padding-bottom: 10px !default; -$heading-divider-border-width: $global-border-width !default; -$heading-divider-border: $global-border !default; - -$heading-bullet-top: unquote('calc(-0.1 * 1em)') !default; -$heading-bullet-height: 0.9em !default; -$heading-bullet-margin-right: 10px !default; -$heading-bullet-border-width: 5px !default; -$heading-bullet-border: $global-border !default; - -$heading-line-top: 50% !default; -$heading-line-height: $heading-line-border-width !default; -$heading-line-width: 2000px !default; -$heading-line-border-width: $global-border-width !default; -$heading-line-border: $global-border !default; -$heading-line-margin-horizontal: 0.6em !default; - - -/* ======================================================================== - Component: Heading - ========================================================================== */ - - -/* Primary - ========================================================================== */ - -.uk-heading-primary { - font-size: $heading-primary-font-size; - line-height: $heading-primary-line-height; - @if(mixin-exists(hook-heading-primary)) {@include hook-heading-primary();} -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-heading-primary { - font-size: $heading-primary-font-size-m; - line-height: $heading-primary-line-height-m; - } - -} - - -/* Hero - ========================================================================== */ - -.uk-heading-hero { - font-size: $heading-hero-font-size; - line-height: $heading-hero-line-height; - @if(mixin-exists(hook-heading-hero)) {@include hook-heading-hero();} -} - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - .uk-heading-hero { - font-size: $heading-hero-font-size-s; - line-height: $heading-hero-line-height-s; - } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-heading-hero { - font-size: $heading-hero-font-size-m; - line-height: $heading-hero-line-height-m; - } - -} - - -/* Divider - ========================================================================== */ - -.uk-heading-divider { - padding-bottom: $heading-divider-padding-bottom; - border-bottom: $heading-divider-border-width solid $heading-divider-border; - @if(mixin-exists(hook-heading-divider)) {@include hook-heading-divider();} -} - - -/* Bullet - ========================================================================== */ - -.uk-heading-bullet { position: relative; } - -/* - * 1. Using `inline-block` to make it work with text alignment - * 2. Center vertically - * 3. Style - */ - -.uk-heading-bullet::before { - content: ""; - /* 1 */ - display: inline-block; - /* 2 */ - position: relative; - top: $heading-bullet-top; - vertical-align: middle; - /* 3 */ - height: $heading-bullet-height; - margin-right: $heading-bullet-margin-right; - border-left: $heading-bullet-border-width solid $heading-bullet-border; - @if(mixin-exists(hook-heading-bullet)) {@include hook-heading-bullet();} -} - - -/* Line - ========================================================================== */ - -/* - * Clip the child element - */ - -.uk-heading-line { overflow: hidden; } - -/* - * Extra markup is needed to make it work with text align - */ - -.uk-heading-line > * { - display: inline-block; - position: relative; -} - -/* - * 1. Center vertically - * 2. Make the element as large as possible. It's clipped by the container. - * 3. Style - */ - -.uk-heading-line > ::before, -.uk-heading-line > ::after { - content: ""; - /* 1 */ - position: absolute; - top: unquote('calc(#{$heading-line-top} - (#{$heading-line-height} / 2))'); - /* 2 */ - width: $heading-line-width; - /* 3 */ - border-bottom: $heading-line-border-width solid $heading-line-border; - @if(mixin-exists(hook-heading-line)) {@include hook-heading-line();} -} - -.uk-heading-line > ::before { - right: 100%; - margin-right: $heading-line-margin-horizontal; -} -.uk-heading-line > ::after { - left: 100%; - margin-left: $heading-line-margin-horizontal; -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-heading-misc)) {@include hook-heading-misc();} - -// @mixin hook-heading-primary(){} -// @mixin hook-heading-hero(){} -// @mixin hook-heading-divider(){} -// @mixin hook-heading-bullet(){} -// @mixin hook-heading-line(){} -// @mixin hook-heading-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-heading-divider-border: $inverse-global-border !default; -$inverse-heading-bullet-border: $inverse-global-border !default; -$inverse-heading-line-border: $inverse-global-border !default; - - - -// @mixin hook-inverse-heading-primary(){} -// @mixin hook-inverse-heading-hero(){} -// @mixin hook-inverse-heading-divider(){} -// @mixin hook-inverse-heading-bullet(){} -// @mixin hook-inverse-heading-line(){} diff --git a/_sass/uikit/components/icon.scss b/_sass/uikit/components/icon.scss deleted file mode 100644 index 2ff70ecf4f..0000000000 --- a/_sass/uikit/components/icon.scss +++ /dev/null @@ -1,223 +0,0 @@ -// Name: Icon -// Description: Component to create icons -// -// Component: `uk-icon` -// -// Modifiers: `uk-icon-image` -// `uk-icon-link` -// `uk-icon-button` -// -// States: `uk-preserve` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$icon-image-size: 20px !default; - -$icon-link-color: $global-muted-color !default; -$icon-link-hover-color: $global-color !default; -$icon-link-active-color: darken($global-color, 5%) !default; - -$icon-button-size: 36px !default; -$icon-button-border-radius: 500px !default; -$icon-button-background: $global-muted-background !default; -$icon-button-color: $global-muted-color !default; - -$icon-button-hover-background: darken($icon-button-background, 5%) !default; -$icon-button-hover-color: $global-color !default; - -$icon-button-active-background: darken($icon-button-background, 10%) !default; -$icon-button-active-color: $global-color !default; - - -/* ======================================================================== - Component: Icon - ========================================================================== */ - -/* - * Note: 1. - 7. is required for `button` elements. Needed for Close and Form Icon component. - * 1. Remove margins in Chrome, Safari and Opera. - * 2. Remove borders for `button`. - * 3. Remove border-radius in Chrome. - * 4. Address `overflow` set to `hidden` in IE. - * 5. Correct `font` properties and `color` not being inherited for `button`. - * 6. Remove the inheritance of text transform in Edge, Firefox, and IE. - * 7. Remove default `button` padding and background color - * 8. Style - * 9. Fill all SVG elements with the current text color if no `fill` attribute is set - * 10. Let the container fit the height of the icon - */ - -.uk-icon { - /* 1 */ - margin: 0; - /* 2 */ - border: none; - /* 3 */ - border-radius: 0; - /* 4 */ - overflow: visible; - /* 5 */ - font: inherit; - color: inherit; - /* 6 */ - text-transform: none; - /* 7. */ - padding: 0; - background-color: transparent; - /* 8 */ - display: inline-block; - /* 9 */ - fill: currentcolor; - /* 10 */ - line-height: 0; -} - -/* Required for `button`. */ -button.uk-icon:not(:disabled) { cursor: pointer; } - -/* - * Remove the inner border and padding in Firefox. - */ - -.uk-icon::-moz-focus-inner { - border: 0; - padding: 0; -} - -/* - * Set the fill and stroke color of all SVG elements to the current text color - * 1. Fix for uppercase attribute names in Edge. Will be fixed in Windows 10 builds 16251+ - */ - -.uk-icon [fill*='#']:not(.uk-preserve), -.uk-icon [FILL*='#']:not(.uk-preserve) { fill: currentcolor; } // 1 -.uk-icon [stroke*='#']:not(.uk-preserve), -.uk-icon [STROKE*='#']:not(.uk-preserve) { stroke: currentcolor; } // 1 - -/* - * Fix Firefox blurry SVG rendering: https://bugzilla.mozilla.org/show_bug.cgi?id=1046835 - */ - -.uk-icon > * { transform: translate(0,0); } - - -/* Image modifier - ========================================================================== */ - -/* - * Display images in icon dimensions - */ - -.uk-icon-image { - width: $icon-image-size; - height: $icon-image-size; - background-position: 50% 50%; - background-repeat: no-repeat; - background-size: contain; - vertical-align: middle; -} - - -/* Style modifiers - ========================================================================== */ - -/* - * Link - */ - -.uk-icon-link { - color: $icon-link-color; - @if(mixin-exists(hook-icon-link)) {@include hook-icon-link();} -} - -.uk-icon-link:hover, -.uk-icon-link:focus { - color: $icon-link-hover-color; - outline: none; - @if(mixin-exists(hook-icon-link-hover)) {@include hook-icon-link-hover();} -} - -/* OnClick + Active */ -.uk-icon-link:active, -.uk-active > .uk-icon-link { - color: $icon-link-active-color; - @if(mixin-exists(hook-icon-link-active)) {@include hook-icon-link-active();} -} - -/* - * Button - * 1. Center icon vertically and horizontally - */ - -.uk-icon-button { - box-sizing: border-box; - width: $icon-button-size; - height: $icon-button-size; - border-radius: $icon-button-border-radius; - background: $icon-button-background; - color: $icon-button-color; - vertical-align: middle; - /* 1 */ - display: inline-flex; - justify-content: center; - align-items: center; - @if(mixin-exists(hook-icon-button)) {@include hook-icon-button();} -} - -/* Hover + Focus */ -.uk-icon-button:hover, -.uk-icon-button:focus { - background-color: $icon-button-hover-background; - color: $icon-button-hover-color; - outline: none; - @if(mixin-exists(hook-icon-button-hover)) {@include hook-icon-button-hover();} -} - -/* OnClick + Active */ -.uk-icon-button:active, -.uk-active > .uk-icon-button { - background-color: $icon-button-active-background; - color: $icon-button-active-color; - @if(mixin-exists(hook-icon-button-active)) {@include hook-icon-button-active();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-icon-misc)) {@include hook-icon-misc();} - -// @mixin hook-icon-link(){} -// @mixin hook-icon-link-hover(){} -// @mixin hook-icon-link-active(){} -// @mixin hook-icon-button(){} -// @mixin hook-icon-button-hover(){} -// @mixin hook-icon-button-active(){} -// @mixin hook-icon-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-icon-link-color: $inverse-global-muted-color !default; -$inverse-icon-link-hover-color: $inverse-global-color !default; -$inverse-icon-link-active-color: $inverse-global-color !default; -$inverse-icon-button-background: $inverse-global-muted-background !default; -$inverse-icon-button-color: $inverse-global-muted-color !default; -$inverse-icon-button-hover-background: darken($inverse-icon-button-background, 5%) !default; -$inverse-icon-button-hover-color: $inverse-global-color !default; -$inverse-icon-button-active-background: darken($inverse-icon-button-background, 10%) !default; -$inverse-icon-button-active-color: $inverse-global-color !default; - - - -// @mixin hook-inverse-icon-link(){} -// @mixin hook-inverse-icon-link-hover(){} -// @mixin hook-inverse-icon-link-active(){} -// @mixin hook-inverse-icon-button(){} -// @mixin hook-inverse-icon-button-hover(){} -// @mixin hook-inverse-icon-button-active(){} diff --git a/_sass/uikit/components/iconnav.scss b/_sass/uikit/components/iconnav.scss deleted file mode 100644 index ae739c9042..0000000000 --- a/_sass/uikit/components/iconnav.scss +++ /dev/null @@ -1,140 +0,0 @@ -// Name: Iconnav -// Description: Component to create icon navigations -// -// Component: `uk-iconnav` -// -// Modifier: `uk-iconnav-vertical` -// -// States: `uk-active` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$iconnav-margin-horizontal: $global-small-margin !default; -$iconnav-margin-vertical: $iconnav-margin-horizontal !default; - -$iconnav-item-color: $global-muted-color !default; - -$iconnav-item-hover-color: $global-color !default; - -$iconnav-item-active-color: $global-color !default; - - -/* ======================================================================== - Component: Iconnav - ========================================================================== */ - -/* - * 1. Allow items to wrap into the next line - * 2. Reset list - * 3. Gutter - */ - -.uk-iconnav { - display: flex; - /* 1 */ - flex-wrap: wrap; - /* 2 */ - margin: 0; - padding: 0; - list-style: none; - /* 3 */ - margin-left: (-$iconnav-margin-horizontal); - @if(mixin-exists(hook-iconnav)) {@include hook-iconnav();} -} - -/* - * 1. Space is allocated solely based on content dimensions: 0 0 auto - * 2. Gutter - */ - -.uk-iconnav > * { - /* 1 */ - flex: none; - /* 2 */ - padding-left: $iconnav-margin-horizontal; -} - - -/* Items - ========================================================================== */ - -/* - * Items must target `a` elements to exclude other elements (e.g. dropdowns) - * 1. Prevent gap if child element is `inline-block`, e.g. an icon - * 2. Style - */ - -.uk-iconnav > * > a { - /* 1 */ - display: block; - /* 2 */ - color: $iconnav-item-color; - @if(mixin-exists(hook-iconnav-item)) {@include hook-iconnav-item();} -} - -/* Hover + Focus */ -.uk-iconnav > * > a:hover, -.uk-iconnav > * > a:focus { - color: $iconnav-item-hover-color; - outline: none; - @if(mixin-exists(hook-iconnav-item-hover)) {@include hook-iconnav-item-hover();} -} - -/* Active */ -.uk-iconnav > .uk-active > a { - color: $iconnav-item-active-color; - @if(mixin-exists(hook-iconnav-item-active)) {@include hook-iconnav-item-active();} -} - - -/* Modifier: 'uk-iconnav-vertical' - ========================================================================== */ - -/* - * 1. Change direction - * 2. Gutter - */ - -.uk-iconnav-vertical { - /* 1 */ - flex-direction: column; - /* 2 */ - margin-left: 0; - margin-top: (-$iconnav-margin-vertical); -} - -/* 2 */ -.uk-iconnav-vertical > * { - padding-left: 0; - padding-top: $iconnav-margin-vertical; -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-iconnav-misc)) {@include hook-iconnav-misc();} - -// @mixin hook-iconnav(){} -// @mixin hook-iconnav-item(){} -// @mixin hook-iconnav-item-hover(){} -// @mixin hook-iconnav-item-active(){} -// @mixin hook-iconnav-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-iconnav-item-color: $inverse-global-muted-color !default; -$inverse-iconnav-item-hover-color: $inverse-global-color !default; -$inverse-iconnav-item-active-color: $inverse-global-color !default; - - - -// @mixin hook-inverse-iconnav-item(){} -// @mixin hook-inverse-iconnav-item-hover(){} -// @mixin hook-inverse-iconnav-item-active(){} \ No newline at end of file diff --git a/_sass/uikit/components/inverse.scss b/_sass/uikit/components/inverse.scss deleted file mode 100644 index c1b0c07c78..0000000000 --- a/_sass/uikit/components/inverse.scss +++ /dev/null @@ -1,46 +0,0 @@ -// Name: Inverse -// Description: Inverse component style for light or dark backgrounds -// -// Component: `uk-light` -// `uk-dark` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$inverse-global-color-mode: light !default; - -$inverse-global-color: rgba($global-inverse-color, 0.7) !default; -$inverse-global-emphasis-color: $global-inverse-color !default; -$inverse-global-muted-color: rgba($global-inverse-color, 0.5) !default; -$inverse-global-inverse-color: $global-color !default; - -$inverse-global-primary-background: $global-inverse-color !default; -$inverse-global-muted-background: rgba($global-inverse-color, 0.1) !default; - -$inverse-global-border: rgba($global-inverse-color, 0.2) !default; - - -/* ======================================================================== - Component: Inverse - ========================================================================== */ - - - -/* - * Implemented class depends on the general theme color - * `uk-light` is for light colors on dark backgrounds - * `uk-dark` is or dark colors on light backgrounds - */ - -@if ($inverse-global-color-mode == light) { .uk-light { @if(mixin-exists(hook-inverse)) {@include hook-inverse();}}} - -@if ($inverse-global-color-mode == dark) { .uk-dark { @if(mixin-exists(hook-inverse)) {@include hook-inverse();}}} - - -// Hooks -// ======================================================================== - -// @mixin hook-inverse(){} \ No newline at end of file diff --git a/_sass/uikit/components/label.scss b/_sass/uikit/components/label.scss deleted file mode 100644 index 6600aedfab..0000000000 --- a/_sass/uikit/components/label.scss +++ /dev/null @@ -1,102 +0,0 @@ -// Name: Label -// Description: Component to indicate important notes -// -// Component: `uk-label` -// -// Modifiers: `uk-label-success` -// `uk-label-warning` -// `uk-label-danger` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$label-padding-vertical: 0 !default; -$label-padding-horizontal: $global-small-margin !default; -$label-background: $global-primary-background !default; -$label-line-height: $global-line-height !default; -$label-font-size: $global-small-font-size !default; -$label-color: $global-inverse-color !default; - -$label-success-background: $global-success-background !default; -$label-success-color: $global-inverse-color !default; -$label-warning-background: $global-warning-background !default; -$label-warning-color: $global-inverse-color !default; -$label-danger-background: $global-danger-background !default; -$label-danger-color: $global-inverse-color !default; - - -/* ======================================================================== - Component: Label - ========================================================================== */ - -.uk-label { - display: inline-block; - padding: $label-padding-vertical $label-padding-horizontal; - background: $label-background; - line-height: $label-line-height; - font-size: $label-font-size; - color: $label-color; - vertical-align: middle; - white-space: nowrap; - @if(mixin-exists(hook-label)) {@include hook-label();} -} - - -/* Color modifiers - ========================================================================== */ - -/* - * Success - */ - -.uk-label-success { - background-color: $label-success-background; - color: $label-success-color; - @if(mixin-exists(hook-label-success)) {@include hook-label-success();} -} - -/* - * Warning - */ - -.uk-label-warning { - background-color: $label-warning-background; - color: $label-warning-color; - @if(mixin-exists(hook-label-warning)) {@include hook-label-warning();} -} - -/* - * Danger - */ - -.uk-label-danger { - background-color: $label-danger-background; - color: $label-danger-color; - @if(mixin-exists(hook-label-danger)) {@include hook-label-danger();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-label-misc)) {@include hook-label-misc();} - -// @mixin hook-label(){} -// @mixin hook-label-success(){} -// @mixin hook-label-warning(){} -// @mixin hook-label-danger(){} -// @mixin hook-label-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-label-background: $inverse-global-primary-background !default; -$inverse-label-color: $inverse-global-inverse-color !default; - - - -// @mixin hook-inverse-label(){} \ No newline at end of file diff --git a/_sass/uikit/components/lightbox.scss b/_sass/uikit/components/lightbox.scss deleted file mode 100644 index 4f9c698df8..0000000000 --- a/_sass/uikit/components/lightbox.scss +++ /dev/null @@ -1,232 +0,0 @@ -// Name: Lightbox -// Description: Component to create an lightbox image gallery -// -// Component: `uk-lightbox` -// -// Sub-objects: `uk-lightbox-page` -// `uk-lightbox-items` -// `uk-lightbox-toolbar` -// `uk-lightbox-toolbar-icon` -// `uk-lightbox-button` -// `uk-lightbox-caption` -// `uk-lightbox-iframe` -// -// States: `uk-open` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$lightbox-z-index: $global-z-index + 10 !default; -$lightbox-background: #000 !default; - -$lightbox-item-color: rgba(255,255,255,0.7) !default; - -$lightbox-toolbar-padding-vertical: 10px !default; -$lightbox-toolbar-padding-horizontal: 10px !default; -$lightbox-toolbar-background: rgba(0,0,0,0.3) !default; -$lightbox-toolbar-color: rgba(255,255,255,0.7) !default; - -$lightbox-toolbar-icon-padding: 5px !default; -$lightbox-toolbar-icon-color: rgba(255,255,255,0.7) !default; - -$lightbox-toolbar-icon-hover-color: #fff !default; - -$lightbox-button-size: 50px !default; -$lightbox-button-background: $lightbox-toolbar-background !default; -$lightbox-button-color: rgba(255,255,255,0.7) !default; - -$lightbox-button-hover-color: #fff !default; - - -/* ======================================================================== - Component: Lightbox - ========================================================================== */ - -/* - * 1. Hide by default - * 2. Set position - * 3. Allow scrolling for the modal dialog - * 4. Horizontal padding - * 5. Mask the background page - * 6. Fade-in transition - */ - -.uk-lightbox { - /* 1 */ - display: none; - /* 2 */ - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: $lightbox-z-index; - /* 5 */ - background: $lightbox-background; - /* 6 */ - opacity: 0; - transition: opacity 0.15s linear; - @if(mixin-exists(hook-lightbox)) {@include hook-lightbox();} -} - -/* - * Open - * 1. Center child - * 2. Fade-in - */ - -.uk-lightbox.uk-open { - display: block; - /* 2 */ - opacity: 1; -} - - -/* Page - ========================================================================== */ - -/* - * Prevent scrollbars - */ - -.uk-lightbox-page { overflow: hidden; } - - -/* Item - ========================================================================== */ - -/* - * 1. Center child within the viewport - * 2. Not visible by default - * 3. Color needed for spinner icon - * 4. Optimize animation - * 5. Responsiveness - * Using `vh` for `max-height` to fix image proportions after resize in Safari and Opera - * Using `vh` and `vw` to make responsive image work in IE11 - */ - -.uk-lightbox-items > * { - /* 1 */ - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - /* 2 */ - display: none; - justify-content: center; - align-items: center; - /* 3 */ - color: $lightbox-item-color; - /* 4 */ - will-change: transform, opacity; - @if(mixin-exists(hook-lightbox-item)) {@include hook-lightbox-item();} -} - -/* 5 */ -.uk-lightbox-items > * > * { - max-width: 100vw; - max-height: 100vh; -} - -.uk-lightbox-items > * > :not(iframe) { - width: auto; - height: auto; -} - -.uk-lightbox-items > .uk-active { display: flex; } - -/* Toolbar - ========================================================================== */ - -.uk-lightbox-toolbar { - padding: $lightbox-toolbar-padding-vertical $lightbox-toolbar-padding-horizontal; - background: $lightbox-toolbar-background; - color: $lightbox-toolbar-color; - @if(mixin-exists(hook-lightbox-toolbar)) {@include hook-lightbox-toolbar();} -} - -.uk-lightbox-toolbar * { color: $lightbox-toolbar-color; } - - -/* Toolbar Icon (Close) - ========================================================================== */ - -.uk-lightbox-toolbar-icon { - padding: $lightbox-toolbar-icon-padding; - color: $lightbox-toolbar-icon-color; - @if(mixin-exists(hook-lightbox-toolbar-icon)) {@include hook-lightbox-toolbar-icon();} -} - -/* - * Hover - */ - -.uk-lightbox-toolbar-icon:hover { - color: $lightbox-toolbar-icon-hover-color; - @if(mixin-exists(hook-lightbox-toolbar-icon-hover)) {@include hook-lightbox-toolbar-icon-hover();} -} - - - -/* Button (Slidenav) - ========================================================================== */ - -/* - * 1. Center icon vertically and horizontally - */ - -.uk-lightbox-button { - box-sizing: border-box; - width: $lightbox-button-size; - height: $lightbox-button-size; - background: $lightbox-button-background; - color: $lightbox-button-color; - /* 1 */ - display: inline-flex; - justify-content: center; - align-items: center; - @if(mixin-exists(hook-lightbox-button)) {@include hook-lightbox-button();} -} - -/* - * Hover - */ - -.uk-lightbox-button:hover { - color: $lightbox-button-hover-color; - @if(mixin-exists(hook-lightbox-button-hover)) {@include hook-lightbox-button-hover();} -} - - -/* Caption - ========================================================================== */ - -.uk-lightbox-caption {} - - -/* Iframe - ========================================================================== */ - -.uk-lightbox-iframe { - width: 80%; - height: 80%; -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-lightbox-misc)) {@include hook-lightbox-misc();} - -// @mixin hook-lightbox(){} -// @mixin hook-lightbox-item(){} -// @mixin hook-lightbox-toolbar(){} -// @mixin hook-lightbox-toolbar-icon(){} -// @mixin hook-lightbox-toolbar-icon-hover(){} -// @mixin hook-lightbox-button(){} -// @mixin hook-lightbox-button-hover(){} -// @mixin hook-lightbox-misc(){} diff --git a/_sass/uikit/components/link.scss b/_sass/uikit/components/link.scss deleted file mode 100644 index 9e0ef0e747..0000000000 --- a/_sass/uikit/components/link.scss +++ /dev/null @@ -1,123 +0,0 @@ -// Name: Link -// Description: Styles for links -// -// Component: `uk-link-muted` -// `uk-link-text` -// `uk-link-heading` -// `uk-link-reset` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$link-muted-color: $global-muted-color !default; -$link-muted-hover-color: $global-color !default; - -$link-text-hover-color: $global-muted-color !default; - -$link-heading-hover-color: $global-primary-background !default; -$link-heading-hover-text-decoration: none !default; - - -/* ======================================================================== - Component: Link - ========================================================================== */ - - -/* Muted - ========================================================================== */ - -a.uk-link-muted, -.uk-link-muted a { - color: $link-muted-color; - @if(mixin-exists(hook-link-muted)) {@include hook-link-muted();} -} - -a.uk-link-muted:hover, -.uk-link-muted a:hover { - color: $link-muted-hover-color; - @if(mixin-exists(hook-link-muted-hover)) {@include hook-link-muted-hover();} -} - - -/* Text - ========================================================================== */ - -a.uk-link-text:not(:hover), -.uk-link-text a:not(:hover) { - color: inherit; - @if(mixin-exists(hook-link-text)) {@include hook-link-text();} -} - -a.uk-link-text:hover, -.uk-link-text a:hover { - color: $link-text-hover-color; - @if(mixin-exists(hook-link-text-hover)) {@include hook-link-text-hover();} -} - - -/* Heading - ========================================================================== */ - -a.uk-link-heading:not(:hover), -.uk-link-heading a:not(:hover) { - color: inherit; - @if(mixin-exists(hook-link-heading)) {@include hook-link-heading();} -} - -a.uk-link-heading:hover, -.uk-link-heading a:hover { - color: $link-heading-hover-color; - text-decoration: $link-heading-hover-text-decoration; - @if(mixin-exists(hook-link-heading-hover)) {@include hook-link-heading-hover();} -} - - -/* Reset - ========================================================================== */ - -/* - * `!important` needed to override inverse component - */ - -a.uk-link-reset, -a.uk-link-reset:hover, -.uk-link-reset a, -.uk-link-reset a:hover { - color: inherit !important; - text-decoration: none !important; - @if(mixin-exists(hook-link-reset)) {@include hook-link-reset();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-link-misc)) {@include hook-link-misc();} - -// @mixin hook-link-muted(){} -// @mixin hook-link-muted-hover(){} -// @mixin hook-link-text(){} -// @mixin hook-link-text-hover(){} -// @mixin hook-link-heading(){} -// @mixin hook-link-heading-hover(){} -// @mixin hook-link-reset(){} -// @mixin hook-link-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-link-muted-color: $inverse-global-muted-color !default; -$inverse-link-muted-hover-color: $inverse-global-color !default; -$inverse-link-text-hover-color: $inverse-global-muted-color !default; -$inverse-link-heading-hover-color: $inverse-global-primary-background !default; - - - -// @mixin hook-inverse-link-muted(){} -// @mixin hook-inverse-link-muted-hover(){} -// @mixin hook-inverse-link-text-hover(){} -// @mixin hook-inverse-link-heading-hover(){} diff --git a/_sass/uikit/components/list.scss b/_sass/uikit/components/list.scss deleted file mode 100644 index ed810bb010..0000000000 --- a/_sass/uikit/components/list.scss +++ /dev/null @@ -1,187 +0,0 @@ -// Name: List -// Description: Styles for lists -// -// Component: `uk-list` -// -// Modifiers: `uk-list-divider` -// `uk-list-striped` -// `uk-list-bullet` -// `uk-list-large` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$list-margin-top: $global-small-margin !default; - -$list-nested-padding-left: $global-gutter !default; - -$list-divider-margin-top: $global-small-margin !default; -$list-divider-border-width: $global-border-width !default; -$list-divider-border: $global-border !default; - -$list-striped-padding-vertical: $global-small-margin !default; -$list-striped-padding-horizontal: $global-small-margin !default; -$list-striped-background: $global-muted-background !default; - -$list-bullet-width: ($global-line-height * 1em) !default; -$list-bullet-height: $list-bullet-width !default; -$list-bullet-margin-right: $global-small-margin !default; -$list-bullet-icon-color: $global-color !default; - -$list-large-margin-top: $global-margin !default; -$list-large-divider-margin-top: $global-margin !default; -$list-large-striped-padding-vertical: $global-margin !default; -$list-large-striped-padding-horizontal: $global-small-margin !default; - -$internal-list-bullet-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%226%22%20height%3D%226%22%20viewBox%3D%220%200%206%206%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22#000%22%20cx%3D%223%22%20cy%3D%223%22%20r%3D%223%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; - - -/* ======================================================================== - Component: List - ========================================================================== */ - -.uk-list { - padding: 0; - list-style: none; -} - -/* - * Micro clearfix - */ - -.uk-list > li::before, -.uk-list > li::after { - content: ""; - display: table; -} - -.uk-list > li::after { clear: both; } - -/* - * Remove margin from the last-child - */ - -.uk-list > li > :last-child { margin-bottom: 0; } - -/* - * Nested lists - */ - -.uk-list ul { - margin: 0; - padding-left: $list-nested-padding-left; - list-style: none; -} - -/* - * Style - */ - -.uk-list > li:nth-child(n+2), -.uk-list > li > ul { margin-top: $list-margin-top; } - - -/* Style modifiers - ========================================================================== */ - -/* - * Divider - */ - -.uk-list-divider > li:nth-child(n+2) { - margin-top: $list-divider-margin-top; - padding-top: $list-divider-margin-top; - border-top: $list-divider-border-width solid $list-divider-border; - @if(mixin-exists(hook-list-divider)) {@include hook-list-divider();} -} - -/* - * Striped - */ - -.uk-list-striped > li { - padding: $list-striped-padding-vertical $list-striped-padding-horizontal; - @if(mixin-exists(hook-list-striped)) {@include hook-list-striped();} -} - -.uk-list-striped > li:nth-of-type(odd) { background: $list-striped-background; } - -.uk-list-striped > li:nth-child(n+2) { margin-top: 0; } - -/* - * Bullet - */ - -.uk-list-bullet > li { - position: relative; - padding-left: unquote('calc(#{$list-bullet-width} + #{$list-bullet-margin-right})'); -} - -.uk-list-bullet > li::before { - content: ""; - position: absolute; - top: 0; - left: 0; - width: $list-bullet-width; - height: $list-bullet-height; - @include svg-fill($internal-list-bullet-image, "#000", $list-bullet-icon-color); - background-repeat: no-repeat; - background-position: 50% 50%; - float: left; - @if(mixin-exists(hook-list-bullet)) {@include hook-list-bullet();} -} - - -/* Size modifier - ========================================================================== */ - -.uk-list-large > li:nth-child(n+2), -.uk-list-large > li > ul { margin-top: $list-large-margin-top; } - -/* - * Divider - */ - -.uk-list-large.uk-list-divider > li:nth-child(n+2) { - margin-top: $list-large-divider-margin-top; - padding-top: $list-large-divider-margin-top; -} - -/* - * Striped - */ - -.uk-list-large.uk-list-striped > li { - padding: $list-large-striped-padding-vertical $list-large-striped-padding-horizontal; - @if(mixin-exists(hook-list-striped)) {@include hook-list-striped();} -} - -.uk-list-large.uk-list-striped > li:nth-child(n+2) { margin-top: 0; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-list-misc)) {@include hook-list-misc();} - -// @mixin hook-list-divider(){} -// @mixin hook-list-striped(){} -// @mixin hook-list-bullet(){} -// @mixin hook-list-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-list-divider-border: $inverse-global-border !default; -$inverse-list-striped-background: $inverse-global-muted-background !default; -$inverse-list-bullet-icon-color: $inverse-global-color !default; - - - -// @mixin hook-inverse-list-divider(){} -// @mixin hook-inverse-list-striped(){} -// @mixin hook-inverse-list-bullet(){} diff --git a/_sass/uikit/components/margin.scss b/_sass/uikit/components/margin.scss deleted file mode 100644 index c3b136911a..0000000000 --- a/_sass/uikit/components/margin.scss +++ /dev/null @@ -1,163 +0,0 @@ -// Name: Margin -// Description: Utilities for margins -// -// Component: `uk-margin-*` -// `uk-margin-small-*` -// `uk-margin-medium-*` -// `uk-margin-large-*` -// `uk-margin-xlarge-*` -// `uk-margin-remove-*` -// `uk-margin-auto-*` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$margin-margin: $global-margin !default; - -$margin-small-margin: $global-small-margin !default; - -$margin-medium-margin: $global-medium-margin !default; - -$margin-large-margin: $global-medium-margin !default; -$margin-large-margin-l: $global-large-margin !default; - -$margin-xlarge-margin: $global-large-margin !default; -$margin-xlarge-margin-l: $global-xlarge-margin !default; - - -/* ======================================================================== - Component: Margin - ========================================================================== */ - -/* - * Default - */ - -.uk-margin { margin-bottom: $margin-margin; } -* + .uk-margin { margin-top: $margin-margin !important; } - -.uk-margin-top { margin-top: $margin-margin !important; } -.uk-margin-bottom { margin-bottom: $margin-margin !important; } -.uk-margin-left { margin-left: $margin-margin !important; } -.uk-margin-right { margin-right: $margin-margin !important; } - - -/* Small - ========================================================================== */ - -.uk-margin-small { margin-bottom: $margin-small-margin; } -* + .uk-margin-small { margin-top: $margin-small-margin !important; } - -.uk-margin-small-top { margin-top: $margin-small-margin !important; } -.uk-margin-small-bottom { margin-bottom: $margin-small-margin !important; } -.uk-margin-small-left { margin-left: $margin-small-margin !important; } -.uk-margin-small-right { margin-right: $margin-small-margin !important; } - - -/* Medium - ========================================================================== */ - -.uk-margin-medium { margin-bottom: $margin-medium-margin; } -* + .uk-margin-medium { margin-top: $margin-medium-margin !important; } - -.uk-margin-medium-top { margin-top: $margin-medium-margin !important; } -.uk-margin-medium-bottom { margin-bottom: $margin-medium-margin !important; } -.uk-margin-medium-left { margin-left: $margin-medium-margin !important; } -.uk-margin-medium-right { margin-right: $margin-medium-margin !important; } - - -/* Large - ========================================================================== */ - -.uk-margin-large { margin-bottom: $margin-large-margin; } -* + .uk-margin-large { margin-top: $margin-large-margin !important; } - -.uk-margin-large-top { margin-top: $margin-large-margin !important; } -.uk-margin-large-bottom { margin-bottom: $margin-large-margin !important; } -.uk-margin-large-left { margin-left: $margin-large-margin !important; } -.uk-margin-large-right { margin-right: $margin-large-margin !important; } - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-margin-large { margin-bottom: $margin-large-margin-l; } - * + .uk-margin-large { margin-top: $margin-large-margin-l !important; } - - .uk-margin-large-top { margin-top: $margin-large-margin-l !important; } - .uk-margin-large-bottom { margin-bottom: $margin-large-margin-l !important; } - .uk-margin-large-left { margin-left: $margin-large-margin-l !important; } - .uk-margin-large-right { margin-right: $margin-large-margin-l !important; } - -} - - -/* XLarge - ========================================================================== */ - -.uk-margin-xlarge { margin-bottom: $margin-xlarge-margin; } -* + .uk-margin-xlarge { margin-top: $margin-xlarge-margin !important; } - -.uk-margin-xlarge-top { margin-top: $margin-xlarge-margin !important; } -.uk-margin-xlarge-bottom { margin-bottom: $margin-xlarge-margin !important; } -.uk-margin-xlarge-left { margin-left: $margin-xlarge-margin !important; } -.uk-margin-xlarge-right { margin-right: $margin-xlarge-margin !important; } - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-margin-xlarge { margin-bottom: $margin-xlarge-margin-l; } - * + .uk-margin-xlarge { margin-top: $margin-xlarge-margin-l !important; } - - .uk-margin-xlarge-top { margin-top: $margin-xlarge-margin-l !important; } - .uk-margin-xlarge-bottom { margin-bottom: $margin-xlarge-margin-l !important; } - .uk-margin-xlarge-left { margin-left: $margin-xlarge-margin-l !important; } - .uk-margin-xlarge-right { margin-right: $margin-xlarge-margin-l !important; } - -} - - -/* Remove - ========================================================================== */ - -.uk-margin-remove { margin: 0 !important; } -.uk-margin-remove-top { margin-top: 0 !important; } -.uk-margin-remove-bottom { margin-bottom: 0 !important; } -.uk-margin-remove-left { margin-left: 0 !important; } -.uk-margin-remove-right { margin-right: 0 !important; } - -.uk-margin-remove-vertical { - margin-top: 0 !important; - margin-bottom: 0 !important; -} - -.uk-margin-remove-adjacent + * { margin-top: 0 !important; } - - -/* Auto - ========================================================================== */ - -.uk-margin-auto { - margin-left: auto !important; - margin-right: auto !important; -} - -.uk-margin-auto-top { margin-top: auto !important; } -.uk-margin-auto-bottom { margin-bottom: auto !important; } -.uk-margin-auto-left { margin-left: auto !important; } -.uk-margin-auto-right { margin-right: auto !important; } - -.uk-margin-auto-vertical { - margin-top: auto !important; - margin-bottom: auto !important; -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-margin-misc)) {@include hook-margin-misc();} - -// @mixin hook-margin-misc(){} diff --git a/_sass/uikit/components/marker.scss b/_sass/uikit/components/marker.scss deleted file mode 100644 index 97e436098c..0000000000 --- a/_sass/uikit/components/marker.scss +++ /dev/null @@ -1,63 +0,0 @@ -// Name: Marker -// Description: Component to create a marker icon -// -// Component: `uk-marker` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$marker-padding: 5px !default; -$marker-background: $global-secondary-background !default; -$marker-color: $global-inverse-color !default; - -$marker-hover-color: $global-inverse-color !default; - - -/* ======================================================================== - Component: Marker - ========================================================================== */ - -/* - * Addopts `uk-icon` - */ - -.uk-marker { - padding: $marker-padding; - background: $marker-background; - color: $marker-color; - @if(mixin-exists(hook-marker)) {@include hook-marker();} -} - -/* Hover + Focus */ -.uk-marker:hover, -.uk-marker:focus { - color: $marker-hover-color; - outline: none; - @if(mixin-exists(hook-marker-hover)) {@include hook-marker-hover();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-marker-misc)) {@include hook-marker-misc();} - -// @mixin hook-marker(){} -// @mixin hook-marker-hover(){} -// @mixin hook-marker-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-marker-background: $global-muted-background !default; -$inverse-marker-color: $global-color !default; -$inverse-marker-hover-color: $global-color !default; - - - -// @mixin hook-inverse-marker(){} -// @mixin hook-inverse-marker-hover(){} diff --git a/_sass/uikit/components/mixin.scss b/_sass/uikit/components/mixin.scss deleted file mode 100644 index 5ed438a569..0000000000 --- a/_sass/uikit/components/mixin.scss +++ /dev/null @@ -1,32 +0,0 @@ -// -// Component: Mixin -// Description: Defines mixins which are used across all components -// -// ======================================================================== - - -// SVG -// ======================================================================== - -/// Replace `$search` with `$replace` in `$string` -/// @author Hugo Giraudel -/// @param {String} $string - Initial string -/// @param {String} $search - Substring to replace -/// @param {String} $replace ('') - New value -/// @return {String} - Updated string -@function str-replace($string, $search, $replace: '') { - $index: str-index($string, $search); - - @if $index { - @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); - } - - @return $string; -} - -@mixin svg-fill($src, $color-default, $color-new){ - - $replace-src: str-replace($src, $color-default, $color-new) !default; - $replace-src: str-replace($replace-src, "#", "%23"); - background-image: url(quote($replace-src)); -} \ No newline at end of file diff --git a/_sass/uikit/components/modal.scss b/_sass/uikit/components/modal.scss deleted file mode 100644 index 863f008a7c..0000000000 --- a/_sass/uikit/components/modal.scss +++ /dev/null @@ -1,368 +0,0 @@ -// Name: Modal -// Description: Component to create modal dialogs -// -// Component: `uk-modal` -// -// Sub-objects: `uk-modal-page` -// `uk-modal-dialog` -// `uk-modal-header` -// `uk-modal-body` -// `uk-modal-footer` -// `uk-modal-title` -// `uk-modal-close` -// -// Adopted: `uk-modal-close-default` -// `uk-modal-close-outside` -// `uk-modal-close-full` -// -// Modifiers: `uk-modal-container` -// `uk-modal-full` -// -// States: `uk-open` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$modal-z-index: $global-z-index + 10 !default; -$modal-background: rgba(0,0,0,0.6) !default; - -$modal-padding-horizontal: 15px !default; -$modal-padding-horizontal-s: $global-gutter !default; -$modal-padding-horizontal-m: $global-medium-gutter !default; -$modal-padding-vertical: $modal-padding-horizontal !default; -$modal-padding-vertical-s: 50px !default; - -$modal-dialog-width: 600px !default; -$modal-dialog-background: $global-background !default; - -$modal-container-width: 1200px !default; - -$modal-body-padding-horizontal: $global-gutter !default; -$modal-body-padding-vertical: $global-gutter !default; - -$modal-header-padding-horizontal: $global-gutter !default; -$modal-header-padding-vertical: ($modal-header-padding-horizontal / 2) !default; -$modal-header-background: $global-muted-background !default; - -$modal-footer-padding-horizontal: $global-gutter !default; -$modal-footer-padding-vertical: ($modal-footer-padding-horizontal / 2) !default; -$modal-footer-background: $global-muted-background !default; - -$modal-title-font-size: $global-xlarge-font-size !default; -$modal-title-line-height: 1.3 !default; - -$modal-close-position: $global-small-margin !default; -$modal-close-padding: 5px !default; - -$modal-close-outside-position: 0 !default; -$modal-close-outside-translate: 100% !default; -$modal-close-outside-color: lighten($global-inverse-color, 20%) !default; -$modal-close-outside-hover-color: $global-inverse-color !default; - - -/* ======================================================================== - Component: Modal - ========================================================================== */ - -/* - * 1. Hide by default - * 2. Set position - * 3. Allow scrolling for the modal dialog - * 4. Horizontal padding - * 5. Mask the background page - * 6. Fade-in transition - */ - -.uk-modal { - /* 1 */ - display: none; - /* 2 */ - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: $modal-z-index; - /* 3 */ - overflow-y: auto; - -webkit-overflow-scrolling: touch; - /* 4 */ - padding: $modal-padding-vertical $modal-padding-horizontal; - /* 5 */ - background: $modal-background; - /* 6 */ - opacity: 0; - transition: opacity 0.15s linear; - @if(mixin-exists(hook-modal)) {@include hook-modal();} -} - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - .uk-modal { padding: $modal-padding-vertical-s $modal-padding-horizontal-s; } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-modal { - padding-left: $modal-padding-horizontal-m; - padding-right: $modal-padding-horizontal-m; - } - -} - -/* - * Open - */ - -.uk-modal.uk-open { opacity: 1; } - - -/* Page - ========================================================================== */ - -/* - * Prevent scrollbars - */ - -.uk-modal-page { overflow: hidden; } - - -/* Dialog - ========================================================================== */ - -/* - * 1. Create position context for spinner and close button - * 2. Dimensions - * 3. Fix `max-width: 100%` not working in combination with flex and responsive images in IE11 - * `!important` needed to overwrite `uk-width-auto`. See `#modal-media-image` in tests - * 4. Style - * 5. Slide-in transition - */ - -.uk-modal-dialog { - /* 1 */ - position: relative; - /* 2 */ - box-sizing: border-box; - margin: 0 auto; - width: $modal-dialog-width; - /* 3 */ - max-width: unquote('calc(100% - 0.01px)') !important; - /* 4 */ - background: $modal-dialog-background; - /* 5 */ - opacity: 0; - transform: translateY(-100px); - transition: 0.3s linear; - transition-property: opacity, transform; - @if(mixin-exists(hook-modal-dialog)) {@include hook-modal-dialog();} -} - -/* - * Open - */ - -.uk-open > .uk-modal-dialog { - opacity: 1; - transform: translateY(0); -} - - -/* Size modifier - ========================================================================== */ - -/* - * Container size - * Take the same size as the Container component - */ - -.uk-modal-container .uk-modal-dialog { width: $modal-container-width; } - -/* - * Full size - * 1. Remove padding and background from modal - * 2. Reset all default declarations from modal dialog - */ - -/* 1 */ -.uk-modal-full { - padding: 0; - background: none; -} - -/* 2 */ -.uk-modal-full .uk-modal-dialog { - margin: 0; - width: 100%; - max-width: 100%; - transform: translateY(0); - @if(mixin-exists(hook-modal-full)) {@include hook-modal-full();} -} - - -/* Sections - ========================================================================== */ - -.uk-modal-body { - padding: $modal-body-padding-vertical $modal-body-padding-horizontal; - @if(mixin-exists(hook-modal-body)) {@include hook-modal-body();} -} - -.uk-modal-header { - padding: $modal-header-padding-vertical $modal-header-padding-horizontal; - background: $modal-header-background; - @if(mixin-exists(hook-modal-header)) {@include hook-modal-header();} -} - -.uk-modal-footer { - padding: $modal-footer-padding-vertical $modal-footer-padding-horizontal; - background: $modal-footer-background; - @if(mixin-exists(hook-modal-footer)) {@include hook-modal-footer();} -} - -/* - * Micro clearfix - */ - -.uk-modal-body::before, -.uk-modal-body::after, -.uk-modal-header::before, -.uk-modal-header::after, -.uk-modal-footer::before, -.uk-modal-footer::after { - content: ""; - display: table; -} - -.uk-modal-body::after, -.uk-modal-header::after, -.uk-modal-footer::after { clear: both; } - -/* - * Remove margin from the last-child - */ - -.uk-modal-body > :last-child, -.uk-modal-header > :last-child, -.uk-modal-footer > :last-child { margin-bottom: 0; } - - -/* Title - ========================================================================== */ - -.uk-modal-title { - font-size: $modal-title-font-size; - line-height: $modal-title-line-height; - @if(mixin-exists(hook-modal-title)) {@include hook-modal-title();} -} - - -/* Close - * Adopts `uk-close` - ========================================================================== */ - -[class*='uk-modal-close-'] { - position: absolute; - z-index: $modal-z-index; - top: $modal-close-position; - right: $modal-close-position; - padding: $modal-close-padding; - @if(mixin-exists(hook-modal-close)) {@include hook-modal-close();} -} - -/* - * Remove margin from adjacent element - */ - -[class*='uk-modal-close-']:first-child + * { margin-top: 0; } - -/* - * Hover - */ - -[class*='uk-modal-close-']:hover { - @if(mixin-exists(hook-modal-close-hover)) {@include hook-modal-close-hover();} -} - -/* - * Default - */ - -.uk-modal-close-default { - @if(mixin-exists(hook-modal-close-default)) {@include hook-modal-close-default();} -} - -.uk-modal-close-default:hover { - @if(mixin-exists(hook-modal-close-default-hover)) {@include hook-modal-close-default-hover();} -} - -/* - * Outside - * 1. Prevent scrollbar on small devices - */ - -.uk-modal-close-outside { - top: $modal-close-outside-position; - /* 1 */ - right: (-$modal-close-padding); - transform: translate(0, -($modal-close-outside-translate)); - color: $modal-close-outside-color; - @if(mixin-exists(hook-modal-close-outside)) {@include hook-modal-close-outside();} -} - -.uk-modal-close-outside:hover { - color: $modal-close-outside-hover-color; - @if(mixin-exists(hook-modal-close-outside-hover)) {@include hook-modal-close-outside-hover();} -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - /* 1 */ - .uk-modal-close-outside { - right: $modal-close-outside-position; - transform: translate($modal-close-outside-translate, -($modal-close-outside-translate)); - } - -} - -/* - * Full - */ - -.uk-modal-close-full { - @if(mixin-exists(hook-modal-close-full)) {@include hook-modal-close-full();} -} - -.uk-modal-close-full:hover { - @if(mixin-exists(hook-modal-close-full-hover)) {@include hook-modal-close-full-hover();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-modal-misc)) {@include hook-modal-misc();} - -// @mixin hook-modal(){} -// @mixin hook-modal-dialog(){} -// @mixin hook-modal-full(){} -// @mixin hook-modal-header(){} -// @mixin hook-modal-body(){} -// @mixin hook-modal-footer(){} -// @mixin hook-modal-title(){} -// @mixin hook-modal-close(){} -// @mixin hook-modal-close-hover(){} -// @mixin hook-modal-close-default(){} -// @mixin hook-modal-close-default-hover(){} -// @mixin hook-modal-close-outside(){} -// @mixin hook-modal-close-outside-hover(){} -// @mixin hook-modal-close-full(){} -// @mixin hook-modal-close-full-hover(){} -// @mixin hook-modal-misc(){} diff --git a/_sass/uikit/components/nav.scss b/_sass/uikit/components/nav.scss deleted file mode 100644 index 9d990ca248..0000000000 --- a/_sass/uikit/components/nav.scss +++ /dev/null @@ -1,357 +0,0 @@ -// Name: Nav -// Description: Defines styles for list navigations -// -// Component: `uk-nav` -// -// Sub-objects: `uk-nav-header` -// `uk-nav-divider` -// `uk-nav-sub` -// -// Modifiers: `uk-nav-parent-icon` -// `uk-nav-default` -// `uk-nav-primary` -// `uk-nav-center` -// -// States: `uk-active` -// `uk-parent` -// `uk-open` -// `uk-touch` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$nav-item-padding-vertical: 5px !default; -$nav-item-padding-horizontal: 0 !default; - -$nav-sublist-padding-vertical: 5px !default; -$nav-sublist-padding-left: 15px !default; -$nav-sublist-deeper-padding-left: 15px !default; -$nav-sublist-item-padding-vertical: 2px !default; - -$nav-parent-icon-width: ($global-line-height * 1em) !default; -$nav-parent-icon-height: $nav-parent-icon-width !default; -$nav-parent-icon-color: $global-color !default; - -$nav-header-padding-vertical: $nav-item-padding-vertical !default; -$nav-header-padding-horizontal: $nav-item-padding-horizontal !default; -$nav-header-font-size: $global-small-font-size !default; -$nav-header-text-transform: uppercase !default; -$nav-header-margin-top: $global-margin !default; - -$nav-divider-margin-vertical: 5px !default; -$nav-divider-margin-horizontal: 0 !default; - -$nav-default-item-color: $global-muted-color !default; -$nav-default-item-hover-color: $global-color !default; -$nav-default-item-active-color: $global-emphasis-color !default; -$nav-default-header-color: $global-emphasis-color !default; -$nav-default-divider-border-width: $global-border-width !default; -$nav-default-divider-border: $global-border !default; -$nav-default-sublist-item-color: $global-muted-color !default; -$nav-default-sublist-item-hover-color: $global-color !default; - -$nav-primary-item-font-size: $global-large-font-size !default; -$nav-primary-item-line-height: $global-line-height !default; -$nav-primary-item-color: $global-muted-color !default; -$nav-primary-item-hover-color: $global-color !default; -$nav-primary-item-active-color: $global-emphasis-color !default; -$nav-primary-header-color: $global-emphasis-color !default; -$nav-primary-divider-border-width: $global-border-width !default; -$nav-primary-divider-border: $global-border !default; -$nav-primary-sublist-item-color: $global-muted-color !default; -$nav-primary-sublist-item-hover-color: $global-color !default; - -$internal-nav-parent-close-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolyline%20fill%3D%22none%22%20stroke%3D%22#000%22%20stroke-width%3D%221.1%22%20points%3D%2210%201%204%207%2010%2013%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$internal-nav-parent-open-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolyline%20fill%3D%22none%22%20stroke%3D%22#000%22%20stroke-width%3D%221.1%22%20points%3D%221%204%207%2010%2013%204%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; - - -/* ======================================================================== - Component: Nav - ========================================================================== */ - -/* - * Reset - * 1. Prepare lists - * 2. Prepare links - * 3. Remove default focus style - */ - -/* 1 */ -.uk-nav, -.uk-nav ul { - margin: 0; - padding: 0; - list-style: none; -} - -/* 2 */ -.uk-nav li > a { - display: block; - text-decoration: none; -} - -/* 3 */ -.uk-nav li > a:focus { outline: none; } - -/* - * Items - * Must target `a` elements to exclude other elements (e.g. lists) - */ - -.uk-nav > li > a { padding: $nav-item-padding-vertical $nav-item-padding-horizontal; } - - -/* Sublists - ========================================================================== */ - -/* - * Level 2 - * `ul` needed for higher specificity to override padding - */ - -ul.uk-nav-sub { - padding: $nav-sublist-padding-vertical 0 $nav-sublist-padding-vertical $nav-sublist-padding-left; - @if(mixin-exists(hook-nav-sub)) {@include hook-nav-sub();} -} - -/* - * Level 3 and deeper - */ - -.uk-nav-sub ul { padding-left: $nav-sublist-deeper-padding-left; } - -/* - * Items - */ - -.uk-nav-sub a { padding: $nav-sublist-item-padding-vertical 0; } - - -/* Parent icon modifier - ========================================================================== */ - -.uk-nav-parent-icon > .uk-parent > a::after { - content: ""; - width: $nav-parent-icon-width; - height: $nav-parent-icon-height; - float: right; - @include svg-fill($internal-nav-parent-close-image, "#000", $nav-parent-icon-color); - background-repeat: no-repeat; - background-position: 50% 50%; - @if(mixin-exists(hook-nav-parent-icon)) {@include hook-nav-parent-icon();} -} - -.uk-nav-parent-icon > .uk-parent.uk-open > a::after { @include svg-fill($internal-nav-parent-open-image, "#000", $nav-parent-icon-color); } - - -/* Header - ========================================================================== */ - -.uk-nav-header { - padding: $nav-header-padding-vertical $nav-header-padding-horizontal; - text-transform: $nav-header-text-transform; - font-size: $nav-header-font-size; - @if(mixin-exists(hook-nav-header)) {@include hook-nav-header();} -} - -.uk-nav-header:not(:first-child) { margin-top: $nav-header-margin-top; } - - -/* Divider - ========================================================================== */ - -.uk-nav-divider { - margin: $nav-divider-margin-vertical $nav-divider-margin-horizontal; - @if(mixin-exists(hook-nav-divider)) {@include hook-nav-divider();} -} - - -/* Default modifier - ========================================================================== */ - -.uk-nav-default { - @if(mixin-exists(hook-nav-default)) {@include hook-nav-default();} -} - -/* - * Items - */ - -.uk-nav-default > li > a { - color: $nav-default-item-color; - @if(mixin-exists(hook-nav-default-item)) {@include hook-nav-default-item();} -} - -/* Hover + Focus */ -.uk-nav-default > li > a:hover, -.uk-nav-default > li > a:focus { - color: $nav-default-item-hover-color; - @if(mixin-exists(hook-nav-default-item-hover)) {@include hook-nav-default-item-hover();} -} - -/* Active */ -.uk-nav-default > li.uk-active > a { - color: $nav-default-item-active-color; - @if(mixin-exists(hook-nav-default-item-active)) {@include hook-nav-default-item-active();} -} - -/* - * Header - */ - -.uk-nav-default .uk-nav-header { - color: $nav-default-header-color; - @if(mixin-exists(hook-nav-default-header)) {@include hook-nav-default-header();} -} - -/* - * Divider - */ - -.uk-nav-default .uk-nav-divider { - border-top: $nav-default-divider-border-width solid $nav-default-divider-border; - @if(mixin-exists(hook-nav-default-divider)) {@include hook-nav-default-divider();} -} - -/* - * Sublists - */ - -.uk-nav-default .uk-nav-sub a { color: $nav-default-sublist-item-color; } - -.uk-nav-default .uk-nav-sub a:hover, -.uk-nav-default .uk-nav-sub a:focus { color: $nav-default-sublist-item-hover-color; } - - -/* Primary modifier - ========================================================================== */ - -.uk-nav-primary { - @if(mixin-exists(hook-nav-primary)) {@include hook-nav-primary();} -} - -/* - * Items - */ - -.uk-nav-primary > li > a { - font-size: $nav-primary-item-font-size; - line-height: $nav-primary-item-line-height; - color: $nav-primary-item-color; - @if(mixin-exists(hook-nav-primary-item)) {@include hook-nav-primary-item();} -} - -/* Hover + Focus */ -.uk-nav-primary > li > a:hover, -.uk-nav-primary > li > a:focus { - color: $nav-primary-item-hover-color; - @if(mixin-exists(hook-nav-primary-item-hover)) {@include hook-nav-primary-item-hover();} -} - -/* Active */ -.uk-nav-primary > li.uk-active > a { - color: $nav-primary-item-active-color; - @if(mixin-exists(hook-nav-primary-item-active)) {@include hook-nav-primary-item-active();} -} - -/* - * Header - */ - -.uk-nav-primary .uk-nav-header { - color: $nav-primary-header-color; - @if(mixin-exists(hook-nav-primary-header)) {@include hook-nav-primary-header();} -} - -/* - * Divider - */ - -.uk-nav-primary .uk-nav-divider { - border-top: $nav-primary-divider-border-width solid $nav-primary-divider-border; - @if(mixin-exists(hook-nav-primary-divider)) {@include hook-nav-primary-divider();} -} - -/* - * Sublists - */ - -.uk-nav-primary .uk-nav-sub a { color: $nav-primary-sublist-item-color; } - -.uk-nav-primary .uk-nav-sub a:hover, -.uk-nav-primary .uk-nav-sub a:focus { color: $nav-primary-sublist-item-hover-color; } - - -/* Alignment modifier - ========================================================================== */ - -.uk-nav-center { text-align: center; } - -/* Sublists */ -.uk-nav-center .uk-nav-sub, -.uk-nav-center .uk-nav-sub ul { padding-left: 0; } - -/* Parent icon modifier */ -.uk-nav-center.uk-nav-parent-icon > .uk-parent > a::after { position: absolute; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-nav-misc)) {@include hook-nav-misc();} - -// @mixin hook-nav-sub(){} -// @mixin hook-nav-parent-icon(){} -// @mixin hook-nav-header(){} -// @mixin hook-nav-divider(){} -// @mixin hook-nav-default(){} -// @mixin hook-nav-default-item(){} -// @mixin hook-nav-default-item-hover(){} -// @mixin hook-nav-default-item-active(){} -// @mixin hook-nav-default-header(){} -// @mixin hook-nav-default-divider(){} -// @mixin hook-nav-primary(){} -// @mixin hook-nav-primary-item(){} -// @mixin hook-nav-primary-item-hover(){} -// @mixin hook-nav-primary-item-active(){} -// @mixin hook-nav-primary-header(){} -// @mixin hook-nav-primary-divider(){} -// @mixin hook-nav-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-nav-parent-icon-color: $inverse-global-color !default; -$inverse-nav-default-item-color: $inverse-global-muted-color !default; -$inverse-nav-default-item-hover-color: $inverse-global-color !default; -$inverse-nav-default-item-active-color: $inverse-global-emphasis-color !default; -$inverse-nav-default-header-color: $inverse-global-emphasis-color !default; -$inverse-nav-default-divider-border: $inverse-global-border !default; -$inverse-nav-default-sublist-item-color: $inverse-global-muted-color !default; -$inverse-nav-default-sublist-item-hover-color: $inverse-global-color !default; - -$inverse-nav-primary-item-color: $inverse-global-muted-color !default; -$inverse-nav-primary-item-hover-color: $inverse-global-color !default; -$inverse-nav-primary-item-active-color: $inverse-global-emphasis-color !default; -$inverse-nav-primary-header-color: $inverse-global-emphasis-color !default; -$inverse-nav-primary-divider-border: $inverse-global-border !default; -$inverse-nav-primary-sublist-item-color: $inverse-global-muted-color !default; -$inverse-nav-primary-sublist-item-hover-color: $inverse-global-color !default; - - - -// @mixin hook-inverse-nav-parent-icon(){} -// @mixin hook-inverse-nav-default-item(){} -// @mixin hook-inverse-nav-default-item-hover(){} -// @mixin hook-inverse-nav-default-item-active(){} -// @mixin hook-inverse-nav-default-header(){} -// @mixin hook-inverse-nav-default-divider(){} -// @mixin hook-inverse-nav-primary-item(){} -// @mixin hook-inverse-nav-primary-item-hover(){} -// @mixin hook-inverse-nav-primary-item-active(){} -// @mixin hook-inverse-nav-primary-header(){} -// @mixin hook-inverse-nav-primary-divider(){} diff --git a/_sass/uikit/components/navbar.scss b/_sass/uikit/components/navbar.scss deleted file mode 100644 index a1f633a3de..0000000000 --- a/_sass/uikit/components/navbar.scss +++ /dev/null @@ -1,537 +0,0 @@ -// Name: Navbar -// Description: Component to create horizontal navigation bars -// -// Component: `uk-navbar` -// -// Sub-objects: `uk-navbar-container` -// `uk-navbar-left` -// `uk-navbar-right` -// `uk-navbar-center` -// `uk-navbar-center-left` -// `uk-navbar-center-right` -// `uk-navbar-nav` -// `uk-navbar-item` -// `uk-navbar-toggle` -// `uk-navbar-subtitle` -// `uk-navbar-dropbar` -// -// Adopted: `uk-navbar-dropdown` + Modifiers -// `uk-navbar-dropdown-nav` -// `uk-navbar-dropdown-grid` -// `uk-navbar-toggle-icon` -// -// Modifiers: `uk-navbar-transparent` -// `uk-navbar-sticky` -// `uk-navbar-dropdown-stack` -// -// States: `uk-active` -// `uk-parent` -// `uk-open` -// -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$navbar-background: $global-muted-background !default; -$navbar-color-mode: none !default; - -$navbar-nav-item-height: 80px !default; -$navbar-nav-item-padding-horizontal: 15px !default; -$navbar-nav-item-color: $global-muted-color !default; -$navbar-nav-item-font-size: $global-font-size !default; -$navbar-nav-item-font-family: $global-font-family !default; -$navbar-nav-item-hover-color: $global-color !default; -$navbar-nav-item-onclick-color: $global-emphasis-color !default; -$navbar-nav-item-active-color: $global-emphasis-color !default; - -$navbar-item-color: $global-color !default; - -$navbar-toggle-color: $global-muted-color !default; -$navbar-toggle-hover-color: $global-color !default; - -$navbar-subtitle-font-size: $global-small-font-size !default; - -$navbar-dropdown-z-index: $global-z-index + 20 !default; -$navbar-dropdown-width: 200px !default; -$navbar-dropdown-margin: 0 !default; -$navbar-dropdown-padding: 15px !default; -$navbar-dropdown-background: $global-muted-background !default; -$navbar-dropdown-color: $global-color !default; -$navbar-dropdown-grid-gutter-horizontal: $global-gutter !default; -$navbar-dropdown-grid-gutter-vertical: $navbar-dropdown-grid-gutter-horizontal !default; - -$navbar-dropdown-dropbar-margin-top: 0 !default; -$navbar-dropdown-dropbar-margin-bottom: $navbar-dropdown-dropbar-margin-top !default; - -$navbar-dropdown-nav-item-color: $global-muted-color !default; -$navbar-dropdown-nav-item-hover-color: $global-color !default; -$navbar-dropdown-nav-item-active-color: $global-emphasis-color !default; -$navbar-dropdown-nav-header-color: $global-emphasis-color !default; -$navbar-dropdown-nav-divider-border-width: $global-border-width !default; -$navbar-dropdown-nav-divider-border: $global-border !default; -$navbar-dropdown-nav-sublist-item-color: $global-muted-color !default; -$navbar-dropdown-nav-sublist-item-hover-color: $global-color !default; - -$navbar-dropbar-background: $navbar-dropdown-background !default; -$navbar-dropbar-z-index: $global-z-index - 20 !default; - - -/* ======================================================================== - Component: Navbar - ========================================================================== */ - -/* - * 1. Create position context to center navbar group - */ - -.uk-navbar { - display: flex; - /* 1 */ - position: relative; - @if(mixin-exists(hook-navbar)) {@include hook-navbar();} -} - - -/* Container - ========================================================================== */ - -.uk-navbar-container:not(.uk-navbar-transparent) { - background: $navbar-background; - @if(mixin-exists(hook-navbar-container)) {@include hook-navbar-container();} -} - -// Color Mode -@if ( $navbar-color-mode == light ) { .uk-navbar-container:not(.uk-navbar-transparent) { @extend .uk-light !optional;} } -@if ( $navbar-color-mode == dark ) { .uk-navbar-container:not(.uk-navbar-transparent) { @extend .uk-dark !optional;} } - -/* - * Remove pseudo elements created by micro clearfix as precaution (if Container component is used) - */ - -.uk-navbar-container > ::before, -.uk-navbar-container > ::after { display: none !important; } - - -/* Groups - ========================================================================== */ - -/* - * 1. Align navs and items vertically if they have a different height - * 2. Note: IE 11 requires an extra `div` which affects the center selector - */ - -.uk-navbar-left, -.uk-navbar-right, -// 2. [class*='uk-navbar-center'], -.uk-navbar-center, -.uk-navbar-center-left > *, -.uk-navbar-center-right > * { - display: flex; - /* 1 */ - align-items: center; -} - -/* - * Horizontal alignment - * 1. Create position context for centered navbar with sub groups (left/right) - * 2. Needed for dropdowns because a new position context is created - * `z-index` must be smaller than off-canvas - * 3. Fix text wrapping if the centered section is larger than 50% of the navbar - * 4. Align sub groups for centered navbar - */ - -.uk-navbar-right { margin-left: auto; } - -.uk-navbar-center:only-child { - margin-left: auto; - margin-right: auto; - /* 1 */ - position: relative; -} - -.uk-navbar-center:not(:only-child) { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%,-50%); - /* 2 */ - z-index: $global-z-index - 10; -} - -/* 3 */ -.uk-navbar-center:not(:only-child) .uk-navbar-nav > li > a, -.uk-navbar-center:not(:only-child) .uk-navbar-item, -.uk-navbar-center:not(:only-child) .uk-navbar-toggle { white-space: nowrap; } - -/* 4 */ -.uk-navbar-center-left, -.uk-navbar-center-right { - position: absolute; - top: 0; -} - -.uk-navbar-center-left { right: 100%; } -.uk-navbar-center-right { left: 100%; } - -[class*='uk-navbar-center-'] .uk-navbar-nav > li > a, -[class*='uk-navbar-center-'] .uk-navbar-item, -[class*='uk-navbar-center-'] .uk-navbar-toggle { white-space: nowrap; } - - -/* Nav - ========================================================================== */ - -/* - * 1. Reset list - */ - -.uk-navbar-nav { - display: flex; - /* 1 */ - margin: 0; - padding: 0; - list-style: none; -} - -/* - * Allow items to wrap into the next line - * Only not `absolute` positioned groups - */ - -.uk-navbar-left, -.uk-navbar-right, -.uk-navbar-center:only-child { flex-wrap: wrap; } - -/* - * Items - * 1. Center content vertically and horizontally - * 2. Dimensions - * 3. Style - * 4. Required for `a` - */ - -.uk-navbar-nav > li > a, // Nav item -.uk-navbar-item, // Content item -.uk-navbar-toggle { // Clickable item - /* 1 */ - display: flex; - justify-content: center; - align-items: center; - /* 2 */ - box-sizing: border-box; - height: $navbar-nav-item-height; - padding: 0 $navbar-nav-item-padding-horizontal; - /* 3 */ - font-size: $navbar-nav-item-font-size; - font-family: $navbar-nav-item-font-family; - /* 4 */ - text-decoration: none; -} - -/* - * Nav items - */ - -.uk-navbar-nav > li > a { - color: $navbar-nav-item-color; - @if(mixin-exists(hook-navbar-nav-item)) {@include hook-navbar-nav-item();} -} - -/* - * Hover - * Apply hover style also to focus state and if dropdown is opened - */ - -.uk-navbar-nav > li:hover > a, -.uk-navbar-nav > li > a:focus, -.uk-navbar-nav > li > a.uk-open { - color: $navbar-nav-item-hover-color; - outline: none; - @if(mixin-exists(hook-navbar-nav-item-hover)) {@include hook-navbar-nav-item-hover();} -} - -/* OnClick */ -.uk-navbar-nav > li > a:active { - color: $navbar-nav-item-onclick-color; - @if(mixin-exists(hook-navbar-nav-item-onclick)) {@include hook-navbar-nav-item-onclick();} -} - -/* Active */ -.uk-navbar-nav > li.uk-active > a { - color: $navbar-nav-item-active-color; - @if(mixin-exists(hook-navbar-nav-item-active)) {@include hook-navbar-nav-item-active();} -} - - -/* Item - ========================================================================== */ - -.uk-navbar-item { - color: $navbar-item-color; - @if(mixin-exists(hook-navbar-item)) {@include hook-navbar-item();} -} - - -/* Toggle - ========================================================================== */ - -.uk-navbar-toggle { - color: $navbar-toggle-color; - @if(mixin-exists(hook-navbar-toggle)) {@include hook-navbar-toggle();} -} - -.uk-navbar-toggle:hover, -.uk-navbar-toggle:focus, -.uk-navbar-toggle.uk-open { - color: $navbar-toggle-hover-color; - outline: none; - text-decoration: none; - @if(mixin-exists(hook-navbar-toggle-hover)) {@include hook-navbar-toggle-hover();} -} - -/* - * Icon - * Adopts `uk-icon` - */ - -.uk-navbar-toggle-icon { - @if(mixin-exists(hook-navbar-toggle-icon)) {@include hook-navbar-toggle-icon();} -} - -/* Hover + Focus */ -:hover > .uk-navbar-toggle-icon, -:focus > .uk-navbar-toggle-icon { - @if(mixin-exists(hook-navbar-toggle-icon-hover)) {@include hook-navbar-toggle-icon-hover();} -} - - -/* Subtitle - ========================================================================== */ - -.uk-navbar-subtitle { - font-size: $navbar-subtitle-font-size; - @if(mixin-exists(hook-navbar-subtitle)) {@include hook-navbar-subtitle();} -} - - -/* Style modifiers - ========================================================================== */ - -.uk-navbar-transparent { - @if(mixin-exists(hook-navbar-transparent)) {@include hook-navbar-transparent();} -} - -.uk-navbar-sticky { - @if(mixin-exists(hook-navbar-sticky)) {@include hook-navbar-sticky();} -} - - -/* Dropdown - ========================================================================== */ - -/* - * Adopts `uk-dropdown` - * 1. Hide by default - * 2. Set position - * 3. Set a default width - * 4. Style - */ - -.uk-navbar-dropdown { - /* 1 */ - display: none; - /* 2 */ - position: absolute; - z-index: $navbar-dropdown-z-index; - /* 3 */ - box-sizing: border-box; - width: $navbar-dropdown-width; - /* 4 */ - padding: $navbar-dropdown-padding; - background: $navbar-dropdown-background; - color: $navbar-dropdown-color; - @if(mixin-exists(hook-navbar-dropdown)) {@include hook-navbar-dropdown();} -} - -/* Show */ -.uk-navbar-dropdown.uk-open { display: block; } - -/* - * Direction / Alignment modifiers - */ - -/* Direction */ -[class*='uk-navbar-dropdown-top'] { margin-top: (-$navbar-dropdown-margin); } -[class*='uk-navbar-dropdown-bottom'] { margin-top: $navbar-dropdown-margin; } -[class*='uk-navbar-dropdown-left'] { margin-left: (-$navbar-dropdown-margin); } -[class*='uk-navbar-dropdown-right'] { margin-left: $navbar-dropdown-margin; } - -/* - * Grid - * Adopts `uk-grid` - */ - -/* Gutter Horizontal */ -.uk-navbar-dropdown-grid { margin-left: (-$navbar-dropdown-grid-gutter-horizontal); } -.uk-navbar-dropdown-grid > * { padding-left: $navbar-dropdown-grid-gutter-horizontal; } - -/* Gutter Vertical */ -.uk-navbar-dropdown-grid > .uk-grid-margin { margin-top: $navbar-dropdown-grid-gutter-vertical; } - -/* Stack */ -.uk-navbar-dropdown-stack .uk-navbar-dropdown-grid > * { width: 100% !important; } - -/* - * Width modifier - */ - -.uk-navbar-dropdown-width-2:not(.uk-navbar-dropdown-stack) { width: ($navbar-dropdown-width * 2); } -.uk-navbar-dropdown-width-3:not(.uk-navbar-dropdown-stack) { width: ($navbar-dropdown-width * 3); } -.uk-navbar-dropdown-width-4:not(.uk-navbar-dropdown-stack) { width: ($navbar-dropdown-width * 4); } -.uk-navbar-dropdown-width-5:not(.uk-navbar-dropdown-stack) { width: ($navbar-dropdown-width * 5); } - -/* - * Dropbar modifier - */ - -.uk-navbar-dropdown-dropbar { - margin-top: $navbar-dropdown-dropbar-margin-top; - margin-bottom: $navbar-dropdown-dropbar-margin-bottom; - @if(mixin-exists(hook-navbar-dropdown-dropbar)) {@include hook-navbar-dropdown-dropbar();} -} - - -/* Dropdown Nav - * Adopts `uk-nav` - ========================================================================== */ - -.uk-navbar-dropdown-nav { - @if(mixin-exists(hook-navbar-dropdown-nav)) {@include hook-navbar-dropdown-nav();} -} - -/* - * Items - */ - -.uk-navbar-dropdown-nav > li > a { - color: $navbar-dropdown-nav-item-color; - @if(mixin-exists(hook-navbar-dropdown-nav-item)) {@include hook-navbar-dropdown-nav-item();} -} - -/* Hover + Focus */ -.uk-navbar-dropdown-nav > li > a:hover, -.uk-navbar-dropdown-nav > li > a:focus { - color: $navbar-dropdown-nav-item-hover-color; - @if(mixin-exists(hook-navbar-dropdown-nav-item-hover)) {@include hook-navbar-dropdown-nav-item-hover();} -} - -/* Active */ -.uk-navbar-dropdown-nav > li.uk-active > a { - color: $navbar-dropdown-nav-item-active-color; - @if(mixin-exists(hook-navbar-dropdown-nav-item-active)) {@include hook-navbar-dropdown-nav-item-active();} -} - -/* - * Header - */ - -.uk-navbar-dropdown-nav .uk-nav-header { - color: $navbar-dropdown-nav-header-color; - @if(mixin-exists(hook-navbar-dropdown-nav-header)) {@include hook-navbar-dropdown-nav-header();} -} - -/* - * Divider - */ - -.uk-navbar-dropdown-nav .uk-nav-divider { - border-top: $navbar-dropdown-nav-divider-border-width solid $navbar-dropdown-nav-divider-border; - @if(mixin-exists(hook-navbar-dropdown-nav-divider)) {@include hook-navbar-dropdown-nav-divider();} -} - -/* - * Sublists - */ - -.uk-navbar-dropdown-nav .uk-nav-sub a { color: $navbar-dropdown-nav-sublist-item-color; } - -.uk-navbar-dropdown-nav .uk-nav-sub a:hover, -.uk-navbar-dropdown-nav .uk-nav-sub a:focus { color: $navbar-dropdown-nav-sublist-item-hover-color; } - - -/* Dropbar - ========================================================================== */ - -.uk-navbar-dropbar { - background: $navbar-dropbar-background; - @if(mixin-exists(hook-navbar-dropbar)) {@include hook-navbar-dropbar();} -} - -/* - * Slide modifier - */ - -.uk-navbar-dropbar-slide { - position: absolute; - z-index: $navbar-dropbar-z-index; - left: 0; - right: 0; - @if(mixin-exists(hook-navbar-dropbar-slide)) {@include hook-navbar-dropbar-slide();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-navbar-misc)) {@include hook-navbar-misc();} - -// @mixin hook-navbar(){} -// @mixin hook-navbar-container(){} -// @mixin hook-navbar-nav-item(){} -// @mixin hook-navbar-nav-item-hover(){} -// @mixin hook-navbar-nav-item-onclick(){} -// @mixin hook-navbar-nav-item-active(){} -// @mixin hook-navbar-item(){} -// @mixin hook-navbar-toggle(){} -// @mixin hook-navbar-toggle-hover(){} -// @mixin hook-navbar-toggle-icon(){} -// @mixin hook-navbar-toggle-icon-hover(){} -// @mixin hook-navbar-subtitle(){} -// @mixin hook-navbar-transparent(){} -// @mixin hook-navbar-sticky(){} -// @mixin hook-navbar-dropdown(){} -// @mixin hook-navbar-dropdown-dropbar(){} -// @mixin hook-navbar-dropdown-nav(){} -// @mixin hook-navbar-dropdown-nav-item(){} -// @mixin hook-navbar-dropdown-nav-item-hover(){} -// @mixin hook-navbar-dropdown-nav-item-active(){} -// @mixin hook-navbar-dropdown-nav-header(){} -// @mixin hook-navbar-dropdown-nav-divider(){} -// @mixin hook-navbar-dropbar(){} -// @mixin hook-navbar-dropbar-slide(){} -// @mixin hook-navbar-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-navbar-nav-item-color: $inverse-global-muted-color !default; -$inverse-navbar-nav-item-hover-color: $inverse-global-color !default; -$inverse-navbar-nav-item-onclick-color: $inverse-global-emphasis-color !default; -$inverse-navbar-nav-item-active-color: $inverse-global-emphasis-color !default; -$inverse-navbar-item-color: $inverse-global-color !default; -$inverse-navbar-toggle-color: $inverse-global-muted-color !default; -$inverse-navbar-toggle-hover-color: $inverse-global-color !default; - - - -// @mixin hook-inverse-navbar-nav-item(){} -// @mixin hook-inverse-navbar-nav-item-hover(){} -// @mixin hook-inverse-navbar-nav-item-onclick(){} -// @mixin hook-inverse-navbar-nav-item-active(){} -// @mixin hook-inverse-navbar-item(){} -// @mixin hook-inverse-navbar-toggle(){} -// @mixin hook-inverse-navbar-toggle-hover(){} diff --git a/_sass/uikit/components/notification.scss b/_sass/uikit/components/notification.scss deleted file mode 100644 index cc1ba55214..0000000000 --- a/_sass/uikit/components/notification.scss +++ /dev/null @@ -1,190 +0,0 @@ -// Name: Notification -// Description: Component to create notification messages -// -// Component: `uk-notification` -// -// Sub-objects: `uk-notification-message` -// -// Adopted: `uk-notification-close` -// -// Modifiers: `uk-notification-top-center` -// `uk-notification-top-right` -// `uk-notification-bottom-left` -// `uk-notification-bottom-center` -// `uk-notification-bottom-right` -// `uk-notification-message-primary` -// `uk-notification-message-success` -// `uk-notification-message-warning` -// `uk-notification-message-danger` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$notification-position: 10px !default; -$notification-z-index: $global-z-index + 40 !default; -$notification-width: 350px !default; - -$notification-message-margin-bottom: 10px !default; -$notification-message-padding: $global-small-gutter !default; -$notification-message-background: $global-muted-background !default; -$notification-message-color: $global-color !default; -$notification-message-font-size: $global-medium-font-size !default; -$notification-message-line-height: 1.4 !default; - -$notification-close-top: $notification-message-padding + 5px !default; -$notification-close-right: $notification-message-padding !default; - -$notification-message-primary-color: $global-primary-background !default; -$notification-message-success-color: $global-success-background !default; -$notification-message-warning-color: $global-warning-background !default; -$notification-message-danger-color: $global-danger-background !default; - - -/* ======================================================================== - Component: Notification - ========================================================================== */ - -/* - * 1. Set position - * 2. Dimensions - */ - -.uk-notification { - /* 1 */ - position: fixed; - top: $notification-position; - left: $notification-position; - z-index: $notification-z-index; - /* 2 */ - box-sizing: border-box; - width: $notification-width; - @if(mixin-exists(hook-notification)) {@include hook-notification();} -} - - -/* Position modifiers -========================================================================== */ - -.uk-notification-top-right, -.uk-notification-bottom-right { - left: auto; - right: $notification-position; -} - -.uk-notification-top-center, -.uk-notification-bottom-center { - left: 50%; - margin-left: ($notification-width / -2); -} - -.uk-notification-bottom-left, -.uk-notification-bottom-right, -.uk-notification-bottom-center { - top: auto; - bottom: $notification-position; -} - - -/* Responsiveness -========================================================================== */ - -/* Phones portrait and smaller */ -@media (max-width: $breakpoint-xsmall-max) { - - .uk-notification { - left: $notification-position; - right: $notification-position; - width: auto; - margin: 0; - } - -} - - -/* Message -========================================================================== */ - -.uk-notification-message { - position: relative; - margin-bottom: $notification-message-margin-bottom; - padding: $notification-message-padding; - background: $notification-message-background; - color: $notification-message-color; - font-size: $notification-message-font-size; - line-height: $notification-message-line-height; - cursor: pointer; - @if(mixin-exists(hook-notification-message)) {@include hook-notification-message();} -} - - -/* Close - * Adopts `uk-close` - ========================================================================== */ - -.uk-notification-close { - display: none; - position: absolute; - top: $notification-close-top; - right: $notification-close-right; - @if(mixin-exists(hook-notification-close)) {@include hook-notification-close();} -} - -.uk-notification-message:hover .uk-notification-close { display: block; } - - -/* Style modifiers - ========================================================================== */ - -/* - * Primary - */ - -.uk-notification-message-primary { - color: $notification-message-primary-color; - @if(mixin-exists(hook-notification-message-primary)) {@include hook-notification-message-primary();} -} - -/* - * Success - */ - -.uk-notification-message-success { - color: $notification-message-success-color; - @if(mixin-exists(hook-notification-message-success)) {@include hook-notification-message-success();} -} - -/* - * Warning - */ - -.uk-notification-message-warning { - color: $notification-message-warning-color; - @if(mixin-exists(hook-notification-message-warning)) {@include hook-notification-message-warning();} -} - -/* - * Danger - */ - -.uk-notification-message-danger { - color: $notification-message-danger-color; - @if(mixin-exists(hook-notification-message-danger)) {@include hook-notification-message-danger();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-notification-misc)) {@include hook-notification-misc();} - -// @mixin hook-notification(){} -// @mixin hook-notification-message(){} -// @mixin hook-notification-close(){} -// @mixin hook-notification-message-primary(){} -// @mixin hook-notification-message-success(){} -// @mixin hook-notification-message-warning(){} -// @mixin hook-notification-message-danger(){} -// @mixin hook-notification-misc(){} diff --git a/_sass/uikit/components/offcanvas.scss b/_sass/uikit/components/offcanvas.scss deleted file mode 100644 index 5fc055873a..0000000000 --- a/_sass/uikit/components/offcanvas.scss +++ /dev/null @@ -1,301 +0,0 @@ -// Name: Off-canvas -// Description: Component to create an off-canvas sidebar -// -// Component: `uk-offcanvas` -// -// Sub-objects: `uk-offcanvas-bar` -// `uk-offcanvas-container` -// `uk-offcanvas-content` -// `uk-offcanvas-page` -// -// Adopted: `uk-offcanvas-close` -// -// Modifiers: `uk-offcanvas-flip` -// `uk-offcanvas-bar-animation` -// `uk-offcanvas-reveal` -// `uk-offcanvas-overlay` -// `uk-offcanvas-content-animation` -// -// States: `uk-open` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$offcanvas-z-index: $global-z-index !default; - -$offcanvas-bar-width: 270px !default; -$offcanvas-bar-padding-vertical: $global-margin !default; -$offcanvas-bar-padding-horizontal: $global-margin !default; -$offcanvas-bar-background: $global-secondary-background !default; -$offcanvas-bar-color-mode: light !default; - -$offcanvas-bar-width-m: 350px !default; -$offcanvas-bar-padding-vertical-m: $global-medium-gutter !default; -$offcanvas-bar-padding-horizontal-m: $global-medium-gutter !default; - -$offcanvas-close-position: 20px !default; -$offcanvas-close-padding: 5px !default; - -$offcanvas-overlay-background: rgba(0,0,0,0.1) !default; - - -/* ======================================================================== - Component: Off-canvas - ========================================================================== */ - -/* - * 1. Hide by default - * 2. Set position - */ - -.uk-offcanvas { - /* 1 */ - display: none; - /* 2 */ - position: fixed; - top: 0; - bottom: 0; - left: 0; - z-index: $offcanvas-z-index; -} - -/* - * Flip modifier - */ - -.uk-offcanvas-flip .uk-offcanvas { - right: 0; - left: auto; -} - - -/* Bar - ========================================================================== */ - -/* - * 1. Set position - * 2. Size and style - * 3. Allow scrolling - * 4. Transform - */ - -.uk-offcanvas-bar { - /* 1 */ - position: absolute; - top: 0; - bottom: 0; - left: 0; - /* 2 */ - box-sizing: border-box; - width: $offcanvas-bar-width; - padding: $offcanvas-bar-padding-vertical $offcanvas-bar-padding-horizontal; - background: $offcanvas-bar-background; - /* 3 */ - overflow-y: auto; - -webkit-overflow-scrolling: touch; - /* 4 */ - transform: translateX(-100%); - @if(mixin-exists(hook-offcanvas-bar)) {@include hook-offcanvas-bar();} -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-offcanvas-bar { - width: $offcanvas-bar-width-m; - padding: $offcanvas-bar-padding-vertical-m $offcanvas-bar-padding-horizontal-m; - } - -} - -// Color Mode -@if ( $offcanvas-bar-color-mode == light ) { .uk-offcanvas-bar { @extend .uk-light !optional;} } -@if ( $offcanvas-bar-color-mode == dark ) { .uk-offcanvas-bar { @extend .uk-dark !optional;} } - -/* Flip modifier */ -.uk-offcanvas-flip .uk-offcanvas-bar { - left: auto; - right: 0; - transform: translateX(100%); -} - -/* - * Open - */ - -.uk-open > .uk-offcanvas-bar { transform: translateX(0); } - -/* - * Slide Animation (Used in slide and push mode) - */ - -.uk-offcanvas-bar-animation { transition: transform 0.3s ease-out; } - -/* - * Reveal Animation - * 1. Set position - * 2. Clip the bar - * 3. Animation - * 4. Reset transform - */ - -.uk-offcanvas-reveal { - /* 1 */ - position: absolute; - top: 0; - bottom: 0; - left: 0; - /* 2 */ - width: 0; - overflow: hidden; - /* 3 */ - transition: width 0.3s ease-out; -} - -.uk-offcanvas-reveal .uk-offcanvas-bar { - /* 4 */ - transform: translateX(0); -} - -.uk-open > .uk-offcanvas-reveal { width: $offcanvas-bar-width; } - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-open > .uk-offcanvas-reveal { width: $offcanvas-bar-width-m; } - -} - -/* - * Flip modifier - */ - -.uk-offcanvas-flip .uk-offcanvas-reveal { - right: 0; - left: auto; -} - - -/* Close - * Adopts `uk-close` - ========================================================================== */ - -.uk-offcanvas-close { - position: absolute; - z-index: $offcanvas-z-index; - top: $offcanvas-close-position; - right: $offcanvas-close-position; - padding: $offcanvas-close-padding; - @if(mixin-exists(hook-offcanvas-close)) {@include hook-offcanvas-close();} -} - - -/* Overlay - ========================================================================== */ - -/* - * Overlay the whole page. Needed for the `::before` - * 1. Using `100vw` so no modification is needed when off-canvas is flipped - * 2. Allow for closing with swipe gesture on devices with pointer events. - */ - -.uk-offcanvas-overlay { - /* 1 */ - width: 100vw; - /* 2 */ - touch-action: none; -} - -/* - * 1. Mask the whole page - * 2. Fade-in transition - */ - -.uk-offcanvas-overlay::before { - /* 1 */ - content: ""; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - background: $offcanvas-overlay-background; - /* 2 */ - opacity: 0; - transition: opacity 0.15s linear; - @if(mixin-exists(hook-offcanvas-overlay)) {@include hook-offcanvas-overlay();} -} - -.uk-offcanvas-overlay.uk-open::before { opacity: 1; } - - -/* Container - ========================================================================== */ - -/* - * Prevent horizontal scrollbar when the content is slide-out - * Has to be on the `html` element too to make it work on the `body` - */ - -.uk-offcanvas-page, -.uk-offcanvas-container { overflow-x: hidden; } - -/* - * Prevent all scrollbars if overlay is used - */ - -.uk-offcanvas-container-overlay { overflow: hidden; } - - -/* Content - ========================================================================== */ - -/* - * Prepare slide-out animation (Used in reveal and push mode) - * Using `position: left` instead of `transform` because position `fixed` elements like sticky navbars - * lose their fixed state and behaves like `absolute` within a transformed container - * Note: JS sets a fixed width and height so the page can slide-out without shrinking - * 1. Smooth scrolling - */ - -.uk-offcanvas-container .uk-offcanvas-content { - position: relative; - left: 0; - transition: left 0.3s ease-out; - /* 1 */ - -webkit-overflow-scrolling: touch; -} - -/* Disable scrolling if overlay mode */ -.uk-offcanvas-overlay .uk-offcanvas-content { overflow-y: hidden; } - -/* - * Activate slide-out animation - */ - -:not(.uk-offcanvas-flip) > .uk-offcanvas-content-animation { left: $offcanvas-bar-width; } - -.uk-offcanvas-flip > .uk-offcanvas-content-animation { left: (-$offcanvas-bar-width); } - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - :not(.uk-offcanvas-flip) > .uk-offcanvas-content-animation { left: $offcanvas-bar-width-m; } - - .uk-offcanvas-flip > .uk-offcanvas-content-animation { left: (-$offcanvas-bar-width-m); } - -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-offcanvas-misc)) {@include hook-offcanvas-misc();} - -// @mixin hook-offcanvas-bar(){} -// @mixin hook-offcanvas-close(){} -// @mixin hook-offcanvas-overlay(){} -// @mixin hook-offcanvas-misc(){} diff --git a/_sass/uikit/components/overlay.scss b/_sass/uikit/components/overlay.scss deleted file mode 100644 index c3eb0a5711..0000000000 --- a/_sass/uikit/components/overlay.scss +++ /dev/null @@ -1,85 +0,0 @@ -// Name: Overlay -// Description: Component to create content areas overlaying an image -// -// Component: `uk-overlay` -// -// Adopted: `uk-overlay-icon` -// -// Modifier: `uk-overlay-default` -// `uk-overlay-primary` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$overlay-padding-horizontal: $global-gutter !default; -$overlay-padding-vertical: $global-gutter !default; - -$overlay-default-background: rgba($global-background, 0.8) !default; - -$overlay-primary-background: rgba($global-secondary-background, 0.8) !default; -$overlay-primary-color-mode: light !default; - - -/* ======================================================================== - Component: Overlay - ========================================================================== */ - -.uk-overlay { - padding: $overlay-padding-vertical $overlay-padding-horizontal; - @if(mixin-exists(hook-overlay)) {@include hook-overlay();} -} - -/* - * Remove margin from the last-child - */ - -.uk-overlay > :last-child { margin-bottom: 0; } - - -/* Icon - ========================================================================== */ - -.uk-overlay-icon { - @if(mixin-exists(hook-overlay-icon)) {@include hook-overlay-icon();} -} - - -/* Style modifiers - ========================================================================== */ - -/* - * Default - */ - -.uk-overlay-default { - background: $overlay-default-background; - @if(mixin-exists(hook-overlay-default)) {@include hook-overlay-default();} -} - -/* - * Primary - */ - -.uk-overlay-primary { - background: $overlay-primary-background; - @if(mixin-exists(hook-overlay-primary)) {@include hook-overlay-primary();} -} - -// Color Mode -@if ( $overlay-primary-color-mode == light ) { .uk-overlay-primary { @extend .uk-light !optional;} } -@if ( $overlay-primary-color-mode == dark ) { .uk-overlay-primary { @extend .uk-dark !optional;} } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-overlay-misc)) {@include hook-overlay-misc();} - -// @mixin hook-overlay(){} -// @mixin hook-overlay-icon(){} -// @mixin hook-overlay-default(){} -// @mixin hook-overlay-primary(){} -// @mixin hook-overlay-misc(){} diff --git a/_sass/uikit/components/padding.scss b/_sass/uikit/components/padding.scss deleted file mode 100644 index 0c0f1ed10b..0000000000 --- a/_sass/uikit/components/padding.scss +++ /dev/null @@ -1,81 +0,0 @@ -// Name: Padding -// Description: Utilities for padding -// -// Component: `uk-padding` -// `uk-padding-large` -// `uk-padding-remove-*` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$padding-padding: $global-gutter !default; -$padding-padding-l: $global-medium-gutter !default; - -$padding-small-padding: $global-small-gutter !default; - -$padding-large-padding: $global-gutter !default; -$padding-large-padding-l: $global-large-gutter !default; - - -/* ======================================================================== - Component: Padding - ========================================================================== */ - -.uk-padding { padding: $padding-padding; } - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-padding { padding: $padding-padding-l; } - -} - - -/* Small - ========================================================================== */ - -.uk-padding-small { padding: $padding-small-padding; } - - -/* Large - ========================================================================== */ - -.uk-padding-large { padding: $padding-large-padding; } - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-padding-large { padding: $padding-large-padding-l; } - -} - - -/* Remove - ========================================================================== */ - -.uk-padding-remove { padding: 0 !important; } -.uk-padding-remove-top { padding-top: 0 !important; } -.uk-padding-remove-bottom { padding-bottom: 0 !important; } -.uk-padding-remove-left { padding-left: 0 !important; } -.uk-padding-remove-right { padding-right: 0 !important; } - -.uk-padding-remove-vertical { - padding-top: 0 !important; - padding-bottom: 0 !important; -} - -.uk-padding-remove-horizontal { - padding-left: 0 !important; - padding-right: 0 !important; -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-padding-misc)) {@include hook-padding-misc();} - -// @mixin hook-padding-misc(){} diff --git a/_sass/uikit/components/pagination.scss b/_sass/uikit/components/pagination.scss deleted file mode 100644 index 5dce23ad12..0000000000 --- a/_sass/uikit/components/pagination.scss +++ /dev/null @@ -1,128 +0,0 @@ -// Name: Pagination -// Description: Component to create a page navigation -// -// Component: `uk-pagination` -// -// Adopted: `uk-pagination-next` -// `uk-pagination-previous` -// -// States: `uk-active` -// `uk-disabled` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$pagination-margin-horizontal: 20px !default; - -$pagination-item-color: $global-muted-color !default; -$pagination-item-hover-color: $global-color !default; -$pagination-item-hover-text-decoration: none !default; -$pagination-item-active-color: $global-color !default; -$pagination-item-disabled-color: $global-muted-color !default; - - -/* ======================================================================== - Component: Pagination - ========================================================================== */ - -/* - * 1. Allow items to wrap into the next line - * 2. Gutter - * 3. Reset list - */ - -.uk-pagination { - display: flex; - /* 1 */ - flex-wrap: wrap; - /* 2 */ - margin-left: (-$pagination-margin-horizontal); - /* 3 */ - padding: 0; - list-style: none; - @if(mixin-exists(hook-pagination)) {@include hook-pagination();} -} - -/* - * 1. Space is allocated solely based on content dimensions: 0 0 auto - * 2. Gutter - * 3. Create position context for dropdowns - */ - -.uk-pagination > * { - /* 1 */ - flex: none; - /* 2 */ - padding-left: $pagination-margin-horizontal; - /* 3 */ - position: relative; -} - - -/* Items - ========================================================================== */ - -/* - * 1. Prevent gap if child element is `inline-block`, e.g. an icon - * 2. Style - */ - -.uk-pagination > * > * { - /* 1 */ - display: block; - /* 2 */ - color: $pagination-item-color; - @if(mixin-exists(hook-pagination-item)) {@include hook-pagination-item();} -} - -/* Hover + Focus */ -.uk-pagination > * > :hover, -.uk-pagination > * > :focus { - color: $pagination-item-hover-color; - text-decoration: $pagination-item-hover-text-decoration; - @if(mixin-exists(hook-pagination-item-hover)) {@include hook-pagination-item-hover();} -} - -/* Active */ -.uk-pagination > .uk-active > * { - color: $pagination-item-active-color; - @if(mixin-exists(hook-pagination-item-active)) {@include hook-pagination-item-active();} -} - -/* Disabled */ -.uk-pagination > .uk-disabled > * { - color: $pagination-item-disabled-color; - @if(mixin-exists(hook-pagination-item-disabled)) {@include hook-pagination-item-disabled();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-pagination-misc)) {@include hook-pagination-misc();} - -// @mixin hook-pagination(){} -// @mixin hook-pagination-item(){} -// @mixin hook-pagination-item-hover(){} -// @mixin hook-pagination-item-active(){} -// @mixin hook-pagination-item-disabled(){} -// @mixin hook-pagination-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-pagination-item-color: $inverse-global-muted-color !default; -$inverse-pagination-item-hover-color: $inverse-global-color !default; -$inverse-pagination-item-active-color: $inverse-global-color !default; -$inverse-pagination-item-disabled-color: $inverse-global-muted-color !default; - - - -// @mixin hook-inverse-pagination-item(){} -// @mixin hook-inverse-pagination-item-hover(){} -// @mixin hook-inverse-pagination-item-active(){} -// @mixin hook-inverse-pagination-item-disabled(){} diff --git a/_sass/uikit/components/placeholder.scss b/_sass/uikit/components/placeholder.scss deleted file mode 100644 index 05c06f7d92..0000000000 --- a/_sass/uikit/components/placeholder.scss +++ /dev/null @@ -1,45 +0,0 @@ -// Name: Placeholder -// Description: Component to create placeholder boxes -// -// Component: `uk-placeholder` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$placeholder-margin-vertical: $global-margin !default; -$placeholder-padding-vertical: $global-gutter !default; -$placeholder-padding-horizontal: $global-gutter !default; -$placeholder-background: $global-muted-background !default; - - -/* ======================================================================== - Component: Placeholder - ========================================================================== */ - -.uk-placeholder { - margin-bottom: $placeholder-margin-vertical; - padding: $placeholder-padding-vertical $placeholder-padding-horizontal; - background: $placeholder-background; - @if(mixin-exists(hook-placeholder)) {@include hook-placeholder();} -} - -/* Add margin if adjacent element */ -* + .uk-placeholder { margin-top: $placeholder-margin-vertical; } - -/* - * Remove margin from the last-child - */ - -.uk-placeholder > :last-child { margin-bottom: 0; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-placeholder-misc)) {@include hook-placeholder-misc();} - -// @mixin hook-placeholder(){} -// @mixin hook-placeholder-misc(){} \ No newline at end of file diff --git a/_sass/uikit/components/position.scss b/_sass/uikit/components/position.scss deleted file mode 100644 index 799b3587a0..0000000000 --- a/_sass/uikit/components/position.scss +++ /dev/null @@ -1,250 +0,0 @@ -// Name: Position -// Description: Utilities to position content -// -// Component: `uk-position-absolute` -// `uk-position-relative` -// `uk-position-z-index` -// `uk-position-top` -// `uk-position-bottom` -// `uk-position-left` -// `uk-position-right` -// `uk-position-top-left` -// `uk-position-top-center` -// `uk-position-top-right` -// `uk-position-bottom-left` -// `uk-position-bottom-center` -// `uk-position-bottom-right` -// `uk-position-center` -// `uk-position-center-left` -// `uk-position-center-right` -// `uk-position-cover` -// -// Modifiers: `uk-position-small` -// `uk-position-medium` -// `uk-position-large` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$position-small-margin: $global-small-gutter !default; -$position-medium-margin: $global-gutter !default; -$position-large-margin: $global-gutter !default; -$position-large-margin-l: 50px !default; - - -/* ======================================================================== - Component: Position - ========================================================================== */ - - -/* Directions - ========================================================================== */ - -[class*='uk-position-top'], -[class*='uk-position-bottom'], -[class*='uk-position-left'], -[class*='uk-position-right'], -[class*='uk-position-center'] { position: absolute !important; } - - -/* Edges - ========================================================================== */ - -/* Don't use `width: 100%` because it is wrong if the parent has padding. */ -.uk-position-top { - top: 0; - left: 0; - right: 0; -} - -.uk-position-bottom { - bottom: 0; - left: 0; - right: 0; -} - -.uk-position-left { - top: 0; - bottom: 0; - left: 0; -} - -.uk-position-right { - top: 0; - bottom: 0; - right: 0; -} - - -/* Corners - ========================================================================== */ - -.uk-position-top-left { - top: 0; - left: 0; -} - -.uk-position-top-right { - top: 0; - right: 0; -} - -.uk-position-bottom-left { - bottom: 0; - left: 0; -} - -.uk-position-bottom-right { - bottom: 0; - right: 0; -} - -/* - * Center - * 1. Fix text wrapping if content is larger than 50% of the container (Not working in Firefox) - * 2. Fix text wrapping for Firefox - */ - -.uk-position-center { - top: 50%; - left: 50%; - transform: translate(-50%,-50%); - /* 1 */ - display: table; - /* 2 */ - width: -moz-max-content; - max-width: 100%; - box-sizing: border-box; -} - -/* Vertical */ -[class*='uk-position-center-left'], -[class*='uk-position-center-right'] { - top: 50%; - transform: translateY(-50%); -} - -.uk-position-center-left { left: 0; } -.uk-position-center-right { right: 0; } - -.uk-position-center-left-out { - right: 100%; - width: max-content; -} - -.uk-position-center-right-out { - left: 100%; - width: max-content; -} - -/* Horizontal */ -.uk-position-top-center, -.uk-position-bottom-center { - left: 50%; - transform: translateX(-50%); - /* 1 */ - display: table; - /* 2 */ - width: -moz-max-content; - max-width: 100%; - box-sizing: border-box; -} - -.uk-position-top-center { top: 0; } -.uk-position-bottom-center { bottom: 0; } - - -/* Cover - ========================================================================== */ - -.uk-position-cover { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} - - -/* Utility - ========================================================================== */ - -.uk-position-relative { position: relative !important; } - -.uk-position-absolute { position: absolute !important; } - -.uk-position-fixed { position: fixed !important; } - -.uk-position-z-index { z-index: 1; } - - -/* Margin modifier - ========================================================================== */ - -/* - * Small - */ - -.uk-position-small { margin: $position-small-margin; } - -.uk-position-small.uk-position-center { transform: translate(-50%, -50%) translate(-$position-small-margin, (-$position-small-margin)); } - -.uk-position-small[class*='uk-position-center-left'], -.uk-position-small[class*='uk-position-center-right'] { transform: translateY(-50%) translateY(-$position-small-margin); } - -.uk-position-small.uk-position-top-center, -.uk-position-small.uk-position-bottom-center { transform: translateX(-50%) translateX(-$position-small-margin); } - -/* - * Medium - */ - -.uk-position-medium { margin: $position-medium-margin; } - -.uk-position-medium.uk-position-center { transform: translate(-50%, -50%) translate(-$position-medium-margin, (-$position-medium-margin)); } - -.uk-position-medium[class*='uk-position-center-left'], -.uk-position-medium[class*='uk-position-center-right'] { transform: translateY(-50%) translateY(-$position-medium-margin); } - -.uk-position-medium.uk-position-top-center, -.uk-position-medium.uk-position-bottom-center { transform: translateX(-50%) translateX(-$position-medium-margin); } - -/* - * Large - */ - -.uk-position-large { margin: $position-large-margin; } - -.uk-position-large.uk-position-center { transform: translate(-50%, -50%) translate(-$position-large-margin, (-$position-large-margin)); } - -.uk-position-large[class*='uk-position-center-left'], -.uk-position-large[class*='uk-position-center-right'] { transform: translateY(-50%) translateY(-$position-large-margin); } - -.uk-position-large.uk-position-top-center, -.uk-position-large.uk-position-bottom-center { transform: translateX(-50%) translateX(-$position-large-margin); } - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-position-large { margin: $position-large-margin-l; } - - .uk-position-large.uk-position-center { transform: translate(-50%, -50%) translate(-$position-large-margin-l, (-$position-large-margin-l)); } - - .uk-position-large[class*='uk-position-center-left'], - .uk-position-large[class*='uk-position-center-right'] { transform: translateY(-50%) translateY(-$position-large-margin-l); } - - .uk-position-large.uk-position-top-center, - .uk-position-large.uk-position-bottom-center { transform: translateX(-50%) translateX(-$position-large-margin-l); } - -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-position-misc)) {@include hook-position-misc();} - -// @mixin hook-position-misc(){} diff --git a/_sass/uikit/components/print.scss b/_sass/uikit/components/print.scss deleted file mode 100644 index 6162df525f..0000000000 --- a/_sass/uikit/components/print.scss +++ /dev/null @@ -1,61 +0,0 @@ -// Name: Print -// Description: Optimize page for printing -// -// Adapted from http://github.com/h5bp/html5-boilerplate -// -// Modifications: Removed link `href` and `title` related rules -// -// ======================================================================== - - -/* ======================================================================== - Component: Print - ========================================================================== */ - -@media print { - - *, - *::before, - *::after { - background: transparent !important; - color: black !important; - box-shadow: none !important; - text-shadow: none !important; - } - - a, - a:visited { text-decoration: underline; } - - pre, - blockquote { - border: 1px solid #999; - page-break-inside: avoid; - } - - thead { display: table-header-group; } - - tr, - img { page-break-inside: avoid; } - - img { max-width: 100% !important; } - - @page { margin: 0.5cm; } - - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - - h2, - h3 { page-break-after: avoid; } - - @if(mixin-exists(hook-print)) {@include hook-print();} - -} - -// Hooks -// ======================================================================== - -// @mixin hook-print(){} diff --git a/_sass/uikit/components/progress.scss b/_sass/uikit/components/progress.scss deleted file mode 100644 index 4575513ed6..0000000000 --- a/_sass/uikit/components/progress.scss +++ /dev/null @@ -1,105 +0,0 @@ -// Name: Progress -// Description: Component to create progress bars -// -// Component: `uk-progress` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$progress-height: 15px !default; -$progress-margin-vertical: $global-margin !default; -$progress-background: $global-muted-background !default; - -$progress-bar-background: $global-primary-background !default; - - -/* ======================================================================== - Component: Progress - ========================================================================== */ - -/* - * 1. Add the correct vertical alignment in Chrome, Firefox, and Opera. - * 2. Remove default style - * 3. Behave like a block element - * 4. Remove borders in Firefox and Edge - * 5. Set background color for progress container in Firefox, IE11 and Edge - * 6. Style - */ - -.uk-progress { - /* 1 */ - vertical-align: baseline; - /* 2 */ - -webkit-appearance: none; - -moz-appearance: none; - /* 3 */ - display: block; - width: 100%; - /* 4 */ - border: 0; - /* 5 */ - background-color: $progress-background; - /* 6 */ - margin-bottom: $progress-margin-vertical; - height: $progress-height; - @if(mixin-exists(hook-progress)) {@include hook-progress();} -} - -/* Add margin if adjacent element */ -* + .uk-progress { margin-top: $progress-margin-vertical; } - -/* - * Remove animated circles for indeterminate state in IE11 and Edge - */ - -.uk-progress:indeterminate { color: transparent; } - -/* - * Progress container - * 2. Remove progress bar for indeterminate state in Firefox - */ - -.uk-progress::-webkit-progress-bar { - background-color: $progress-background; - @if(mixin-exists(hook-progress)) {@include hook-progress();} -} - -/* 2 */ -.uk-progress:indeterminate::-moz-progress-bar { width: 0; } - -/* - * Progress bar - * 1. Remove right border in IE11 and Edge - */ - -.uk-progress::-webkit-progress-value { - background-color: $progress-bar-background; - transition: width 0.6s ease; - @if(mixin-exists(hook-progress-bar)) {@include hook-progress-bar();} -} - -.uk-progress::-moz-progress-bar { - background-color: $progress-bar-background; - @if(mixin-exists(hook-progress-bar)) {@include hook-progress-bar();} -} - -.uk-progress::-ms-fill { - background-color: $progress-bar-background; - transition: width 0.6s ease; - /* 1 */ - border: 0; - @if(mixin-exists(hook-progress-bar)) {@include hook-progress-bar();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-progress-misc)) {@include hook-progress-misc();} - -// @mixin hook-progress(){} -// @mixin hook-progress-bar(){} -// @mixin hook-progress-misc(){} diff --git a/_sass/uikit/components/search.scss b/_sass/uikit/components/search.scss deleted file mode 100644 index 78a1bf5098..0000000000 --- a/_sass/uikit/components/search.scss +++ /dev/null @@ -1,328 +0,0 @@ -// Name: Search -// Description: Component to create the search -// -// Component: `uk-search` -// -// Sub-objects: `uk-search-input` -// `uk-search-toggle` -// -// Adopted: `uk-search-icon` -// -// Modifier: `uk-search-default` -// `uk-search-navbar` -// `uk-search-large` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$search-color: $global-color !default; -$search-placeholder-color: $global-muted-color !default; - -$search-icon-color: $global-muted-color !default; - -$search-default-width: 180px !default; -$search-default-height: $global-control-height !default; -$search-default-padding-horizontal: 6px !default; -$search-default-background: $global-muted-background !default; -$search-default-focus-background: $search-default-background !default; - -$search-default-icon-width: $global-control-height !default; - -$search-navbar-width: 400px !default; -$search-navbar-height: 40px !default; -$search-navbar-background: transparent !default; -$search-navbar-font-size: $global-large-font-size !default; - -$search-navbar-icon-width: 40px !default; - -$search-large-width: 500px !default; -$search-large-height: 80px !default; -$search-large-background: transparent !default; -$search-large-font-size: $global-xxlarge-font-size !default; - -$search-large-icon-width: 80px !default; - -$search-toggle-color: $global-muted-color !default; -$search-toggle-hover-color: $global-color !default; - - -/* ======================================================================== - Component: Search - ========================================================================== */ - -/* - * 1. Container fits its content - * 2. Create position context - * 3. Prevent content overflow - * 4. Reset `form` - */ - -.uk-search { - /* 1 */ - display: inline-block; - /* 2 */ - position: relative; - /* 3 */ - max-width: 100%; - /* 4 */ - margin: 0; -} - - -/* Input - ========================================================================== */ - -/* - * Remove the inner padding and cancel buttons in Chrome on OS X and Safari on OS X. - */ - -.uk-search-input::-webkit-search-cancel-button, -.uk-search-input::-webkit-search-decoration { -webkit-appearance: none; } - -/* - * Removes placeholder transparency in Firefox. - */ - -.uk-search-input::-moz-placeholder { opacity: 1; } - -/* - * 1. Define consistent box sizing. - * 2. Address margins set differently in Firefox/IE and Chrome/Safari/Opera. - * 3. Remove `border-radius` in iOS. - * 4. Change font properties to `inherit` in all browsers - * 5. Show the overflow in Edge. - * 6. Remove default style in iOS. - * 7. Vertical alignment - * 8. Take the full container width - * 9. Style - */ - -.uk-search-input { - /* 1 */ - box-sizing: border-box; - /* 2 */ - margin: 0; - /* 3 */ - border-radius: 0; - /* 4 */ - font: inherit; - /* 5 */ - overflow: visible; - /* 6 */ - -webkit-appearance: none; - /* 7 */ - vertical-align: middle; - /* 8 */ - width: 100%; - /* 9 */ - border: none; - color: $search-color; - @if(mixin-exists(hook-search-input)) {@include hook-search-input();} -} - -.uk-search-input:focus { outline: none; } - -/* Placeholder */ -.uk-search-input:-ms-input-placeholder { color: $search-placeholder-color !important; } -.uk-search-input::placeholder { color: $search-placeholder-color; } - - -/* Icon (Adopts `uk-icon`) - ========================================================================== */ - -/* - * Remove default focus style - */ - -.uk-search-icon:focus { outline: none; } - -/* - * Position above input - * 1. Set position - * 2. Center icon vertically and horizontally - * 3. Style - */ - -.uk-search .uk-search-icon { - /* 1 */ - position: absolute; - top: 0; - bottom: 0; - left: 0; - /* 2 */ - display: inline-flex; - justify-content: center; - align-items: center; - /* 3 */ - color: $search-icon-color; -} - -/* - * Required for `a`. - */ - -.uk-search .uk-search-icon:hover { color: $search-icon-color; } - -/* - * Make `input` element clickable through icon, e.g. if it's a `span` - */ - -.uk-search .uk-search-icon:not(a):not(button):not(input) { pointer-events: none; } - -/* - * Position modifier - */ - -.uk-search .uk-search-icon-flip { - right: 0; - left: auto; -} - - -/* Default modifier - ========================================================================== */ - -.uk-search-default { width: $search-default-width; } - -/* - * Input - */ - -.uk-search-default .uk-search-input { - height: $search-default-height; - padding-left: $search-default-padding-horizontal; - padding-right: $search-default-padding-horizontal; - background: $search-default-background; - @if(mixin-exists(hook-search-default-input)) {@include hook-search-default-input();} -} - -/* Focus */ -.uk-search-default .uk-search-input:focus { - background-color: $search-default-focus-background; - @if(mixin-exists(hook-search-default-input-focus)) {@include hook-search-default-input-focus();} -} - -/* - * Icon - */ - -.uk-search-default .uk-search-icon { width: $search-default-icon-width; } - -.uk-search-default .uk-search-icon:not(.uk-search-icon-flip) + .uk-search-input { padding-left: ($search-default-icon-width); } -.uk-search-default .uk-search-icon-flip + .uk-search-input { padding-right: ($search-default-icon-width); } - - -/* Navbar modifier - ========================================================================== */ - -.uk-search-navbar { width: $search-navbar-width; } - -/* - * Input - */ - -.uk-search-navbar .uk-search-input { - height: $search-navbar-height; - background: $search-navbar-background; - font-size: $search-navbar-font-size; - @if(mixin-exists(hook-search-navbar-input)) {@include hook-search-navbar-input();} -} - -/* - * Icon - */ - -.uk-search-navbar .uk-search-icon { width: $search-navbar-icon-width; } - -.uk-search-navbar .uk-search-icon:not(.uk-search-icon-flip) + .uk-search-input { padding-left: ($search-navbar-icon-width); } -.uk-search-navbar .uk-search-icon-flip + .uk-search-input { padding-right: ($search-navbar-icon-width); } - - -/* Large modifier - ========================================================================== */ - -.uk-search-large { width: $search-large-width; } - -/* - * Input - */ - -.uk-search-large .uk-search-input { - height: $search-large-height; - background: $search-large-background; - font-size: $search-large-font-size; - @if(mixin-exists(hook-search-large-input)) {@include hook-search-large-input();} -} - -/* - * Icon - */ - -.uk-search-large .uk-search-icon { width: $search-large-icon-width; } - -.uk-search-large .uk-search-icon:not(.uk-search-icon-flip) + .uk-search-input { padding-left: ($search-large-icon-width); } -.uk-search-large .uk-search-icon-flip + .uk-search-input { padding-right: ($search-large-icon-width); } - - -/* Toggle - ========================================================================== */ - -.uk-search-toggle { - color: $search-toggle-color; - @if(mixin-exists(hook-search-toggle)) {@include hook-search-toggle();} -} - -/* Hover + Focus */ -.uk-search-toggle:hover, -.uk-search-toggle:focus { - color: $search-toggle-hover-color; - @if(mixin-exists(hook-search-toggle-hover)) {@include hook-search-toggle-hover();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-search-misc)) {@include hook-search-misc();} - -// @mixin hook-search-input(){} -// @mixin hook-search-default-input(){} -// @mixin hook-search-default-input-focus(){} -// @mixin hook-search-navbar-input(){} -// @mixin hook-search-large-input(){} - -// @mixin hook-search-toggle(){} -// @mixin hook-search-toggle-hover(){} - -// @mixin hook-search-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-search-color: $inverse-global-color !default; -$inverse-search-placeholder-color: $inverse-global-muted-color !default; - -$inverse-search-icon-color: $inverse-global-muted-color !default; - -$inverse-search-default-background: $inverse-global-muted-background !default; -$inverse-search-default-focus-background: $inverse-search-default-background !default; - -$inverse-search-navbar-background: transparent !default; - -$inverse-search-large-background: transparent !default; - -$inverse-search-toggle-color: $inverse-global-muted-color !default; -$inverse-search-toggle-hover-color: $inverse-global-color !default; - - - -// @mixin hook-inverse-search-default-input(){} -// @mixin hook-inverse-search-default-input-focus(){} -// @mixin hook-inverse-search-navbar-input(){} -// @mixin hook-inverse-search-large-input(){} -// @mixin hook-inverse-search-toggle(){} -// @mixin hook-inverse-search-toggle-hover(){} diff --git a/_sass/uikit/components/section.scss b/_sass/uikit/components/section.scss deleted file mode 100644 index 3c0707a156..0000000000 --- a/_sass/uikit/components/section.scss +++ /dev/null @@ -1,226 +0,0 @@ -// Name: Section -// Description: Component to create horizontal layout section -// -// Component: `uk-section` -// -// Modifiers: `uk-section-xsmall` -// `uk-section-small` -// `uk-section-large` -// `uk-section-xlarge` -// `uk-section-default` -// `uk-section-muted` -// `uk-section-primary` -// `uk-section-secondary` -// `uk-section-overlap` -// -// States: `uk-preserve-color` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$section-padding-vertical: $global-medium-margin !default; -$section-padding-vertical-m: $global-large-margin !default; - -$section-xsmall-padding-vertical: $global-margin !default; - -$section-small-padding-vertical: $global-medium-margin !default; - -$section-large-padding-vertical: $global-large-margin !default; -$section-large-padding-vertical-m: $global-xlarge-margin !default; - -$section-xlarge-padding-vertical: $global-xlarge-margin !default; -$section-xlarge-padding-vertical-m: ($global-large-margin + $global-xlarge-margin) !default; - -$section-default-background: $global-background !default; - -$section-muted-background: $global-muted-background !default; - -$section-primary-background: $global-primary-background !default; -$section-primary-color-mode: light !default; - -$section-secondary-background: $global-secondary-background !default; -$section-secondary-color-mode: light !default; - - -/* ======================================================================== - Component: Section - ========================================================================== */ - -/* - * 1. Make it work with `100vh` and height in general - */ - -.uk-section { - box-sizing: border-box; /* 1 */ - padding-top: $section-padding-vertical; - padding-bottom: $section-padding-vertical; - @if(mixin-exists(hook-section)) {@include hook-section();} -} - -/* Desktop and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-section { - // padding-top: $section-padding-vertical-m; - // padding-bottom: $section-padding-vertical-m; - padding-top: 15px; - padding-bottom: 15px; - } - -} - -/* - * Micro clearfix - */ - -.uk-section::before, -.uk-section::after { - content: ""; - display: table; -} - -.uk-section::after { clear: both; } - -/* - * Remove margin from the last-child - */ - -.uk-section > :last-child { margin-bottom: 0; } - - -/* Size modifiers - ========================================================================== */ - -/* - * XSmall - */ - -.uk-section-xsmall { - padding-top: $section-xsmall-padding-vertical; - padding-bottom: $section-xsmall-padding-vertical; -} - -/* - * Small - */ - -.uk-section-small { - padding-top: $section-small-padding-vertical; - padding-bottom: $section-small-padding-vertical; -} - -/* - * Large - */ - -.uk-section-large { - padding-top: $section-large-padding-vertical; - padding-bottom: $section-large-padding-vertical; -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-section-large { - padding-top: $section-large-padding-vertical-m; - padding-bottom: $section-large-padding-vertical-m; - } - -} - - -/* - * XLarge - */ - -.uk-section-xlarge { - padding-top: $section-xlarge-padding-vertical; - padding-bottom: $section-xlarge-padding-vertical; -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-section-xlarge { - padding-top: $section-xlarge-padding-vertical-m; - padding-bottom: $section-xlarge-padding-vertical-m; - } - -} - - -/* Style modifiers - ========================================================================== */ - -/* - * Default - */ - -.uk-section-default { - background: $section-default-background; - @if(mixin-exists(hook-section-default)) {@include hook-section-default();} -} - -/* - * Muted - */ - -.uk-section-muted { - background: $section-muted-background; - @if(mixin-exists(hook-section-muted)) {@include hook-section-muted();} -} - -/* - * Primary - */ - -.uk-section-primary { - background: $section-primary-background; - @if(mixin-exists(hook-section-primary)) {@include hook-section-primary();} -} - -@if ( $section-primary-color-mode == light ) { .uk-section-primary:not(.uk-preserve-color) { @extend .uk-light !optional;} } -@if ( $section-primary-color-mode == dark ) { .uk-section-primary:not(.uk-preserve-color) { @extend .uk-dark !optional;} } - - -/* - * Secondary - */ - -.uk-section-secondary { - background: $section-secondary-background; - @if(mixin-exists(hook-section-secondary)) {@include hook-section-secondary();} -} - -@if ( $section-secondary-color-mode == light ) { .uk-section-secondary:not(.uk-preserve-color) { @extend .uk-light !optional;} } -@if ( $section-secondary-color-mode == dark ) { .uk-section-secondary:not(.uk-preserve-color) { @extend .uk-dark !optional;} } - - -/* Overlap modifier - ========================================================================== */ - -/* - * Reserved modifier to make a section overlap another section with an border image - * Implemented by the theme - */ - -.uk-section-overlap { - @if(mixin-exists(hook-section-overlap)) {@include hook-section-overlap();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-section-misc)) {@include hook-section-misc();} - -// @mixin hook-section(){} -// @mixin hook-section-default(){} -// @mixin hook-section-muted(){} -// @mixin hook-section-secondary(){} -// @mixin hook-section-primary(){} -// @mixin hook-section-overlap(){} -// @mixin hook-section-misc(){} diff --git a/_sass/uikit/components/slidenav.scss b/_sass/uikit/components/slidenav.scss deleted file mode 100644 index 0b9af8f256..0000000000 --- a/_sass/uikit/components/slidenav.scss +++ /dev/null @@ -1,122 +0,0 @@ -// Name: Slidenav -// Description: Component to create previous/next icon navigations -// -// Component: `uk-slidenav` -// -// Sub-objects: `uk-slidenav-container` -// -// Modifiers: `uk-slidenav-previous` -// `uk-slidenav-next` -// `uk-slidenav-large` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$slidenav-padding-vertical: 5px !default; -$slidenav-padding-horizontal: 10px !default; - -$slidenav-color: rgba($global-color, 0.5) !default; -$slidenav-hover-color: rgba($global-color, 0.9) !default; -$slidenav-active-color: rgba($global-color, 0.5) !default; - -$slidenav-large-padding-vertical: 10px !default; -$slidenav-large-padding-horizontal: $slidenav-large-padding-vertical !default; - - -/* ======================================================================== - Component: Slidenav - ========================================================================== */ - -/* - * Adopts `uk-icon` - */ - -.uk-slidenav { - padding: $slidenav-padding-vertical $slidenav-padding-horizontal; - color: $slidenav-color; - @if(mixin-exists(hook-slidenav)) {@include hook-slidenav();} -} - -/* Hover + Focus */ -.uk-slidenav:hover, -.uk-slidenav:focus { - color: $slidenav-hover-color; - outline: none; - @if(mixin-exists(hook-slidenav-hover)) {@include hook-slidenav-hover();} -} - -/* OnClick */ -.uk-slidenav:active { - color: $slidenav-active-color; - @if(mixin-exists(hook-slidenav-active)) {@include hook-slidenav-active();} -} - - -/* Icon modifier - ========================================================================== */ - -/* - * Previous - */ - -.uk-slidenav-previous { - @if(mixin-exists(hook-slidenav-previous)) {@include hook-slidenav-previous();} -} - -/* - * Next - */ - -.uk-slidenav-next { - @if(mixin-exists(hook-slidenav-next)) {@include hook-slidenav-next();} -} - - -/* Size modifier - ========================================================================== */ - -.uk-slidenav-large { - padding: $slidenav-large-padding-vertical $slidenav-large-padding-horizontal; - @if(mixin-exists(hook-slidenav-large)) {@include hook-slidenav-large();} -} - - -/* Container - ========================================================================== */ - -.uk-slidenav-container { - display: flex; - @if(mixin-exists(hook-slidenav-container)) {@include hook-slidenav-container();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-slidenav-misc)) {@include hook-slidenav-misc();} - -// @mixin hook-slidenav(){} -// @mixin hook-slidenav-hover(){} -// @mixin hook-slidenav-active(){} -// @mixin hook-slidenav-previous(){} -// @mixin hook-slidenav-next(){} -// @mixin hook-slidenav-large(){} -// @mixin hook-slidenav-container(){} -// @mixin hook-slidenav-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-slidenav-color: rgba($inverse-global-color, 0.7) !default; -$inverse-slidenav-hover-color: rgba($inverse-global-color, 0.95) !default; -$inverse-slidenav-active-color: rgba($inverse-global-color, 0.7) !default; - - - -// @mixin hook-inverse-slidenav(){} -// @mixin hook-inverse-slidenav-hover(){} -// @mixin hook-inverse-slidenav-active(){} diff --git a/_sass/uikit/components/slider.scss b/_sass/uikit/components/slider.scss deleted file mode 100644 index 2799868e28..0000000000 --- a/_sass/uikit/components/slider.scss +++ /dev/null @@ -1,99 +0,0 @@ -// Name: Slider -// Description: Component to create horizontal sliders -// -// Component: `uk-slider` -// -// Sub-objects: `uk-slider-container` -// `uk-slider-items` -// -// States: `uk-active` -// -// ======================================================================== - - -/* ======================================================================== - Component: Slider - ========================================================================== */ - -/* - * 1. Prevent tab highlighting on iOS. - */ - -.uk-slider { - /* 1 */ - -webkit-tap-highlight-color: transparent; - @if(mixin-exists(hook-slider)) {@include hook-slider();} -} - - -/* Container - ========================================================================== */ - -/* - * 1. Clip child elements - */ - -.uk-slider-container { - /* 1 */ - overflow: hidden; -} - -/* Items - ========================================================================== */ - -/* - * 1. Optimize animation - * 2. Create a containing block. In Safari it's neither created by `transform` nor `will-change`. - */ - -.uk-slider-items { - /* 1 */ - will-change: transform; - /* 2 */ - position: relative; -} - -/* - * 1. Reset list style without interfering with grid - * 2. Prevent displaying the callout information on iOS. - */ - -.uk-slider-items:not(.uk-grid) { - display: flex; - /* 1 */ - margin: 0; - padding: 0; - list-style: none; - /* 2 */ - -webkit-touch-callout: none; -} - -.uk-slider-items.uk-grid { flex-wrap: nowrap; } - - -/* Item - ========================================================================== */ - -/* - * 1. Let items take content dimensions (0 0 auto) - * 2. Create position context - * 3. Disable horizontal panning gestures in IE11 and Edge - */ - -.uk-slider-items > * { - /* 1 */ - flex: none; - /* 2 */ - position: relative; - /* 3 */ - touch-action: pan-y; -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-slider-misc)) {@include hook-slider-misc();} - -// @mixin hook-slider(){} -// @mixin hook-slider-misc(){} diff --git a/_sass/uikit/components/slideshow.scss b/_sass/uikit/components/slideshow.scss deleted file mode 100644 index fc355d419d..0000000000 --- a/_sass/uikit/components/slideshow.scss +++ /dev/null @@ -1,93 +0,0 @@ -// Name: Slideshow -// Description: Component to create slideshows -// -// Component: `uk-slideshow` -// -// Sub-objects: `uk-slideshow-items` -// -// States: `uk-active` -// -// ======================================================================== - - -/* ======================================================================== - Component: Slideshow - ========================================================================== */ - -/* - * 1. Prevent tab highlighting on iOS. - */ - -.uk-slideshow { - /* 1 */ - -webkit-tap-highlight-color: transparent; - @if(mixin-exists(hook-slideshow)) {@include hook-slideshow();} -} - - -/* Items - ========================================================================== */ - -/* - * 1. Create position and stacking context - * 2. Reset list - * 3. Clip child elements - * 4. Prevent displaying the callout information on iOS. - */ - -.uk-slideshow-items { - /* 1 */ - position: relative; - z-index: 0; - /* 2 */ - margin: 0; - padding: 0; - list-style: none; - /* 3 */ - overflow: hidden; - /* 4 */ - -webkit-touch-callout: none; -} - - -/* Item - ========================================================================== */ - -/* - * 1. Position items above each other - * 2. Take the full width - * 3. Clip child elements, e.g. for `uk-cover` - * 4. Optimize animation - * 5. Disable horizontal panning gestures in IE11 and Edge - */ - -.uk-slideshow-items > * { - /* 1 */ - position: absolute; - top: 0; - left: 0; - /* 2 */ - right: 0; - bottom: 0; - /* 3 */ - overflow: hidden; - /* 4 */ - will-change: transform, opacity; - /* 5 */ - touch-action: pan-y; -} - -/* - * Hide not active items - */ - -.uk-slideshow-items > :not(.uk-active) { display: none; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-slideshow-misc)) {@include hook-slideshow-misc();} - -// @mixin hook-slideshow(){} -// @mixin hook-slideshow-misc(){} diff --git a/_sass/uikit/components/sortable.scss b/_sass/uikit/components/sortable.scss deleted file mode 100644 index 8895f11c1f..0000000000 --- a/_sass/uikit/components/sortable.scss +++ /dev/null @@ -1,101 +0,0 @@ -// Name: Sortable -// Description: Component to create sortable grids and lists -// -// Component: `uk-sortable` -// -// Sub-objects: `uk-sortable-drag` -// `uk-sortable-placeholder` -// `uk-sortable-handle` -// -// Modifiers: `uk-sortable-empty` -// -// States: `uk-drag` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$sortable-dragged-z-index: $global-z-index + 50 !default; - -$sortable-placeholder-opacity: 0 !default; - -$sortable-empty-height: 50px !default; - - -/* ======================================================================== - Component: Sortable - ========================================================================== */ - -.uk-sortable { - position: relative; - @if(mixin-exists(hook-sortable)) {@include hook-sortable();} -} - -/* - * Deactivate browser touch actions in IE11 and Edge - */ - -.uk-sortable > * { touch-action: none; } - -/* - * Deactivate pointer-events on SVGs in Safari - */ - -.uk-sortable svg { pointer-events: none; } - -/* - * Remove margin from the last-child - */ - -.uk-sortable > :last-child { margin-bottom: 0; } - - -/* Drag - ========================================================================== */ - -.uk-sortable-drag { - position: absolute !important; - z-index: $sortable-dragged-z-index !important; - pointer-events: none; - @if(mixin-exists(hook-sortable-drag)) {@include hook-sortable-drag();} -} - - -/* Placeholder - ========================================================================== */ - -.uk-sortable-placeholder { - opacity: $sortable-placeholder-opacity; - @if(mixin-exists(hook-sortable-placeholder)) {@include hook-sortable-placeholder();} -} - - -/* Empty modifier - ========================================================================== */ - -.uk-sortable-empty { - min-height: $sortable-empty-height; - @if(mixin-exists(hook-sortable-empty)) {@include hook-sortable-empty();} -} - - -/* Handle - ========================================================================== */ - -/* Hover */ -.uk-sortable-handle:hover { cursor: move; } - - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-sortable-misc)) {@include hook-sortable-misc();} - -// @mixin hook-sortable(){} -// @mixin hook-sortable-drag(){} -// @mixin hook-sortable-placeholder(){} -// @mixin hook-sortable-empty(){} -// @mixin hook-sortable-misc(){} diff --git a/_sass/uikit/components/spinner.scss b/_sass/uikit/components/spinner.scss deleted file mode 100644 index a02f41d17b..0000000000 --- a/_sass/uikit/components/spinner.scss +++ /dev/null @@ -1,74 +0,0 @@ -// Name: Spinner -// Description: Component to create a loading spinner -// -// Component: `uk-spinner` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$spinner-size: 30px !default; -$spinner-stroke-width: 1 !default; -$spinner-radius: floor(($spinner-size - $spinner-stroke-width) / 2) !default; // Minus stroke width to prevent overflow clipping -$spinner-circumference: round(2 * 3.141 * $spinner-radius) !default; -$spinner-duration: 1.4s !default; - - -/* ======================================================================== - Component: Spinner - ========================================================================== */ - -/* - * Adopts `uk-icon` - */ - -.uk-spinner { - @if(mixin-exists(hook-spinner)) {@include hook-spinner();} -} - - -/* SVG - ========================================================================== */ - -.uk-spinner > * { animation: uk-spinner-rotate $spinner-duration linear infinite; } - -@keyframes uk-spinner-rotate { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(270deg); } -} - -/* - * Circle - */ - -.uk-spinner > * > * { - stroke-dasharray: $spinner-circumference; - stroke-dashoffset: 0; - transform-origin: center; - animation: uk-spinner-dash $spinner-duration ease-in-out infinite; - stroke-width: $spinner-stroke-width; - stroke-linecap: round; -} - -@keyframes uk-spinner-dash { - 0% { stroke-dashoffset: $spinner-circumference; } - 50% { - stroke-dashoffset: $spinner-circumference/4; - transform:rotate(135deg); - } - 100% { - stroke-dashoffset: $spinner-circumference; - transform:rotate(450deg); - } -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-spinner-misc)) {@include hook-spinner-misc();} - -// @mixin hook-spinner(){} -// @mixin hook-spinner-misc(){} diff --git a/_sass/uikit/components/sticky.scss b/_sass/uikit/components/sticky.scss deleted file mode 100644 index e8e54f9201..0000000000 --- a/_sass/uikit/components/sticky.scss +++ /dev/null @@ -1,53 +0,0 @@ -// Name: Sticky -// Description: Component to make elements sticky in the viewport -// -// Component: `uk-sticky` -// -// Modifier: `uk-sticky-fixed` -// -// States: `uk-active` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$sticky-z-index: $global-z-index - 20 !default; - -$sticky-animation-duration: 0.2s !default; -$sticky-reverse-animation-duration: 0.2s !default; - - -/* ======================================================================== - Component: Sticky - ========================================================================== */ - -/* - * 1. Resolve frame rate issues on devices with lower frame rates by forcing hardware acceleration - */ - -.uk-sticky-fixed { - z-index: $sticky-z-index; - box-sizing: border-box; - margin: 0 !important; - /* 1 */ - -webkit-backface-visibility: hidden; - backface-visibility: hidden; -} - -/* - * Faster animations - */ - -.uk-sticky[class*='uk-animation-'] { animation-duration: $sticky-animation-duration; } - -.uk-sticky.uk-animation-reverse { animation-duration: $sticky-reverse-animation-duration; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-sticky-misc)) {@include hook-sticky-misc();} - -// @mixin hook-sticky-misc(){} diff --git a/_sass/uikit/components/subnav.scss b/_sass/uikit/components/subnav.scss deleted file mode 100644 index 5397501669..0000000000 --- a/_sass/uikit/components/subnav.scss +++ /dev/null @@ -1,232 +0,0 @@ -// Name: Subnav -// Description: Component to create a sub navigation -// -// Component: `uk-subnav` -// -// Modifiers: `uk-subnav-divider` -// `uk-subnav-pill` -// -// States: `uk-active` -// `uk-first-column` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$subnav-margin-horizontal: 20px !default; - -$subnav-item-color: $global-muted-color !default; -$subnav-item-hover-color: $global-color !default; -$subnav-item-hover-text-decoration: none !default; -$subnav-item-active-color: $global-emphasis-color !default; - -$subnav-divider-margin-horizontal: $subnav-margin-horizontal !default; -$subnav-divider-border-height: 1.5em !default; -$subnav-divider-border-width: $global-border-width !default; -$subnav-divider-border: $global-border !default; - -$subnav-pill-item-padding-vertical: 5px !default; -$subnav-pill-item-padding-horizontal: 10px !default; -$subnav-pill-item-background: transparent !default; -$subnav-pill-item-color: $subnav-item-color !default; -$subnav-pill-item-hover-background: $global-muted-background !default; -$subnav-pill-item-hover-color: $global-color !default; -$subnav-pill-item-onclick-background: $subnav-pill-item-hover-background !default; -$subnav-pill-item-onclick-color: $subnav-pill-item-hover-color !default; -$subnav-pill-item-active-background: $global-primary-background !default; -$subnav-pill-item-active-color: $global-inverse-color !default; - -$subnav-item-disabled-color: $global-muted-color !default; - - -/* ======================================================================== - Component: Subnav - ========================================================================== */ - -/* - * 1. Allow items to wrap into the next line - * 2. Gutter - * 3. Reset list - */ - -.uk-subnav { - display: flex; - /* 1 */ - flex-wrap: wrap; - /* 2 */ - margin-left: (-$subnav-margin-horizontal); - /* 3 */ - padding: 0; - list-style: none; - @if(mixin-exists(hook-subnav)) {@include hook-subnav();} -} - -/* - * 1. Space is allocated solely based on content dimensions: 0 0 auto - * 2. Gutter - * 3. Create position context for dropdowns - */ - -.uk-subnav > * { - /* 1 */ - flex: none; - /* 2 */ - padding-left: $subnav-margin-horizontal; - /* 3 */ - position: relative; -} - - -/* Items - ========================================================================== */ - -/* - * Items must target `a` elements to exclude other elements (e.g. dropdowns) - * Using `:first-child` instead of `a` to support `span` elements for text - * 1. Prevent gap if child element is `inline-block`, e.g. an icon - * 2. Style - */ - -.uk-subnav > * > :first-child { - /* 1 */ - display: block; - /* 2 */ - color: $subnav-item-color; - @if(mixin-exists(hook-subnav-item)) {@include hook-subnav-item();} -} - -/* Hover + Focus */ -.uk-subnav > * > a:hover, -.uk-subnav > * > a:focus { - color: $subnav-item-hover-color; - text-decoration: $subnav-item-hover-text-decoration; - outline: none; - @if(mixin-exists(hook-subnav-item-hover)) {@include hook-subnav-item-hover();} -} - -/* Active */ -.uk-subnav > .uk-active > a { - color: $subnav-item-active-color; - @if(mixin-exists(hook-subnav-item-active)) {@include hook-subnav-item-active();} -} - - -/* Divider modifier - ========================================================================== */ - -/* - * 1. Align items and divider vertically - */ - -.uk-subnav-divider > * { - /* 1 */ - display: flex; - align-items: center; -} - -/* - * Divider - * `nth-child` makes it also work without JS if it's only one row - */ - -.uk-subnav-divider > :nth-child(n+2):not(.uk-first-column)::before { - content: ""; - height: $subnav-divider-border-height; - margin-left: ($subnav-divider-margin-horizontal - $subnav-margin-horizontal); - margin-right: $subnav-divider-margin-horizontal; - border-left: $subnav-divider-border-width solid $subnav-divider-border; - @if(mixin-exists(hook-subnav-divider)) {@include hook-subnav-divider();} -} - - -/* Pill modifier - ========================================================================== */ - -.uk-subnav-pill > * > :first-child { - padding: $subnav-pill-item-padding-vertical $subnav-pill-item-padding-horizontal; - background: $subnav-pill-item-background; - color: $subnav-pill-item-color; - @if(mixin-exists(hook-subnav-pill-item)) {@include hook-subnav-pill-item();} -} - -/* Hover + Focus */ -.uk-subnav-pill > * > a:hover, -.uk-subnav-pill > * > a:focus { - background-color: $subnav-pill-item-hover-background; - color: $subnav-pill-item-hover-color; - @if(mixin-exists(hook-subnav-pill-item-hover)) {@include hook-subnav-pill-item-hover();} -} - -/* OnClick */ -.uk-subnav-pill > * > a:active { - background-color: $subnav-pill-item-onclick-background; - color: $subnav-pill-item-onclick-color; - @if(mixin-exists(hook-subnav-pill-item-onclick)) {@include hook-subnav-pill-item-onclick();} -} - -/* Active */ -.uk-subnav-pill > .uk-active > a { - background-color: $subnav-pill-item-active-background; - color: $subnav-pill-item-active-color; - @if(mixin-exists(hook-subnav-pill-item-active)) {@include hook-subnav-pill-item-active();} -} - - -/* Disabled - * The same for all style modifiers - ========================================================================== */ - -.uk-subnav > .uk-disabled > a { - color: $subnav-item-disabled-color; - @if(mixin-exists(hook-subnav-item-disabled)) {@include hook-subnav-item-disabled();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-subnav-misc)) {@include hook-subnav-misc();} - -// @mixin hook-subnav(){} -// @mixin hook-subnav-item(){} -// @mixin hook-subnav-item-hover(){} -// @mixin hook-subnav-item-active(){} -// @mixin hook-subnav-divider(){} -// @mixin hook-subnav-pill-item(){} -// @mixin hook-subnav-pill-item-hover(){} -// @mixin hook-subnav-pill-item-onclick(){} -// @mixin hook-subnav-pill-item-active(){} -// @mixin hook-subnav-item-disabled(){} -// @mixin hook-subnav-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-subnav-item-color: $inverse-global-muted-color !default; -$inverse-subnav-item-hover-color: $inverse-global-color !default; -$inverse-subnav-item-active-color: $inverse-global-emphasis-color !default; -$inverse-subnav-divider-border: $inverse-global-border !default; -$inverse-subnav-pill-item-background: transparent !default; -$inverse-subnav-pill-item-color: $inverse-global-muted-color !default; -$inverse-subnav-pill-item-hover-background: $inverse-global-muted-background !default; -$inverse-subnav-pill-item-hover-color: $inverse-global-color !default; -$inverse-subnav-pill-item-onclick-background: $inverse-subnav-pill-item-hover-background !default; -$inverse-subnav-pill-item-onclick-color: $inverse-subnav-pill-item-hover-color !default; -$inverse-subnav-pill-item-active-background: $inverse-global-primary-background !default; -$inverse-subnav-pill-item-active-color: $inverse-global-inverse-color !default; -$inverse-subnav-item-disabled-color: $inverse-global-muted-color !default; - - - -// @mixin hook-inverse-subnav-item(){} -// @mixin hook-inverse-subnav-item-hover(){} -// @mixin hook-inverse-subnav-item-active(){} -// @mixin hook-inverse-subnav-divider(){} -// @mixin hook-inverse-subnav-pill-item(){} -// @mixin hook-inverse-subnav-pill-item-hover(){} -// @mixin hook-inverse-subnav-pill-item-onclick(){} -// @mixin hook-inverse-subnav-pill-item-active(){} -// @mixin hook-inverse-subnav-item-disabled(){} diff --git a/_sass/uikit/components/switcher.scss b/_sass/uikit/components/switcher.scss deleted file mode 100644 index 0d99cdf7b9..0000000000 --- a/_sass/uikit/components/switcher.scss +++ /dev/null @@ -1,47 +0,0 @@ -// Name: Switcher -// Description: Component to navigate through different content panes -// -// Component: `uk-switcher` -// -// States: `uk-active` -// -// ======================================================================== - - -/* ======================================================================== - Component: Switcher - ========================================================================== */ - -/* - * Reset list - */ - -.uk-switcher { - margin: 0; - padding: 0; - list-style: none; -} - - -/* Items - ========================================================================== */ - -/* - * Hide not active items - */ - -.uk-switcher > :not(.uk-active) { display: none; } - -/* - * Remove margin from the last-child - */ - -.uk-switcher > * > :last-child { margin-bottom: 0; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-switcher-misc)) {@include hook-switcher-misc();} - -// @mixin hook-switcher-misc(){} \ No newline at end of file diff --git a/_sass/uikit/components/tab.scss b/_sass/uikit/components/tab.scss deleted file mode 100644 index 87e0a678d8..0000000000 --- a/_sass/uikit/components/tab.scss +++ /dev/null @@ -1,191 +0,0 @@ -// Name: Tab -// Description: Component to create a tabbed navigation -// -// Component: `uk-tab` -// -// Modifiers: `uk-tab-bottom` -// `uk-tab-left` -// `uk-tab-right` -// -// States: `uk-active` -// `uk-disabled` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$tab-margin-horizontal: 20px !default; - -$tab-item-padding-horizontal: 10px !default; -$tab-item-padding-vertical: 5px !default; -$tab-item-color: $global-muted-color !default; -$tab-item-hover-color: $global-color !default; -$tab-item-hover-text-decoration: none !default; -$tab-item-active-color: $global-emphasis-color !default; -$tab-item-disabled-color: $global-muted-color !default; - - -/* ======================================================================== - Component: Tab - ========================================================================== */ - -/* - * 1. Allow items to wrap into the next line - * 2. Gutter - * 3. Reset list - */ - -.uk-tab { - display: flex; - /* 1 */ - flex-wrap: wrap; - /* 2 */ - margin-left: (-$tab-margin-horizontal); - /* 3 */ - padding: 0; - list-style: none; - @if(mixin-exists(hook-tab)) {@include hook-tab();} -} - -/* - * 1. Space is allocated solely based on content dimensions: 0 0 auto - * 2. Gutter - * 3. Create position context for dropdowns - */ - -.uk-tab > * { - /* 1 */ - flex: none; - /* 2 */ - padding-left: $tab-margin-horizontal; - /* 3 */ - position: relative; -} - - -/* Items - ========================================================================== */ - -/* - * Items must target `a` elements to exclude other elements (e.g. dropdowns) - * 1. Center text if a width is set - * 2. Style - */ - -.uk-tab > * > a { - /* 1 */ - display: block; - text-align: center; - /* 2 */ - padding: $tab-item-padding-vertical $tab-item-padding-horizontal; - color: $tab-item-color; - @if(mixin-exists(hook-tab-item)) {@include hook-tab-item();} -} - -/* Hover + Focus */ -.uk-tab > * > a:hover, -.uk-tab > * > a:focus { - color: $tab-item-hover-color; - text-decoration: $tab-item-hover-text-decoration; - @if(mixin-exists(hook-tab-item-hover)) {@include hook-tab-item-hover();} -} - -/* Active */ -.uk-tab > .uk-active > a { - color: $tab-item-active-color; - @if(mixin-exists(hook-tab-item-active)) {@include hook-tab-item-active();} -} - -/* Disabled */ -.uk-tab > .uk-disabled > a { - color: $tab-item-disabled-color; - @if(mixin-exists(hook-tab-item-disabled)) {@include hook-tab-item-disabled();} -} - - -/* Position modifier - ========================================================================== */ - -/* - * Bottom - */ - -.uk-tab-bottom { - @if(mixin-exists(hook-tab-bottom)) {@include hook-tab-bottom();} -} - -.uk-tab-bottom > * > a { - @if(mixin-exists(hook-tab-bottom-item)) {@include hook-tab-bottom-item();} -} - -/* - * Left + Right - * 1. Reset Gutter - */ - -.uk-tab-left, -.uk-tab-right { - flex-direction: column; - /* 1 */ - margin-left: 0; -} - -/* 1 */ -.uk-tab-left > *, -.uk-tab-right > * { padding-left: 0; } - -.uk-tab-left { - @if(mixin-exists(hook-tab-left)) {@include hook-tab-left();} -} - -.uk-tab-right { - @if(mixin-exists(hook-tab-right)) {@include hook-tab-right();} -} - -.uk-tab-left > * > a { - text-align: left; - @if(mixin-exists(hook-tab-left-item)) {@include hook-tab-left-item();} -} - -.uk-tab-right > * > a { - text-align: left; - @if(mixin-exists(hook-tab-right-item)) {@include hook-tab-right-item();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-tab-misc)) {@include hook-tab-misc();} - -// @mixin hook-tab(){} -// @mixin hook-tab-item(){} -// @mixin hook-tab-item-hover(){} -// @mixin hook-tab-item-active(){} -// @mixin hook-tab-item-disabled(){} -// @mixin hook-tab-bottom(){} -// @mixin hook-tab-bottom-item(){} -// @mixin hook-tab-left(){} -// @mixin hook-tab-left-item(){} -// @mixin hook-tab-right(){} -// @mixin hook-tab-right-item(){} -// @mixin hook-tab-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-tab-item-color: $inverse-global-muted-color !default; -$inverse-tab-item-hover-color: $inverse-global-color !default; -$inverse-tab-item-active-color: $inverse-global-emphasis-color !default; -$inverse-tab-item-disabled-color: $inverse-global-muted-color !default; - - - -// @mixin hook-inverse-tab(){} -// @mixin hook-inverse-tab-item(){} -// @mixin hook-inverse-tab-item-hover(){} -// @mixin hook-inverse-tab-item-active(){} -// @mixin hook-inverse-tab-item-disabled(){} \ No newline at end of file diff --git a/_sass/uikit/components/table.scss b/_sass/uikit/components/table.scss deleted file mode 100644 index 313d218b23..0000000000 --- a/_sass/uikit/components/table.scss +++ /dev/null @@ -1,316 +0,0 @@ -// Name: Table -// Description: Styles for tables -// -// Component: `uk-table` -// -// Modifiers: `uk-table-middle` -// `uk-table-divider` -// `uk-table-striped` -// `uk-table-hover` -// `uk-table-small` -// `uk-table-justify` -// `uk-table-shrink` -// `uk-table-expand` -// `uk-table-link` -// `uk-table-responsive` -// -// States: `uk-active` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$table-margin-vertical: $global-margin !default; - -$table-cell-padding-vertical: 16px !default; -$table-cell-padding-horizontal: 12px !default; - -$table-header-cell-font-size: $global-font-size !default; -$table-header-cell-font-weight: bold !default; -$table-header-cell-color: $global-color !default; - -$table-footer-font-size: $global-small-font-size !default; - -$table-caption-font-size: $global-small-font-size !default; -$table-caption-color: $global-muted-color !default; - -$table-row-active-background: #ffd !default; - -$table-divider-border-width: $global-border-width !default; -$table-divider-border: $global-border !default; - -$table-striped-row-background: $global-muted-background !default; - -$table-hover-row-background: $table-row-active-background !default; - -$table-small-cell-padding-vertical: 10px !default; -$table-small-cell-padding-horizontal: 12px !default; - -$table-large-cell-padding-vertical: 22px !default; -$table-large-cell-padding-horizontal: 12px !default; - -$table-expand-min-width: 150px !default; - - -/* ======================================================================== - Component: Table - ========================================================================== */ - -/* - * 1. Remove most spacing between table cells. - * 2. Behave like a block element - * 3. Style - */ - -.uk-table { - /* 1 */ - border-collapse: collapse; - border-spacing: 0; - /* 2 */ - width: 100%; - /* 3 */ - margin-bottom: $table-margin-vertical; - @if(mixin-exists(hook-table)) {@include hook-table();} -} - -/* Add margin if adjacent element */ -* + .uk-table { margin-top: $table-margin-vertical; } - - -/* Header cell - ========================================================================== */ - -/* - * 1. Style - */ - -.uk-table th { - padding: $table-cell-padding-vertical $table-cell-padding-horizontal; - text-align: left; - vertical-align: bottom; - /* 1 */ - font-size: $table-header-cell-font-size; - font-weight: $table-header-cell-font-weight; - color: $table-header-cell-color; - @if(mixin-exists(hook-table-header-cell)) {@include hook-table-header-cell();} -} - - -/* Cell - ========================================================================== */ - -.uk-table td { - padding: $table-cell-padding-vertical $table-cell-padding-horizontal; - vertical-align: top; - @if(mixin-exists(hook-table-cell)) {@include hook-table-cell();} -} - -/* - * Remove margin from the last-child - */ - -.uk-table td > :last-child { margin-bottom: 0; } - - -/* Footer - ========================================================================== */ - -.uk-table tfoot { - font-size: $table-footer-font-size; - @if(mixin-exists(hook-table-footer)) {@include hook-table-footer();} -} - - -/* Caption - ========================================================================== */ - -.uk-table caption { - font-size: $table-caption-font-size; - text-align: left; - color: $table-caption-color; - @if(mixin-exists(hook-table-caption)) {@include hook-table-caption();} -} - - -/* Row - ========================================================================== */ - -.uk-table > tr.uk-active, -.uk-table tbody tr.uk-active { - background: $table-row-active-background; - @if(mixin-exists(hook-table-row-active)) {@include hook-table-row-active();} -} - - -/* Alignment modifier - ========================================================================== */ - -.uk-table-middle, -.uk-table-middle td { vertical-align: middle !important; } - - -/* Style modifiers - ========================================================================== */ - -/* - * Divider - */ - -.uk-table-divider > tr:not(:first-child), -.uk-table-divider > :not(:first-child) > tr, -.uk-table-divider > :first-child > tr:not(:first-child) { - border-top: $table-divider-border-width solid $table-divider-border; - @if(mixin-exists(hook-table-divider)) {@include hook-table-divider();} -} - -/* - * Striped - */ - -.uk-table-striped > tr:nth-of-type(odd), -.uk-table-striped tbody tr:nth-of-type(odd) { - background: $table-striped-row-background; - @if(mixin-exists(hook-table-striped)) {@include hook-table-striped();} -} - -/* - * Hover - */ - -.uk-table-hover > tr:hover, -.uk-table-hover tbody tr:hover { - background: $table-hover-row-background; - @if(mixin-exists(hook-table-hover)) {@include hook-table-hover();} -} - - -/* Size modifier - ========================================================================== */ - -.uk-table-small th, -.uk-table-small td { - padding: $table-small-cell-padding-vertical $table-small-cell-padding-horizontal; - @if(mixin-exists(hook-table-small)) {@include hook-table-small();} -} - -.uk-table-large th, -.uk-table-large td { - padding: $table-large-cell-padding-vertical $table-large-cell-padding-horizontal; - @if(mixin-exists(hook-table-large)) {@include hook-table-large();} -} - - -/* Justify modifier - ========================================================================== */ - -.uk-table-justify th:first-child, -.uk-table-justify td:first-child { padding-left: 0; } - -.uk-table-justify th:last-child, -.uk-table-justify td:last-child { padding-right: 0; } - - -/* Cell size modifier - ========================================================================== */ - -.uk-table-shrink { width: 1px; } -.uk-table-expand { min-width: $table-expand-min-width; } - - -/* Cell link modifier - ========================================================================== */ - -/* - * Does not work with `uk-table-justify` at the moment - */ - -.uk-table-link { padding: 0 !important; } - -.uk-table-link > a { - display: block; - padding: $table-cell-padding-vertical $table-cell-padding-horizontal; -} - -.uk-table-small .uk-table-link > a { padding: $table-small-cell-padding-vertical $table-small-cell-padding-horizontal; } - - -/* Responsive table - ========================================================================== */ - - -/* Phone landscape and smaller */ -@media (max-width: $breakpoint-small-max) { - - .uk-table-responsive, - .uk-table-responsive tbody, - .uk-table-responsive th, - .uk-table-responsive td, - .uk-table-responsive tr { display: block; } - - .uk-table-responsive thead { display: none; } - - .uk-table-responsive th, - .uk-table-responsive td { - width: auto !important; - max-width: none !important; - min-width: 0 !important; - overflow: visible !important; - white-space: normal !important; - } - - .uk-table-responsive th:not(:first-child):not(.uk-table-link), - .uk-table-responsive td:not(:first-child):not(.uk-table-link), - .uk-table-responsive .uk-table-link:not(:first-child) > a { padding-top: round($table-cell-padding-vertical / 3) !important; } - - .uk-table-responsive th:not(:last-child):not(.uk-table-link), - .uk-table-responsive td:not(:last-child):not(.uk-table-link), - .uk-table-responsive .uk-table-link:not(:last-child) > a { padding-bottom: round($table-cell-padding-vertical / 3) !important; } - - .uk-table-justify.uk-table-responsive th, - .uk-table-justify.uk-table-responsive td { - padding-left: 0; - padding-right: 0; - } - -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-table-misc)) {@include hook-table-misc();} - -// @mixin hook-table(){} -// @mixin hook-table-header-cell(){} -// @mixin hook-table-cell(){} -// @mixin hook-table-footer(){} -// @mixin hook-table-caption(){} -// @mixin hook-table-row-active(){} -// @mixin hook-table-divider(){} -// @mixin hook-table-striped(){} -// @mixin hook-table-hover(){} -// @mixin hook-table-small(){} -// @mixin hook-table-large(){} -// @mixin hook-table-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-table-header-cell-color: $inverse-global-color !default; -$inverse-table-caption-color: $inverse-global-muted-color !default; -$inverse-table-row-active-background: fade-out($inverse-global-muted-background, 0.02) !default; -$inverse-table-divider-border: $inverse-global-border !default; -$inverse-table-striped-row-background: $inverse-global-muted-background !default; -$inverse-table-hover-row-background: $inverse-table-row-active-background !default; - - - -// @mixin hook-inverse-table-header-cell(){} -// @mixin hook-inverse-table-caption(){} -// @mixin hook-inverse-table-row-active(){} -// @mixin hook-inverse-table-divider(){} -// @mixin hook-inverse-table-striped(){} -// @mixin hook-inverse-table-hover(){} \ No newline at end of file diff --git a/_sass/uikit/components/text.scss b/_sass/uikit/components/text.scss deleted file mode 100644 index c60915b84b..0000000000 --- a/_sass/uikit/components/text.scss +++ /dev/null @@ -1,262 +0,0 @@ -// Name: Text -// Description: Utilities for text -// -// Component: `uk-text-*` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$text-lead-font-size: $global-large-font-size !default; -$text-lead-line-height: 1.5 !default; -$text-lead-color: $global-emphasis-color !default; - -$text-meta-font-size: $global-small-font-size !default; -$text-meta-line-height: 1.4 !default; -$text-meta-color: $global-muted-color !default; - -$text-small-font-size: $global-small-font-size !default; -$text-small-line-height: 1.5 !default; - -$text-large-font-size: $global-large-font-size !default; -$text-large-line-height: 1.5 !default; - -$text-bold-font-weight: bolder !default; - -$text-muted-color: $global-muted-color !default; -$text-primary-color: $global-primary-background !default; -$text-success-color: $global-success-background !default; -$text-warning-color: $global-warning-background !default; -$text-danger-color: $global-danger-background !default; - -$text-background-color: $global-primary-background !default; - - -/* ======================================================================== - Component: Text - ========================================================================== */ - - -/* Style modifiers - ========================================================================== */ - -.uk-text-lead { - font-size: $text-lead-font-size; - line-height: $text-lead-line-height; - color: $text-lead-color; - @if(mixin-exists(hook-text-lead)) {@include hook-text-lead();} -} - -.uk-text-meta { - font-size: $text-meta-font-size; - line-height: $text-meta-line-height; - color: $text-meta-color; - @if(mixin-exists(hook-text-meta)) {@include hook-text-meta();} -} - - -/* Size modifiers - ========================================================================== */ - -.uk-text-small { - font-size: $text-small-font-size; - line-height: $text-small-line-height; - @if(mixin-exists(hook-text-small)) {@include hook-text-small();} -} - -.uk-text-large { - font-size: $text-large-font-size; - line-height: $text-large-line-height; - @if(mixin-exists(hook-text-large)) {@include hook-text-large();} -} - - -/* Weight modifier - ========================================================================== */ - -.uk-text-bold { font-weight: $text-bold-font-weight; } - - -/* Transform modifier - ========================================================================== */ - -.uk-text-uppercase { text-transform: uppercase !important; } -.uk-text-capitalize { text-transform: capitalize !important; } -.uk-text-lowercase { text-transform: lowercase !important; } - - -/* Color modifiers - ========================================================================== */ - -.uk-text-muted { color: $text-muted-color !important; } -.uk-text-primary { color: $text-primary-color !important; } -.uk-text-success { color: $text-success-color !important; } -.uk-text-warning { color: $text-warning-color !important; } -.uk-text-danger { color: $text-danger-color !important; } - - -/* Background modifier - ========================================================================== */ - -/* - * 1. The background clips to the foreground text. Works in Chrome, Firefox, Safari, Edge and Opera - * Default color is set to transparent - * 2. Container fits the text - * 3. Fallback color for IE11 - */ - -.uk-text-background { - /* 1 */ - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - /* 2 */ - display: inline-block; - /* 3 */ - color: $text-background-color !important; -} - -@supports (-webkit-background-clip: text) { - - .uk-text-background { - background-color: $text-background-color; - @if(mixin-exists(hook-text-background)) {@include hook-text-background();} - } - -} - - -/* Alignment modifiers - ========================================================================== */ - -.uk-text-left { text-align: left !important; } -.uk-text-right { text-align: right !important; } -.uk-text-center { text-align: center !important; } -.uk-text-justify { text-align: justify !important; } - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - .uk-text-left\@s { text-align: left !important; } - .uk-text-right\@s { text-align: right !important; } - .uk-text-center\@s { text-align: center !important; } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-text-left\@m { text-align: left !important; } - .uk-text-right\@m { text-align: right !important; } - .uk-text-center\@m { text-align: center !important; } - -} - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-text-left\@l { text-align: left !important; } - .uk-text-right\@l { text-align: right !important; } - .uk-text-center\@l { text-align: center !important; } - -} - -/* Large screen and bigger */ -@media (min-width: $breakpoint-xlarge) { - - .uk-text-left\@xl { text-align: left !important; } - .uk-text-right\@xl { text-align: right !important; } - .uk-text-center\@xl { text-align: center !important; } - -} - -/* - * Vertical - */ - -.uk-text-top { vertical-align: top !important; } -.uk-text-middle { vertical-align: middle !important; } -.uk-text-bottom { vertical-align: bottom !important; } -.uk-text-baseline { vertical-align: baseline !important; } - - -/* Wrap modifiers - ========================================================================== */ - -/* - * Prevent text from wrapping onto multiple lines - */ - -.uk-text-nowrap { white-space: nowrap; } - -/* - * 1. Make sure a max-width is set after which truncation can occur - * 2. Prevent text from wrapping onto multiple lines, and truncate with an ellipsis - * 3. Fix for table cells - */ - -.uk-text-truncate { - /* 1 */ - max-width: 100%; - /* 2 */ - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* 2 */ -th.uk-text-truncate, -td.uk-text-truncate { max-width: 0; } - - -/* - * 1. Wrap long words onto the next line and break them if they are too long to fit - * 2. Legacy `word-wrap` as fallback for `overflow-wrap` - * 3. Add a hyphen where the word breaks - * 4. Fix `overflow-wrap` which doesn't work with table cells in Chrome, Opera, IE11 and Edge - * Must use `break-all` to support IE11 and Edge - */ - -.uk-text-break { - /* 1 */ - overflow-wrap: break-word; - /* 2 */ - word-wrap: break-word; - /* 3 */ - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -/* 4 */ -th.uk-text-break, -td.uk-text-break { word-break: break-all; } - - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-text-misc)) {@include hook-text-misc();} - -// @mixin hook-text-lead(){} -// @mixin hook-text-meta(){} -// @mixin hook-text-small(){} -// @mixin hook-text-large(){} -// @mixin hook-text-background(){} -// @mixin hook-text-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-text-lead-color: $inverse-global-color !default; -$inverse-text-meta-color: $inverse-global-muted-color !default; -$inverse-text-muted-color: $inverse-global-muted-color !default; -$inverse-text-primary-color: $inverse-global-color !default; - - - -// @mixin hook-inverse-text-lead(){} -// @mixin hook-inverse-text-meta(){} diff --git a/_sass/uikit/components/thumbnav.scss b/_sass/uikit/components/thumbnav.scss deleted file mode 100644 index ee551f99b6..0000000000 --- a/_sass/uikit/components/thumbnav.scss +++ /dev/null @@ -1,123 +0,0 @@ -// Name: Thumbnav -// Description: Component to create thumbnail navigations -// -// Component: `uk-thumbnav` -// -// Modifier: `uk-thumbnav-vertical` -// -// States: `uk-active` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$thumbnav-margin-horizontal: 15px !default; -$thumbnav-margin-vertical: $thumbnav-margin-horizontal !default; - - -/* ======================================================================== - Component: Thumbnav - ========================================================================== */ - -/* - * 1. Allow items to wrap into the next line - * 2. Reset list - * 3. Gutter - */ - -.uk-thumbnav { - display: flex; - /* 1 */ - flex-wrap: wrap; - /* 2 */ - margin: 0; - padding: 0; - list-style: none; - /* 3 */ - margin-left: (-$thumbnav-margin-horizontal); - @if(mixin-exists(hook-thumbnav)) {@include hook-thumbnav();} -} - -/* - * 1. Space is allocated solely based on content dimensions: 0 0 auto - * 2. Gutter - */ - -.uk-thumbnav > * { - /* 1 */ - flex: none; - /* 2 */ - padding-left: $thumbnav-margin-horizontal; -} - - -/* Items - ========================================================================== */ - -/* - * Items - */ - -.uk-thumbnav > * > * { - display: inline-block; - @if(mixin-exists(hook-thumbnav-item)) {@include hook-thumbnav-item();} -} - -/* Hover + Focus */ -.uk-thumbnav > * > :hover, -.uk-thumbnav > * > :focus { - outline: none; - @if(mixin-exists(hook-thumbnav-item-hover)) {@include hook-thumbnav-item-hover();} -} - -/* Active */ -.uk-thumbnav > .uk-active > * { - @if(mixin-exists(hook-thumbnav-item-active)) {@include hook-thumbnav-item-active();} -} - - -/* Modifier: 'uk-thumbnav-vertical' - ========================================================================== */ - -/* - * 1. Change direction - * 2. Gutter - */ - -.uk-thumbnav-vertical { - /* 1 */ - flex-direction: column; - /* 2 */ - margin-left: 0; - margin-top: (-$thumbnav-margin-vertical); -} - -/* 2 */ -.uk-thumbnav-vertical > * { - padding-left: 0; - padding-top: $thumbnav-margin-vertical; -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-thumbnav-misc)) {@include hook-thumbnav-misc();} - -// @mixin hook-thumbnav(){} -// @mixin hook-thumbnav-item(){} -// @mixin hook-thumbnav-item-hover(){} -// @mixin hook-thumbnav-item-active(){} -// @mixin hook-thumbnav-misc(){} - - -// Inverse -// ======================================================================== - - - -// @mixin hook-inverse-thumbnav-item(){} -// @mixin hook-inverse-thumbnav-item-hover(){} -// @mixin hook-inverse-thumbnav-item-active(){} \ No newline at end of file diff --git a/_sass/uikit/components/tile.scss b/_sass/uikit/components/tile.scss deleted file mode 100644 index 3a364bfb2a..0000000000 --- a/_sass/uikit/components/tile.scss +++ /dev/null @@ -1,224 +0,0 @@ -// Name: Tile -// Description: Component to create tiled boxes -// -// Component: `uk-tile` -// -// Modifiers: `uk-tile-xsmall` -// `uk-tile-small` -// `uk-tile-large` -// `uk-tile-xlarge` -// `uk-tile-default` -// `uk-tile-muted` -// `uk-tile-primary` -// `uk-tile-secondary` -// -// States: `uk-preserve-color` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$tile-padding-horizontal: 15px !default; -$tile-padding-horizontal-s: $global-gutter !default; -$tile-padding-horizontal-m: $global-medium-gutter !default; -$tile-padding-vertical: $global-medium-margin !default; -$tile-padding-vertical-m: $global-large-margin !default; - -$tile-xsmall-padding-vertical: $global-margin !default; - -$tile-small-padding-vertical: $global-medium-margin !default; - -$tile-large-padding-vertical: $global-large-margin !default; -$tile-large-padding-vertical-m: $global-xlarge-margin !default; - -$tile-xlarge-padding-vertical: $global-xlarge-margin !default; -$tile-xlarge-padding-vertical-m: ($global-large-margin + $global-xlarge-margin) !default; - -$tile-default-background: $global-background !default; - -$tile-muted-background: $global-muted-background !default; - -$tile-primary-background: $global-primary-background !default; -$tile-primary-color-mode: light !default; - -$tile-secondary-background: $global-secondary-background !default; -$tile-secondary-color-mode: light !default; - - -/* ======================================================================== - Component: Tile - ========================================================================== */ - -.uk-tile { - position: relative; - box-sizing: border-box; - padding-left: $tile-padding-horizontal; - padding-right: $tile-padding-horizontal; - padding-top: $tile-padding-vertical; - padding-bottom: $tile-padding-vertical; - @if(mixin-exists(hook-tile)) {@include hook-tile();} -} - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - .uk-tile { - padding-left: $tile-padding-horizontal-s; - padding-right: $tile-padding-horizontal-s; - } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-tile { - padding-left: $tile-padding-horizontal-m; - padding-right: $tile-padding-horizontal-m; - padding-top: $tile-padding-vertical-m; - padding-bottom: $tile-padding-vertical-m; - } - -} - -/* - * Micro clearfix - */ - -.uk-tile::before, -.uk-tile::after { - content: ""; - display: table; -} - -.uk-tile::after { clear: both; } - -/* - * Remove margin from the last-child - */ - -.uk-tile > :last-child { margin-bottom: 0; } - - -/* Size modifiers - ========================================================================== */ - -/* - * XSmall - */ - -.uk-tile-xsmall { - padding-top: $tile-xsmall-padding-vertical; - padding-bottom: $tile-xsmall-padding-vertical; -} - -/* - * Small - */ - -.uk-tile-small { - padding-top: $tile-small-padding-vertical; - padding-bottom: $tile-small-padding-vertical; -} - -/* - * Large - */ - -.uk-tile-large { - padding-top: $tile-large-padding-vertical; - padding-bottom: $tile-large-padding-vertical; -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-tile-large { - padding-top: $tile-large-padding-vertical-m; - padding-bottom: $tile-large-padding-vertical-m; - } - -} - - -/* - * XLarge - */ - -.uk-tile-xlarge { - padding-top: $tile-xlarge-padding-vertical; - padding-bottom: $tile-xlarge-padding-vertical; -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-tile-xlarge { - padding-top: $tile-xlarge-padding-vertical-m; - padding-bottom: $tile-xlarge-padding-vertical-m; - } - -} - - -/* Style modifiers - ========================================================================== */ - -/* - * Default - */ - -.uk-tile-default { - background: $tile-default-background; - @if(mixin-exists(hook-tile-default)) {@include hook-tile-default();} -} - -/* - * Muted - */ - -.uk-tile-muted { - background: $tile-muted-background; - @if(mixin-exists(hook-tile-muted)) {@include hook-tile-muted();} -} - -/* - * Primary - */ - -.uk-tile-primary { - background: $tile-primary-background; - @if(mixin-exists(hook-tile-primary)) {@include hook-tile-primary();} -} - -// Color Mode -@if ( $tile-primary-color-mode == light ) { .uk-tile-primary:not(.uk-preserve-color) { @extend .uk-light !optional;} } -@if ( $tile-primary-color-mode == dark ) { .uk-tile-primary:not(.uk-preserve-color) { @extend .uk-dark !optional;} } - -/* - * Secondary - */ - -.uk-tile-secondary { - background: $tile-secondary-background; - @if(mixin-exists(hook-tile-secondary)) {@include hook-tile-secondary();} -} - -// Color Mode -@if ( $tile-secondary-color-mode == light ) { .uk-tile-secondary:not(.uk-preserve-color) { @extend .uk-light !optional;} } -@if ( $tile-secondary-color-mode == dark ) { .uk-tile-secondary:not(.uk-preserve-color) { @extend .uk-dark !optional;} } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-tile-misc)) {@include hook-tile-misc();} - -// @mixin hook-tile(){} -// @mixin hook-tile-default(){} -// @mixin hook-tile-muted(){} -// @mixin hook-tile-primary(){} -// @mixin hook-tile-secondary(){} -// @mixin hook-tile-misc(){} diff --git a/_sass/uikit/components/tooltip.scss b/_sass/uikit/components/tooltip.scss deleted file mode 100644 index 1f8e8c88f6..0000000000 --- a/_sass/uikit/components/tooltip.scss +++ /dev/null @@ -1,84 +0,0 @@ -// Name: Tooltip -// Description: Component to create tooltips -// -// Component: `uk-tooltip` -// -// Modifiers `uk-tooltip-top` -// `uk-tooltip-top-left` -// `uk-tooltip-top-right` -// `uk-tooltip-bottom` -// `uk-tooltip-bottom-left` -// `uk-tooltip-bottom-right` -// `uk-tooltip-left` -// `uk-tooltip-right` -// -// States: `uk-active` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$tooltip-z-index: $global-z-index + 30 !default; -$tooltip-max-width: 200px !default; -$tooltip-padding-vertical: 3px !default; -$tooltip-padding-horizontal: 6px !default; -$tooltip-background: #666 !default; -$tooltip-border-radius: 2px !default; -$tooltip-color: $global-inverse-color !default; -$tooltip-font-size: 12px !default; - -$tooltip-margin: 10px !default; - - -/* ======================================================================== - Component: Tooltip - ========================================================================== */ - -/* - * 1. Hide by default - * 2. Position - * 3. Dimensions - * 4. Style - */ - -.uk-tooltip { - /* 1 */ - display: none; - /* 2 */ - position: absolute; - z-index: $tooltip-z-index; - /* 3 */ - box-sizing: border-box; - max-width: $tooltip-max-width; - padding: $tooltip-padding-vertical $tooltip-padding-horizontal; - /* 4 */ - background: $tooltip-background; - border-radius: $tooltip-border-radius; - color: $tooltip-color; - font-size: $tooltip-font-size; - @if(mixin-exists(hook-tooltip)) {@include hook-tooltip();} -} - -/* Show */ -.uk-tooltip.uk-active { display: block; } - - -/* Direction / Alignment modifiers - ========================================================================== */ - -/* Direction */ -[class*='uk-tooltip-top'] { margin-top: (-$tooltip-margin); } -[class*='uk-tooltip-bottom'] { margin-top: $tooltip-margin; } -[class*='uk-tooltip-left'] { margin-left: (-$tooltip-margin); } -[class*='uk-tooltip-right'] { margin-left: $tooltip-margin; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-tooltip-misc)) {@include hook-tooltip-misc();} - -// @mixin hook-tooltip(){} -// @mixin hook-tooltip-misc(){} diff --git a/_sass/uikit/components/totop.scss b/_sass/uikit/components/totop.scss deleted file mode 100644 index 4b8aa1d88f..0000000000 --- a/_sass/uikit/components/totop.scss +++ /dev/null @@ -1,71 +0,0 @@ -// Name: Totop -// Description: Component to create an icon to scroll back to top -// -// Component: `uk-totop` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$totop-padding: 5px !default; -$totop-color: $global-muted-color !default; - -$totop-hover-color: $global-color !default; - -$totop-active-color: $global-emphasis-color !default; - - -/* ======================================================================== - Component: Totop - ========================================================================== */ - -/* - * Addopts `uk-icon` - */ - -.uk-totop { - padding: $totop-padding; - color: $totop-color; - @if(mixin-exists(hook-totop)) {@include hook-totop();} -} - -/* Hover + Focus */ -.uk-totop:hover, -.uk-totop:focus { - color: $totop-hover-color; - outline: none; - @if(mixin-exists(hook-totop-hover)) {@include hook-totop-hover();} -} - -/* OnClick */ -.uk-totop:active { - color: $totop-active-color; - @if(mixin-exists(hook-totop-active)) {@include hook-totop-active();} -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-totop-misc)) {@include hook-totop-misc();} - -// @mixin hook-totop(){} -// @mixin hook-totop-hover(){} -// @mixin hook-totop-active(){} -// @mixin hook-totop-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-totop-color: $inverse-global-muted-color !default; -$inverse-totop-hover-color: $inverse-global-color !default; -$inverse-totop-active-color: $inverse-global-emphasis-color !default; - - - -// @mixin hook-inverse-totop(){} -// @mixin hook-inverse-totop-hover(){} -// @mixin hook-inverse-totop-active(){} diff --git a/_sass/uikit/components/transition.scss b/_sass/uikit/components/transition.scss deleted file mode 100644 index c99927433b..0000000000 --- a/_sass/uikit/components/transition.scss +++ /dev/null @@ -1,145 +0,0 @@ -// Name: Transition -// Description: Utilities for transitions -// -// Component: `uk-transition-*` -// -// Modifiers: `uk-transition-fade` -// `uk-transition-scale-up` -// `uk-transition-scale-down` -// `uk-transition-slide-top-*` -// `uk-transition-slide-bottom-*` -// `uk-transition-slide-left-*` -// `uk-transition-slide-right-*` -// `uk-transition-opaque` -// `uk-transition-slow` -// -// Sub-objects: `uk-transition-toggle`, -// `uk-transition-active` -// -// States: `uk-hover` -// `uk-active` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$transition-duration: 0.3s !default; - -$transition-scale: 1.1 !default; - -$transition-slide-small-translate: 10px !default; -$transition-slide-medium-translate: 50px !default; - -$transition-slow-duration: 0.7s !default; - - -/* ======================================================================== - Component: Transition - ========================================================================== */ - -/* - * Using multiple selectors to exclude `uk-transition-toggle` - * Note: Transitions don't work with `uk-postion-center-*` classes because they also use `transform` - * Just put the transition in an extra `div` - */ - -.uk-transition-fade, -[class*='uk-transition-scale'], -[class*='uk-transition-slide'] { - transition: $transition-duration ease-out; - transition-property: opacity, transform, filter; -} - -.uk-transition-toggle:focus { outline: none; } - -/* - * Fade - */ - -.uk-transition-fade { opacity: 0; } - -/* Show */ -.uk-transition-toggle:hover [class*='uk-transition-fade'], -.uk-transition-toggle.uk-hover [class*='uk-transition-fade'], -.uk-transition-toggle:focus [class*='uk-transition-fade'], -.uk-transition-active.uk-active [class*='uk-transition-fade'] { opacity: 1; } - -/* - * Scale - * Note: Using `scale3d` for better image rendering - */ - -[class*='uk-transition-scale'] { opacity: 0; } - -.uk-transition-scale-up { transform: scale3d(1,1,1); } - -.uk-transition-scale-down { transform: scale3d($transition-scale,$transition-scale,1); } - -/* Show */ -.uk-transition-toggle:hover .uk-transition-scale-up, -.uk-transition-toggle.uk-hover .uk-transition-scale-up, -.uk-transition-toggle:focus .uk-transition-scale-up, -.uk-transition-active.uk-active .uk-transition-scale-up { - opacity: 1; - transform: scale3d($transition-scale,$transition-scale,1); -} - -.uk-transition-toggle:hover .uk-transition-scale-down, -.uk-transition-toggle.uk-hover .uk-transition-scale-down, -.uk-transition-toggle:focus .uk-transition-scale-down, -.uk-transition-active.uk-active .uk-transition-scale-down { - opacity: 1; - transform: scale3d(1,1,1); -} - -/* - * Slide - */ - -[class*='uk-transition-slide'] { opacity: 0; } - -.uk-transition-slide-top { transform: translateY(-100%); } -.uk-transition-slide-bottom { transform: translateY(100%); } -.uk-transition-slide-left { transform: translateX(-100%); } -.uk-transition-slide-right { transform: translateX(100%); } - -.uk-transition-slide-top-small { transform: translateY(-$transition-slide-small-translate); } -.uk-transition-slide-bottom-small { transform: translateY($transition-slide-small-translate); } -.uk-transition-slide-left-small { transform: translateX(-$transition-slide-small-translate); } -.uk-transition-slide-right-small { transform: translateX($transition-slide-small-translate); } - -.uk-transition-slide-top-medium { transform: translateY(-$transition-slide-medium-translate); } -.uk-transition-slide-bottom-medium { transform: translateY($transition-slide-medium-translate); } -.uk-transition-slide-left-medium { transform: translateX(-$transition-slide-medium-translate); } -.uk-transition-slide-right-medium { transform: translateX($transition-slide-medium-translate); } - -/* Show */ -.uk-transition-toggle:hover [class*='uk-transition-slide'], -.uk-transition-toggle.uk-hover [class*='uk-transition-slide'], -.uk-transition-toggle:focus [class*='uk-transition-slide'], -.uk-transition-active.uk-active [class*='uk-transition-slide'] { - opacity: 1; - transform: translateX(0) translateY(0); -} - - -/* Opacity modifier -========================================================================== */ - -.uk-transition-opaque { opacity: 1; } - - -/* Duration modifiers -========================================================================== */ - -.uk-transition-slow { transition-duration: $transition-slow-duration; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-transition-misc)) {@include hook-transition-misc();} - -// @mixin hook-transition-misc(){} diff --git a/_sass/uikit/components/utility.scss b/_sass/uikit/components/utility.scss deleted file mode 100644 index 2ac1777683..0000000000 --- a/_sass/uikit/components/utility.scss +++ /dev/null @@ -1,570 +0,0 @@ -// Name: Utility -// Description: Utilities collection -// -// Component: `uk-panel-*` -// `uk-clearfix` -// `uk-float-*` -// `uk-overflow-*` -// `uk-resize-*` -// `uk-display-*` -// `uk-inline-*` -// `uk-height-*` -// `uk-responsive-*` -// `uk-preserve-width` -// `uk-border-*` -// `uk-box-shadow-*` -// `uk-box-shadow-bottom` -// `uk-dropcap` -// `uk-leader` -// `uk-logo` -// `uk-svg` -// `uk-blend-*` -// `uk-transform-*` -// `uk-transform-origin-*` -// -// States: `uk-disabled` -// `uk-drag` -// `uk-dragover` -// `uk-preserve` -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$panel-scrollable-height: 170px !default; -$panel-scrollable-padding: 10px !default; -$panel-scrollable-border-width: $global-border-width !default; -$panel-scrollable-border: $global-border !default; - -$height-small-height: 150px !default; -$height-medium-height: 300px !default; -$height-large-height: 450px !default; - -$border-rounded-border-radius: 5px !default; - -$box-shadow-duration: 0.1s !default; - -$box-shadow-bottom-height: 30px !default; -$box-shadow-bottom-border-radius: 100% !default; -$box-shadow-bottom-background: #444 !default; -$box-shadow-bottom-blur: 20px !default; - -$dropcap-margin-right: 10px !default; -$dropcap-font-size: (($global-line-height * 3) * 1em) !default; - -$leader-fill-content: '.' !default; -$leader-fill-margin-left: $global-small-gutter !default; - -$logo-font-size: $global-large-font-size !default; -$logo-font-family: $global-font-family !default; -$logo-color: $global-color !default; -$logo-hover-color: $global-color !default; - -$dragover-box-shadow: 0 0 20px rgba(100,100,100,0.3) !default; - - -/* ======================================================================== - Component: Utility - ========================================================================== */ - - -/* Panel - ========================================================================== */ - -.uk-panel { - position: relative; - box-sizing: border-box; -} - -/* - * Micro clearfix - */ - -.uk-panel::before, -.uk-panel::after { - content: ""; - display: table; -} - -.uk-panel::after { clear: both; } - -/* - * Remove margin from the last-child - */ - -.uk-panel > :last-child { margin-bottom: 0; } - - -/* - * Scrollable - */ - -.uk-panel-scrollable { - height: $panel-scrollable-height; - padding: $panel-scrollable-padding; - border: $panel-scrollable-border-width solid $panel-scrollable-border; - overflow: auto; - -webkit-overflow-scrolling: touch; - resize: both; - @if(mixin-exists(hook-panel-scrollable)) {@include hook-panel-scrollable();} -} - - -/* Clearfix - ========================================================================== */ - -/* - * 1. `table-cell` is used with `::before` because `table` creates a 1px gap when it becomes a flex item, only in Webkit - * 2. `table` is used again with `::after` because `clear` only works with block elements. - * Note: `display: block` with `overflow: hidden` is currently not working in the latest Safari - */ - -/* 1 */ -.uk-clearfix::before { - content: ""; - display: table-cell; -} - -/* 2 */ -.uk-clearfix::after { - content: ""; - display: table; - clear: both; -} - - -/* Float - ========================================================================== */ - -/* - * 1. Prevent content overflow - */ - -.uk-float-left { float: left; } -.uk-float-right { float: right; } - -/* 1 */ -[class*='uk-float-'] { max-width: 100%; } - - -/* Overfow - ========================================================================== */ - -.uk-overflow-hidden { overflow: hidden; } - -/* - * Enable scrollbars if content is clipped - * Note: Firefox ignores `padding-bottom` for the scrollable overflow https://bugzilla.mozilla.org/show_bug.cgi?id=748518 - */ - -.uk-overflow-auto { - overflow: auto; - -webkit-overflow-scrolling: touch; -} - -.uk-overflow-auto > :last-child { margin-bottom: 0; } - - -/* Resize - ========================================================================== */ - -.uk-resize { resize: both; } -.uk-resize-vertical { resize: vertical; } - - -/* Display - ========================================================================== */ - -.uk-display-block { display: block !important; } -.uk-display-inline { display: inline !important; } -.uk-display-inline-block { display: inline-block !important; } - - -/* Inline - ========================================================================== */ - -/* - * 1. Container fits its content - * 2. Create position context - * 3. Prevent content overflow - * 4. Behave like most inline-block elements - * 5. Force hardware acceleration without creating a new stacking context - * to fix 1px glitch when combined with overlays and transitions in Webkit - * 6. Clip child elements - */ - -[class*='uk-inline'] { - /* 1 */ - display: inline-block; - /* 2 */ - position: relative; - /* 3 */ - max-width: 100%; - /* 4 */ - vertical-align: middle; - /* 5 */ - -webkit-backface-visibility: hidden; -} - -.uk-inline-clip { - /* 6 */ - overflow: hidden; -} - - -/* Height - ========================================================================== */ - -[class*='uk-height'] { box-sizing: border-box; } - -/* - * Only works if parent element has a height set - */ - -.uk-height-1-1 { height: 100%; } - -/* - * Useful to create image teasers - */ - -.uk-height-viewport { min-height: 100vh; } - -/* - * Pixel - * Useful for `overflow: auto` - */ - -.uk-height-small { height: $height-small-height; } -.uk-height-medium { height: $height-medium-height; } -.uk-height-large { height: $height-large-height; } - -.uk-height-max-small { max-height: $height-small-height; } -.uk-height-max-medium { max-height: $height-medium-height; } -.uk-height-max-large { max-height: $height-large-height; } - - -/* Responsive objects - ========================================================================== */ - -/* - * Preserve original dimensions - * Because `img, `video`, `canvas` and `audio` are already responsive by default, see Base component - */ - -.uk-preserve-width, -.uk-preserve-width audio, -.uk-preserve-width canvas, -.uk-preserve-width img, -.uk-preserve-width svg, -.uk-preserve-width video { max-width: none; } - -/* - * Responsiveness - * Corrects `max-width` and `max-height` behavior if padding and border are used - */ - -.uk-responsive-width, -.uk-responsive-height { box-sizing: border-box; } - -/* - * 1. Set a maximum width. `important` needed to override `uk-preserve-width img` - * 2. Auto scale the height. Only needed if `height` attribute is present - */ - -.uk-responsive-width { - /* 1 */ - max-width: 100% !important; - /* 2 */ - height: auto; -} - -/* - * 1. Set a maximum height. Only works if the parent element has a fixed height - * 2. Auto scale the width. Only needed if `width` attribute is present - * 3. Reset max-width, which `img, `video`, `canvas` and `audio` already have by default - */ - -.uk-responsive-height { - /* 1 */ - max-height: 100%; - /* 2 */ - width: auto; - /* 3 */ - max-width: none; -} - - -/* Border - ========================================================================== */ - -.uk-border-circle { border-radius: 50%; } -.uk-border-rounded { border-radius: $border-rounded-border-radius; } - -/* - * Fix `overflow: hidden` to be ignored with border-radius and CSS transforms in Webkit - */ - -.uk-inline-clip[class*='uk-border-'] { -webkit-transform: translateZ(0); } - - -/* Box-shadow - ========================================================================== */ - -.uk-box-shadow-small { box-shadow: $global-small-box-shadow; } -.uk-box-shadow-medium { box-shadow: $global-medium-box-shadow; } -.uk-box-shadow-large { box-shadow: $global-large-box-shadow; } -.uk-box-shadow-xlarge { box-shadow: $global-xlarge-box-shadow; } - -/* - * Hover - */ - -[class*='uk-box-shadow-hover'] { transition: box-shadow $box-shadow-duration ease-in-out; } - -.uk-box-shadow-hover-small:hover { box-shadow: $global-small-box-shadow; } -.uk-box-shadow-hover-medium:hover { box-shadow: $global-medium-box-shadow; } -.uk-box-shadow-hover-large:hover { box-shadow: $global-large-box-shadow; } -.uk-box-shadow-hover-xlarge:hover { box-shadow: $global-xlarge-box-shadow; } - - -/* Box-shadow bottom - ========================================================================== */ - -/* - * 1. Set position. - * 2. Set style - * 3. Blur doesn't work on pseudo elements with negative `z-index` in Edge. - * Solved by using `before` and add position context to child elements. - */ - -@supports (filter: blur(0)) { - - .uk-box-shadow-bottom { - display: inline-block; - position: relative; - max-width: 100%; - vertical-align: middle; - } - - .uk-box-shadow-bottom::before { - content: ''; - /* 1 */ - position: absolute; - bottom: (-$box-shadow-bottom-height); - left: 0; - right: 0; - /* 2 */ - height: $box-shadow-bottom-height; - border-radius: $box-shadow-bottom-border-radius; - background: $box-shadow-bottom-background; - filter: blur($box-shadow-bottom-blur); - @if(mixin-exists(hook-box-shadow-bottom)) {@include hook-box-shadow-bottom();} - } - - /* 3 */ - .uk-box-shadow-bottom > * { position: relative; } - -} - - -/* Drop cap - ========================================================================== */ - -.uk-dropcap::first-letter, -.uk-dropcap > p:first-of-type::first-letter { - display: block; - margin-right: $dropcap-margin-right; - float: left; - font-size: $dropcap-font-size; - line-height: 1; - @if(mixin-exists(hook-dropcap)) {@include hook-dropcap();} -} - - -/* Leader - ========================================================================== */ - -.uk-leader { overflow: hidden; } - -/* - * 1. Place element in text flow - * 2. Never break into a new line - * 3. Get a string back with as many repeating characters to fill the container - * 4. Prevent wrapping. Overflowing characters will be clipped by the container - */ - -.uk-leader-fill::after { - /* 1 */ - display: inline-block; - margin-left: $leader-fill-margin-left; - /* 2 */ - width: 0; - /* 3 */ - content: attr(data-fill); - /* 4 */ - white-space: nowrap; - @if(mixin-exists(hook-leader)) {@include hook-leader();} -} - -/* - * Hide if media does not match - */ - -.uk-leader-fill.uk-leader-hide::after { display: none; } - -/* Pass fill character to JS */ -.var-leader-fill:before { content: $leader-fill-content; } - - -/* Logo - ========================================================================== */ - -/* - * 1. Required for `a` - */ - -.uk-logo { - font-size: $logo-font-size; - font-family: $logo-font-family; - color: $logo-color; - /* 1 */ - text-decoration: none; - @if(mixin-exists(hook-logo)) {@include hook-logo();} -} - -/* Hover + Focus */ -.uk-logo:hover, -.uk-logo:focus { - color: $logo-hover-color; - outline: none; - /* 1 */ - text-decoration: none; - @if(mixin-exists(hook-logo-hover)) {@include hook-logo-hover();} -} - -.uk-logo-inverse { display: none; } - - -/* SVG - ========================================================================== */ - -/* - * 1. Fill all SVG elements with the current text color if no `fill` attribute is set - * 2. Set the fill and stroke color of all SVG elements to the current text color - * 3. Fix for uppercase attribute names in Edge. Will be fixed in Windows 10 builds 16251+ - */ - -/* 1 */ -.uk-svg, -/* 2 */ -.uk-svg:not(.uk-preserve) [fill*='#']:not(.uk-preserve), -.uk-svg:not(.uk-preserve) [FILL*='#']:not(.uk-preserve) { fill: currentcolor; } // 3 -.uk-svg:not(.uk-preserve) [stroke*='#']:not(.uk-preserve), -.uk-svg:not(.uk-preserve) [STROKE*='#']:not(.uk-preserve) { stroke: currentcolor; } // 3 - -/* - * Fix Firefox blurry SVG rendering: https://bugzilla.mozilla.org/show_bug.cgi?id=1046835 - */ - -.uk-svg { transform: translate(0,0); } - - -/* Disabled State - ========================================================================== */ - -.uk-disabled { pointer-events: none; } - - -/* Drag State - ========================================================================== */ - -/* - * 1. Needed if moving over elements with have their own cursor on hover, e.g. links or buttons - * 2. Fix dragging over iframes - */ - -.uk-drag, -/* 1 */ -.uk-drag * { cursor: move; } - -/* 2 */ -.uk-drag iframe { pointer-events: none; } - - -/* Dragover State - ========================================================================== */ - -/* - * Create a box-shadow when dragging a file over the upload area - */ - -.uk-dragover { box-shadow: $dragover-box-shadow; } - - -/* Blend modes - ========================================================================== */ - -.uk-blend-multiply { mix-blend-mode: multiply; } -.uk-blend-screen { mix-blend-mode: screen; } -.uk-blend-overlay { mix-blend-mode: overlay; } -.uk-blend-darken { mix-blend-mode: darken; } -.uk-blend-lighten { mix-blend-mode: lighten; } -.uk-blend-color-dodge { mix-blend-mode: color-dodge; } -.uk-blend-color-burn { mix-blend-mode: color-burn; } -.uk-blend-hard-light { mix-blend-mode: hard-light; } -.uk-blend-soft-light { mix-blend-mode: soft-light; } -.uk-blend-difference { mix-blend-mode: difference; } -.uk-blend-exclusion { mix-blend-mode: exclusion; } -.uk-blend-hue { mix-blend-mode: hue; } -.uk-blend-saturation { mix-blend-mode: saturation; } -.uk-blend-color { mix-blend-mode: color; } -.uk-blend-luminosity { mix-blend-mode: luminosity; } - - -/* Transform -========================================================================== */ - -.uk-transform-center { transform: translate(-50%, -50%); } - - -/* Transform Origin -========================================================================== */ - -.uk-transform-origin-top-left { transform-origin: 0 0; } -.uk-transform-origin-top-center { transform-origin: 50% 0; } -.uk-transform-origin-top-right { transform-origin: 100% 0; } -.uk-transform-origin-center-left { transform-origin: 0 50%; } -.uk-transform-origin-center-right { transform-origin: 100% 50%; } -.uk-transform-origin-bottom-left { transform-origin: 0 100%; } -.uk-transform-origin-bottom-center { transform-origin: 50% 100%; } -.uk-transform-origin-bottom-right { transform-origin: 100% 100%; } - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-utility-misc)) {@include hook-utility-misc();} - -// @mixin hook-panel-scrollable(){} -// @mixin hook-box-shadow-bottom(){} -// @mixin hook-dropcap(){} -// @mixin hook-leader(){} -// @mixin hook-logo(){} -// @mixin hook-logo-hover(){} -// @mixin hook-utility-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-logo-color: $inverse-global-color !default; -$inverse-logo-hover-color: $inverse-global-color !default; - - - -// @mixin hook-inverse-dropcap(){} -// @mixin hook-inverse-leader(){} -// @mixin hook-inverse-logo(){} -// @mixin hook-inverse-logo-hover(){} diff --git a/_sass/uikit/components/variables.scss b/_sass/uikit/components/variables.scss deleted file mode 100644 index a08406a05d..0000000000 --- a/_sass/uikit/components/variables.scss +++ /dev/null @@ -1,117 +0,0 @@ -// -// Component: Variables -// Description: Defines common values which are used across all components -// -// ======================================================================== - - -// Breakpoints -// ======================================================================== - -// Phone Portrait: Galaxy (360x640), iPhone 6 (375x667), iPhone 6+ (414x736) -// Phone Landscape: Galaxy (640x360), iPhone 6 (667x375), iPhone 6+ (736x414) -// Tablet Portrait: iPad (768x1024), Galaxy Tab (800x1280), -// Tablet Landscape: iPad (1024x768), iPad Pro (1024x1366), -// Desktop: Galaxy Tab (1280x800), iPad Pro (1366x1024) - -$breakpoint-small: 640px !default; // Phone landscape -$breakpoint-medium: 960px !default; // Tablet Landscape -$breakpoint-large: 1200px !default; // Desktop -$breakpoint-xlarge: 1600px !default; // Large Screens - -$breakpoint-xsmall-max: ($breakpoint-small - 1) !default; -$breakpoint-small-max: ($breakpoint-medium - 1) !default; -$breakpoint-medium-max: ($breakpoint-large - 1) !default; -$breakpoint-large-max: ($breakpoint-xlarge - 1) !default; - - -// Global variables -// ======================================================================== - -// -// Typography -// - -$global-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !default; -$global-font-size: 16px !default; -$global-line-height: 1.5 !default; // 24px - -$global-xxlarge-font-size: 2.625rem !default; // 42px -$global-xlarge-font-size: 2rem !default; // 32px -$global-large-font-size: 1.5rem !default; // 24px -$global-medium-font-size: 1.25rem !default; // 20px -$global-small-font-size: 0.875rem !default; // 14px - -// -// Colors -// - -$global-color: #666 !default; -$global-emphasis-color: #333 !default; -$global-muted-color: #999 !default; - -$global-link-color: #1e87f0 !default; -$global-link-hover-color: #0f6ecd !default; - -$global-inverse-color: #fff !default; - -// -// Backgrounds -// - -$global-background: #fff !default; - -$global-muted-background: #f8f8f8 !default; -$global-primary-background: #1e87f0 !default; -$global-secondary-background: #222 !default; - -$global-success-background: #32d296 !default; -$global-warning-background: #faa05a !default; -$global-danger-background: #f0506e !default; - -// -// Borders -// - -$global-border-width: 1px !default; -$global-border: #e5e5e5 !default; - -// -// Box-Shadows -// - -$global-small-box-shadow: 0 2px 8px rgba(0,0,0,0.08) !default; -$global-medium-box-shadow: 0 5px 15px rgba(0,0,0,0.08) !default; -$global-large-box-shadow: 0 14px 25px rgba(0,0,0,0.16) !default; -$global-xlarge-box-shadow: 0 28px 50px rgba(0,0,0,0.16) !default; - -// -// Spacings -// - -// Used in margin, section, list -$global-margin: 20px !default; -$global-small-margin: 10px !default; -$global-medium-margin: 40px !default; -$global-large-margin: 70px !default; -$global-xlarge-margin: 140px !default; - -// Used in grid, column, container, align, card, padding -$global-gutter: 30px !default; -$global-small-gutter: 15px !default; -$global-medium-gutter: 40px !default; -$global-large-gutter: 70px !default; - -// -// Controls -// - -$global-control-height: 40px !default; -$global-control-small-height: 30px !default; -$global-control-large-height: 55px !default; - -// -// Z-index -// - -$global-z-index: 1000 !default; \ No newline at end of file diff --git a/_sass/uikit/components/visibility.scss b/_sass/uikit/components/visibility.scss deleted file mode 100644 index ee800a1537..0000000000 --- a/_sass/uikit/components/visibility.scss +++ /dev/null @@ -1,151 +0,0 @@ -// Name: Visibility -// Description: Utilities to show or hide content on breakpoints, hover or touch -// -// Component: `uk-hidden-*` -// `uk-visible-*` -// `uk-invisible` -// `uk-visible-toggle` -// `uk-hidden-hover` -// `uk-invisible-hover` -// `uk-hidden-touch` -// `uk-hidden-notouch` -// -// ======================================================================== - - -/* ======================================================================== - Component: Visibility - ========================================================================== */ - -/* - * Hidden - * `hidden` attribute also set here to make it stronger - */ - -[hidden], -.uk-hidden { display: none !important; } - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - .uk-hidden\@s { display: none !important; } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-hidden\@m { display: none !important; } - -} - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-hidden\@l { display: none !important; } - -} - -/* Large screen and bigger */ -@media (min-width: $breakpoint-xlarge) { - - .uk-hidden\@xl { display: none !important; } - -} - -/* - * Visible - */ - -/* Phone portrait and smaller */ -@media (max-width: $breakpoint-xsmall-max) { - - .uk-visible\@s { display: none !important; } - -} - -/* Phone landscape and smaller */ -@media (max-width: $breakpoint-small-max) { - - .uk-visible\@m { display: none !important; } - -} - -/* Tablet landscape and smaller */ -@media (max-width: $breakpoint-medium-max) { - - .uk-visible\@l { display: none !important; } - -} - -/* Desktop and smaller */ -@media (max-width: $breakpoint-large-max) { - - .uk-visible\@xl { display: none !important; } - -} - - -/* Visibility - ========================================================================== */ - -.uk-invisible { visibility: hidden !important; } - - -/* Hover - ========================================================================== */ - -/* - * Hidden - * Can't use `display: hidden` because it's not focusable. This is accessible through keyboard. - */ - -.uk-visible-toggle:not(:hover):not(.uk-hover) .uk-hidden-hover:not(:focus) { - position: absolute !important; - width: 0 !important; - height: 0 !important; - padding: 0 !important; - margin: 0 !important; - overflow: hidden !important; -} - -/* - * Invisible - * Can't use `visibility: hidden` because it's not focusable. This is accessible through keyboard. - */ - -.uk-visible-toggle:not(:hover):not(.uk-hover) .uk-invisible-hover:not(:focus) { opacity: 0 !important; } - - -/* Touch - ========================================================================== */ - -/* - * Hide if primary pointing device has limited accuracy, e.g. a touch screen. - * Works on mobile browsers: Safari, Chrome and Android browser - */ - -@media (pointer: coarse) { - .uk-hidden-touch { display: none !important; } -} - -/* - * Hide if primary pointing device is accurate, e.g. mouse. - * 1. Fallback for IE11 and Firefox, because `pointer` is not supported - * 2. Reset if supported - */ - -/* 1 */ -.uk-hidden-notouch { display: none !important; } - -@media (pointer: coarse) { - .uk-hidden-notouch { display: block !important; } -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-visibility-misc)) {@include hook-visibility-misc();} - -// @mixin hook-visibility-misc(){} diff --git a/_sass/uikit/components/width.scss b/_sass/uikit/components/width.scss deleted file mode 100644 index 7f4a2cb533..0000000000 --- a/_sass/uikit/components/width.scss +++ /dev/null @@ -1,398 +0,0 @@ -// Name: Width -// Description: Utilities for widths -// -// Component: `uk-child-width-*` -// `uk-width-*` -// -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$width-small-width: 150px !default; -$width-medium-width: 300px !default; -$width-large-width: 450px !default; -$width-xlarge-width: 600px !default; -$width-xxlarge-width: 750px !default; - - -/* ======================================================================== - Component: Width - ========================================================================== */ - - -/* Equal child widths - ========================================================================== */ - -[class*='uk-child-width'] > * { - box-sizing: border-box; - width: 100%; -} - -.uk-child-width-1-2 > * { width: 50%; } -.uk-child-width-1-3 > * { width: unquote('calc(100% * 1 / 3.001)'); } -.uk-child-width-1-4 > * { width: 25%; } -.uk-child-width-1-5 > * { width: 20%; } -.uk-child-width-1-6 > * { width: unquote('calc(100% * 1 / 6.001)'); } - -.uk-child-width-auto > * { width: auto; } - -/* - * Instead of 0, 1px is needed to make cell wrap into next row if predecessor is 100% wide - * and the grid gutter is 0 pixels wide - */ - -.uk-child-width-expand > * { width: 1px; } - -/* - * 1. Make `width: 1px` work, because according to the spec flex items won’t shrink - * below their minimum content size. To change this, set the min-width. - * Only needed for Firefox. All other browsers ignore this. - * - * 2. `width` is ignored when wrapping flex items in Safari - * https://github.com/philipwalton/flexbugs#11-min-and-max-size-declarations-are-ignored-when-wrapping-flex-items - */ - -.uk-child-width-expand > :not([class*='uk-width']) { - flex: 1; - /* 1 */ - min-width: 0; - /* 2 */ - flex-basis: 1px; -} - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - .uk-child-width-1-1\@s > * { width: 100%; } - .uk-child-width-1-2\@s > * { width: 50%; } - .uk-child-width-1-3\@s > * { width: unquote('calc(100% * 1 / 3.001)'); } - .uk-child-width-1-4\@s > * { width: 25%; } - .uk-child-width-1-5\@s > * { width: 20%; } - .uk-child-width-1-6\@s > * { width: unquote('calc(100% * 1 / 6.001)'); } - - .uk-child-width-auto\@s > * { width: auto; } - .uk-child-width-expand\@s > * { width: 1px; } - - .uk-child-width-expand\@s > :not([class*='uk-width']) { - flex: 1; - min-width: 0; - flex-basis: 1px; - } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - .uk-child-width-1-1\@m > * { width: 100%; } - .uk-child-width-1-2\@m > * { width: 50%; } - .uk-child-width-1-3\@m > * { width: unquote('calc(100% * 1 / 3.001)'); } - .uk-child-width-1-4\@m > * { width: 25%; } - .uk-child-width-1-5\@m > * { width: 20%; } - .uk-child-width-1-6\@m > * { width: unquote('calc(100% * 1 / 6.001)'); } - - .uk-child-width-auto\@m > * { width: auto; } - .uk-child-width-expand\@m > * { width: 1px; } - - .uk-child-width-expand\@m > :not([class*='uk-width']) { - flex: 1; - min-width: 0; - flex-basis: 1px; - } - -} - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - .uk-child-width-1-1\@l > * { width: 100%; } - .uk-child-width-1-2\@l > * { width: 50%; } - .uk-child-width-1-3\@l > * { width: unquote('calc(100% * 1 / 3.001)'); } - .uk-child-width-1-4\@l > * { width: 25%; } - .uk-child-width-1-5\@l > * { width: 20%; } - .uk-child-width-1-6\@l > * { width: unquote('calc(100% * 1 / 6.001)'); } - - .uk-child-width-auto\@l > * { width: auto; } - .uk-child-width-expand\@l > * { width: 1px; } - - .uk-child-width-expand\@l > :not([class*='uk-width']) { - flex: 1; - min-width: 0; - flex-basis: 1px; - } - -} - -/* Large screen and bigger */ -@media (min-width: $breakpoint-xlarge) { - - .uk-child-width-1-1\@xl > * { width: 100%; } - .uk-child-width-1-2\@xl > * { width: 50%; } - .uk-child-width-1-3\@xl > * { width: unquote('calc(100% * 1 / 3.001)'); } - .uk-child-width-1-4\@xl > * { width: 25%; } - .uk-child-width-1-5\@xl > * { width: 20%; } - .uk-child-width-1-6\@xl > * { width: unquote('calc(100% * 1 / 6.001)'); } - - .uk-child-width-auto\@xl > * { width: auto; } - .uk-child-width-expand\@xl > * { width: 1px; } - - .uk-child-width-expand\@xl > :not([class*='uk-width']) { - flex: 1; - min-width: 0; - flex-basis: 1px; - } - -} - - -/* Single Widths - ========================================================================== */ - -/* - * 1. `max-width` is needed for the pixel-based classes - */ - -[class*='uk-width'] { - box-sizing: border-box; - width: 100%; - /* 1 */ - max-width: 100%; -} - -/* Halves */ -.uk-width-1-2 { width: 50%; } - -/* Thirds */ -.uk-width-1-3 { width: unquote('calc(100% * 1 / 3.001)'); } -.uk-width-2-3 { width: unquote('calc(100% * 2 / 3.001)'); } - -/* Quarters */ -.uk-width-1-4 { width: 25%; } -.uk-width-3-4 { width: 75%; } - -/* Fifths */ -.uk-width-1-5 { width: 20%; } -.uk-width-2-5 { width: 40%; } -.uk-width-3-5 { width: 60%; } -.uk-width-4-5 { width: 80%; } - -/* Sixths */ -.uk-width-1-6 { width: unquote('calc(100% * 1 / 6.001)'); } -.uk-width-5-6 { width: unquote('calc(100% * 5 / 6.001)'); } - -/* Pixel */ -.uk-width-small { width: $width-small-width; } -.uk-width-medium { width: $width-medium-width; } -.uk-width-large { width: $width-large-width; } -.uk-width-xlarge { width: $width-xlarge-width; } -.uk-width-xxlarge { width: $width-xxlarge-width; } - -/* Auto */ -.uk-width-auto { width: auto; } - -/* Expand */ -.uk-width-expand { - width: 1px; - flex: 1; - min-width: 0; - flex-basis: 1px; -} - -/* Phone landscape and bigger */ -@media (min-width: $breakpoint-small) { - - /* Whole */ - .uk-width-1-1\@s { width: 100%; } - - /* Halves */ - .uk-width-1-2\@s { width: 50%; } - - /* Thirds */ - .uk-width-1-3\@s { width: unquote('calc(100% * 1 / 3.001)'); } - .uk-width-2-3\@s { width: unquote('calc(100% * 2 / 3.001)'); } - - /* Quarters */ - .uk-width-1-4\@s { width: 25%; } - .uk-width-3-4\@s { width: 75%; } - - /* Fifths */ - .uk-width-1-5\@s { width: 20%; } - .uk-width-2-5\@s { width: 40%; } - .uk-width-3-5\@s { width: 60%; } - .uk-width-4-5\@s { width: 80%; } - - /* Sixths */ - .uk-width-1-6\@s { width: unquote('calc(100% * 1 / 6.001)'); } - .uk-width-5-6\@s { width: unquote('calc(100% * 5 / 6.001)'); } - - /* Pixel */ - .uk-width-small\@s { width: $width-small-width; } - .uk-width-medium\@s { width: $width-medium-width; } - .uk-width-large\@s { width: $width-large-width; } - .uk-width-xlarge\@s { width: $width-xlarge-width; } - .uk-width-xxlarge\@s { width: $width-xxlarge-width; } - - /* Auto */ - .uk-width-auto\@s { width: auto; } - - /* Expand */ - .uk-width-expand\@s { - width: 1px; - flex: 1; - min-width: 0; - flex-basis: 1px; - } - -} - -/* Tablet landscape and bigger */ -@media (min-width: $breakpoint-medium) { - - /* Whole */ - .uk-width-1-1\@m { width: 100%; } - - /* Halves */ - .uk-width-1-2\@m { width: 50%; } - - /* Thirds */ - .uk-width-1-3\@m { width: unquote('calc(100% * 1 / 3.001)'); } - .uk-width-2-3\@m { width: unquote('calc(100% * 2 / 3.001)'); } - - /* Quarters */ - .uk-width-1-4\@m { width: 25%; } - .uk-width-3-4\@m { width: 75%; } - - /* Fifths */ - .uk-width-1-5\@m { width: 20%; } - .uk-width-2-5\@m { width: 40%; } - .uk-width-3-5\@m { width: 60%; } - .uk-width-4-5\@m { width: 80%; } - - /* Sixths */ - .uk-width-1-6\@m { width: unquote('calc(100% * 1 / 6.001)'); } - .uk-width-5-6\@m { width: unquote('calc(100% * 5 / 6.001)'); } - - /* Pixel */ - .uk-width-small\@m { width: $width-small-width; } - .uk-width-medium\@m { width: $width-medium-width; } - .uk-width-large\@m { width: $width-large-width; } - .uk-width-xlarge\@m { width: $width-xlarge-width; } - .uk-width-xxlarge\@m { width: $width-xxlarge-width; } - - /* Auto */ - .uk-width-auto\@m { width: auto; } - - /* Expand */ - .uk-width-expand\@m { - width: 1px; - flex: 1; - min-width: 0; - flex-basis: 1px; - } - -} - -/* Desktop and bigger */ -@media (min-width: $breakpoint-large) { - - /* Whole */ - .uk-width-1-1\@l { width: 100%; } - - /* Halves */ - .uk-width-1-2\@l { width: 50%; } - - /* Thirds */ - .uk-width-1-3\@l { width: unquote('calc(100% * 1 / 3.001)'); } - .uk-width-2-3\@l { width: unquote('calc(100% * 2 / 3.001)'); } - - /* Quarters */ - .uk-width-1-4\@l { width: 25%; } - .uk-width-3-4\@l { width: 75%; } - - /* Fifths */ - .uk-width-1-5\@l { width: 20%; } - .uk-width-2-5\@l { width: 40%; } - .uk-width-3-5\@l { width: 60%; } - .uk-width-4-5\@l { width: 80%; } - - /* Sixths */ - .uk-width-1-6\@l { width: unquote('calc(100% * 1 / 6.001)'); } - .uk-width-5-6\@l { width: unquote('calc(100% * 5 / 6.001)'); } - - /* Pixel */ - .uk-width-small\@l { width: $width-small-width; } - .uk-width-medium\@l { width: $width-medium-width; } - .uk-width-large\@l { width: $width-large-width; } - .uk-width-xlarge\@l { width: $width-xlarge-width; } - .uk-width-xxlarge\@l { width: $width-xxlarge-width; } - - /* Auto */ - .uk-width-auto\@l { width: auto; } - - /* Expand */ - .uk-width-expand\@l { - width: 1px; - flex: 1; - min-width: 0; - flex-basis: 1px; - } - -} - -/* Large screen and bigger */ -@media (min-width: $breakpoint-xlarge) { - - /* Whole */ - .uk-width-1-1\@xl { width: 100%; } - - /* Halves */ - .uk-width-1-2\@xl { width: 50%; } - - /* Thirds */ - .uk-width-1-3\@xl { width: unquote('calc(100% * 1 / 3.001)'); } - .uk-width-2-3\@xl { width: unquote('calc(100% * 2 / 3.001)'); } - - /* Quarters */ - .uk-width-1-4\@xl { width: 25%; } - .uk-width-3-4\@xl { width: 75%; } - - /* Fifths */ - .uk-width-1-5\@xl { width: 20%; } - .uk-width-2-5\@xl { width: 40%; } - .uk-width-3-5\@xl { width: 60%; } - .uk-width-4-5\@xl { width: 80%; } - - /* Sixths */ - .uk-width-1-6\@xl { width: unquote('calc(100% * 1 / 6.001)'); } - .uk-width-5-6\@xl { width: unquote('calc(100% * 5 / 6.001)'); } - - /* Pixel */ - .uk-width-small\@xl { width: $width-small-width; } - .uk-width-medium\@xl { width: $width-medium-width; } - .uk-width-large\@xl { width: $width-large-width; } - .uk-width-xlarge\@xl { width: $width-xlarge-width; } - .uk-width-xxlarge\@xl { width: $width-xxlarge-width; } - - /* Auto */ - .uk-width-auto\@xl { width: auto; } - - /* Expand */ - .uk-width-expand\@xl { - width: 1px; - flex: 1; - min-width: 0; - flex-basis: 1px; - } - -} - - -// Hooks -// ======================================================================== - -@if(mixin-exists(hook-width-misc)) {@include hook-width-misc();} - -// @mixin hook-width-misc(){} diff --git a/_sass/uikit/mixins-theme.scss b/_sass/uikit/mixins-theme.scss deleted file mode 100644 index 993cf20969..0000000000 --- a/_sass/uikit/mixins-theme.scss +++ /dev/null @@ -1,2033 +0,0 @@ -@mixin hook-accordion(){} -@mixin hook-accordion-item(){} -@mixin hook-accordion-title(){ - - overflow: hidden; - - &::after { - content: ""; - width: ($accordion-title-line-height * 1em); - height: ($accordion-title-line-height * 1em); - float: right; - @include svg-fill($internal-accordion-close-image, "#000", $accordion-icon-color); - background-repeat: no-repeat; - background-position: 50% 50%; - } - - .uk-open > &::after { @include svg-fill($internal-accordion-open-image, "#000", $accordion-icon-color); } - -} -@mixin hook-accordion-title-hover(){} -@mixin hook-accordion-content(){} -@mixin hook-accordion-misc(){} -@mixin hook-inverse-accordion-item(){} -@mixin hook-inverse-accordion-title(){} -@mixin hook-inverse-accordion-title-hover(){} -@mixin hook-inverse-component-accordion(){ - - .uk-accordion-title::after { @include svg-fill($internal-accordion-close-image, "#000", $inverse-global-color); } - - .uk-open > .uk-accordion-title::after { @include svg-fill($internal-accordion-open-image, "#000", $inverse-global-color); } - -} -@mixin hook-align-misc(){} -@mixin hook-alert(){} -@mixin hook-alert-close(){ - color: inherit; - opacity: $alert-close-opacity; -} -@mixin hook-alert-close-hover(){ - color: inherit; - opacity: $alert-close-hover-opacity; -} -@mixin hook-alert-primary(){} -@mixin hook-alert-success(){} -@mixin hook-alert-warning(){} -@mixin hook-alert-danger(){} -@mixin hook-alert-misc(){ - - /* - * Content - */ - - .uk-alert h1, - .uk-alert h2, - .uk-alert h3, - .uk-alert h4, - .uk-alert h5, - .uk-alert h6 { color: inherit; } - - .uk-alert a:not([class]) { - color: inherit; - text-decoration: underline; - } - - .uk-alert a:not([class]):hover { - color: inherit; - text-decoration: underline; - } - -} -@mixin hook-article(){} -@mixin hook-article-adjacent(){} -@mixin hook-article-title(){} -@mixin hook-article-meta(){ - - a { color: $article-meta-link-color; } - - a:hover { - color: $article-meta-link-hover-color; - text-decoration: none; - } - -} -@mixin hook-article-misc(){} -@mixin hook-inverse-article-title(){} -@mixin hook-inverse-article-meta(){} -@mixin hook-inverse-component-article(){ - - .uk-article-title { - @if(mixin-exists(hook-inverse-article-title)) {@include hook-inverse-article-title();} - } - - .uk-article-meta { - color: $inverse-article-meta-color; - @if(mixin-exists(hook-inverse-article-meta)) {@include hook-inverse-article-meta();} - } - -} -@mixin hook-animation-misc(){} -@mixin hook-background-misc(){} -@mixin hook-badge(){} -@mixin hook-badge-hover(){} -@mixin hook-badge-misc(){} -@mixin hook-inverse-badge(){} -@mixin hook-inverse-badge-hover(){} -@mixin hook-inverse-component-badge(){ - - .uk-badge { - background-color: $inverse-badge-background; - color: $inverse-badge-color; - @if(mixin-exists(hook-inverse-badge)) {@include hook-inverse-badge();} - } - - .uk-badge:hover, - .uk-badge:focus { - color: $inverse-badge-hover-color; - @if(mixin-exists(hook-inverse-badge-hover)) {@include hook-inverse-badge-hover();} - } - -} -@mixin hook-base-body(){} -@mixin hook-base-link(){} -@mixin hook-base-link-hover(){} -@mixin hook-base-code(){ - padding: $base-code-padding-vertical $base-code-padding-horizontal; - background: $base-code-background; -} -@mixin hook-base-heading(){} -@mixin hook-base-h1(){} -@mixin hook-base-h2(){} -@mixin hook-base-h3(){} -@mixin hook-base-h4(){} -@mixin hook-base-h5(){} -@mixin hook-base-h6(){} -@mixin hook-base-hr(){} -@mixin hook-base-blockquote(){ - color: $base-blockquote-color; -} -@mixin hook-base-blockquote-footer(){ - - color: $base-blockquote-footer-color; - - &::before { content: "— "; } - -} -@mixin hook-base-pre(){ - padding: $base-pre-padding; - border: $base-pre-border-width solid $base-pre-border; - border-radius: $base-pre-border-radius; - background: $base-pre-background; -} -@mixin hook-base-misc(){} -@mixin hook-inverse-base-link(){} -@mixin hook-inverse-base-link-hover(){} -@mixin hook-inverse-base-code(){ - background: $inverse-global-muted-background; -} -@mixin hook-inverse-base-heading(){} -@mixin hook-inverse-base-h1(){} -@mixin hook-inverse-base-h2(){} -@mixin hook-inverse-base-h3(){} -@mixin hook-inverse-base-h4(){} -@mixin hook-inverse-base-h5(){} -@mixin hook-inverse-base-h6(){} -@mixin hook-inverse-base-blockquote(){ color: $inverse-base-blockquote-color; } -@mixin hook-inverse-base-blockquote-footer(){ color: $inverse-base-blockquote-footer-color; } -@mixin hook-inverse-base-hr(){} -@mixin hook-inverse-component-base(){ - - color: $inverse-base-color; - - // Base - // ======================================================================== - - // - // Link - // - - a, - .uk-link { - color: $inverse-base-link-color; - @if(mixin-exists(hook-inverse-base-link)) {@include hook-inverse-base-link();} - } - - a:hover, - .uk-link:hover { - color: $inverse-base-link-hover-color; - @if(mixin-exists(hook-inverse-base-link-hover)) {@include hook-inverse-base-link-hover();} - } - - // - // Code - // - - :not(pre) > code, - :not(pre) > kbd, - :not(pre) > samp { - color: $inverse-base-code-color; - @if(mixin-exists(hook-inverse-base-code)) {@include hook-inverse-base-code();} - } - - // - // Emphasize - // - - em { color: $inverse-base-em-color; } - - // - // Headings - // - - h1, .uk-h1, - h2, .uk-h2, - h3, .uk-h3, - h4, .uk-h4, - h5, .uk-h5, - h6, .uk-h6 { - color: $inverse-base-heading-color; - @if(mixin-exists(hook-inverse-base-heading)) {@include hook-inverse-base-heading();} - } - - h1, .uk-h1 { - @if(mixin-exists(hook-inverse-base-h1)) {@include hook-inverse-base-h1();} - } - - h2, .uk-h2 { - @if(mixin-exists(hook-inverse-base-h2)) {@include hook-inverse-base-h2();} - } - - h3, .uk-h3 { - @if(mixin-exists(hook-inverse-base-h3)) {@include hook-inverse-base-h3();} - } - - h4, .uk-h4 { - @if(mixin-exists(hook-inverse-base-h4)) {@include hook-inverse-base-h4();} - } - - h5, .uk-h5 { - @if(mixin-exists(hook-inverse-base-h5)) {@include hook-inverse-base-h5();} - } - - h6, .uk-h6 { - @if(mixin-exists(hook-inverse-base-h6)) {@include hook-inverse-base-h6();} - } - - // - // Blockquotes - // - - blockquote { - @if(mixin-exists(hook-inverse-base-blockquote)) {@include hook-inverse-base-blockquote();} - } - - blockquote footer { - @if(mixin-exists(hook-inverse-base-blockquote-footer)) {@include hook-inverse-base-blockquote-footer();} - } - - // - // Horizontal rules - // - - hr, .uk-hr { - border-top-color: $inverse-base-hr-border; - @if(mixin-exists(hook-inverse-base-hr)) {@include hook-inverse-base-hr();} - } - -} -@mixin hook-breadcrumb(){} -@mixin hook-breadcrumb-item(){} -@mixin hook-breadcrumb-item-hover(){} -@mixin hook-breadcrumb-item-disabled(){} -@mixin hook-breadcrumb-item-active(){} -@mixin hook-breadcrumb-divider(){} -@mixin hook-breadcrumb-misc(){} -@mixin hook-inverse-breadcrumb-item(){} -@mixin hook-inverse-breadcrumb-item-hover(){} -@mixin hook-inverse-breadcrumb-item-disabled(){} -@mixin hook-inverse-breadcrumb-item-active(){} -@mixin hook-inverse-breadcrumb-divider(){} -@mixin hook-inverse-component-breadcrumb(){ - - .uk-breadcrumb > * > * { - color: $inverse-breadcrumb-item-color; - @if(mixin-exists(hook-inverse-breadcrumb-item)) {@include hook-inverse-breadcrumb-item();} - } - - .uk-breadcrumb > * > :hover, - .uk-breadcrumb > * > :focus { - color: $inverse-breadcrumb-item-hover-color; - @if(mixin-exists(hook-inverse-breadcrumb-item-hover)) {@include hook-inverse-breadcrumb-item-hover();} - } - - - .uk-breadcrumb > .uk-disabled > * { - @if(mixin-exists(hook-inverse-breadcrumb-item-disabled)) {@include hook-inverse-breadcrumb-item-disabled();} - } - - .uk-breadcrumb > :last-child > * { - color: $inverse-breadcrumb-item-active-color; - @if(mixin-exists(hook-inverse-breadcrumb-item-active)) {@include hook-inverse-breadcrumb-item-active();} - } - - // - // Divider - // - - .uk-breadcrumb > :nth-child(n+2):not(.uk-first-column)::before { - color: $inverse-breadcrumb-divider-color; - @if(mixin-exists(hook-inverse-breadcrumb-divider)) {@include hook-inverse-breadcrumb-divider();} - } - -} -@mixin hook-button(){ - text-transform: $button-text-transform; - transition: 0.1s ease-in-out; - transition-property: color, background-color, border-color; -} -@mixin hook-button-hover(){} -@mixin hook-button-focus(){} -@mixin hook-button-active(){} -@mixin hook-button-default(){ border: $button-border-width solid $button-default-border; } -@mixin hook-button-default-hover(){ border-color: $button-default-hover-border; } -@mixin hook-button-default-active(){ border-color: $button-default-active-border; } -@mixin hook-button-primary(){ border: $button-border-width solid transparent; } -@mixin hook-button-primary-hover(){} -@mixin hook-button-primary-active(){} -@mixin hook-button-secondary(){ border: $button-border-width solid transparent; } -@mixin hook-button-secondary-hover(){} -@mixin hook-button-secondary-active(){} -@mixin hook-button-danger(){ border: $button-border-width solid transparent; } -@mixin hook-button-danger-hover(){} -@mixin hook-button-danger-active(){} -@mixin hook-button-disabled(){ border-color: $button-disabled-border; } -@mixin hook-button-small(){} -@mixin hook-button-large(){} -@mixin hook-button-text(){ - - position: relative; - - &::before { - content: ""; - position: absolute; - bottom: 0; - left: 0; - right: 100%; - border-bottom: $button-text-border-width solid $button-text-border; - transition: right 0.3s ease-out; - } - -} -@mixin hook-button-text-hover(){ - - &::before { right: 0; } - -} -@mixin hook-button-text-disabled(){ - - &::before { display: none; } - -} -@mixin hook-button-link(){} -@mixin hook-button-misc(){ - - /* Group - ========================================================================== */ - - /* - * Collapse border - */ - - .uk-button-group > .uk-button:nth-child(n+2), - .uk-button-group > div:nth-child(n+2) .uk-button { margin-left: (-$button-border-width); } - - /* - * Create position context to superimpose the successor elements border - * Known issue: If you use an `a` element as button and an icon inside, - * the active state will not work if you click the icon inside the button - * Workaround: Just use a `button` or `input` element as button - */ - - .uk-button-group .uk-button:hover, - .uk-button-group .uk-button:focus, - .uk-button-group .uk-button:active, - .uk-button-group .uk-button.uk-active { - position: relative; - z-index: 1; - } - -} -@mixin hook-inverse-button-default(){ border-color: $inverse-global-color; } -@mixin hook-inverse-button-default-hover(){ border-color: $inverse-global-emphasis-color; } -@mixin hook-inverse-button-default-active(){ border-color: $inverse-global-emphasis-color; } -@mixin hook-inverse-button-primary(){} -@mixin hook-inverse-button-primary-hover(){} -@mixin hook-inverse-button-primary-active(){} -@mixin hook-inverse-button-secondary(){} -@mixin hook-inverse-button-secondary-hover(){} -@mixin hook-inverse-button-secondary-active(){} -@mixin hook-inverse-button-text(){ - &::before { border-bottom-color: $inverse-global-emphasis-color; } -} -@mixin hook-inverse-button-text-hover(){} -@mixin hook-inverse-button-text-disabled(){} -@mixin hook-inverse-button-link(){} -@mixin hook-inverse-component-button(){ - - // - // Default - // - - .uk-button-default { - background-color: $inverse-button-default-background; - color: $inverse-button-default-color; - @if(mixin-exists(hook-inverse-button-default)) {@include hook-inverse-button-default();} - } - - .uk-button-default:hover, - .uk-button-default:focus { - background-color: $inverse-button-default-hover-background; - color: $inverse-button-default-hover-color; - @if(mixin-exists(hook-inverse-button-default-hover)) {@include hook-inverse-button-default-hover();} - } - - .uk-button-default:active, - .uk-button-default.uk-active { - background-color: $inverse-button-default-active-background; - color: $inverse-button-default-active-color; - @if(mixin-exists(hook-inverse-button-default-active)) {@include hook-inverse-button-default-active();} - } - - // - // Primary - // - - .uk-button-primary { - background-color: $inverse-button-primary-background; - color: $inverse-button-primary-color; - @if(mixin-exists(hook-inverse-button-primary)) {@include hook-inverse-button-primary();} - } - - .uk-button-primary:hover, - .uk-button-primary:focus { - background-color: $inverse-button-primary-hover-background; - color: $inverse-button-primary-hover-color; - @if(mixin-exists(hook-inverse-button-primary-hover)) {@include hook-inverse-button-primary-hover();} - } - - .uk-button-primary:active, - .uk-button-primary.uk-active { - background-color: $inverse-button-primary-active-background; - color: $inverse-button-primary-active-color; - @if(mixin-exists(hook-inverse-button-primary-active)) {@include hook-inverse-button-primary-active();} - } - - // - // Secondary - // - - .uk-button-secondary { - background-color: $inverse-button-secondary-background; - color: $inverse-button-secondary-color; - @if(mixin-exists(hook-inverse-button-secondary)) {@include hook-inverse-button-secondary();} - } - - .uk-button-secondary:hover, - .uk-button-secondary:focus { - background-color: $inverse-button-secondary-hover-background; - color: $inverse-button-secondary-hover-color; - @if(mixin-exists(hook-inverse-button-secondary-hover)) {@include hook-inverse-button-secondary-hover();} - } - - .uk-button-secondary:active, - .uk-button-secondary.uk-active { - background-color: $inverse-button-secondary-active-background; - color: $inverse-button-secondary-active-color; - @if(mixin-exists(hook-inverse-button-secondary-active)) {@include hook-inverse-button-secondary-active();} - } - - // - // Text - // - - .uk-button-text { - color: $inverse-button-text-color; - @if(mixin-exists(hook-inverse-button-text)) {@include hook-inverse-button-text();} - } - - .uk-button-text:hover, - .uk-button-text:focus { - color: $inverse-button-text-hover-color; - @if(mixin-exists(hook-inverse-button-text-hover)) {@include hook-inverse-button-text-hover();} - } - - .uk-button-text:disabled { - color: $inverse-button-text-disabled-color; - @if(mixin-exists(hook-inverse-button-text-disabled)) {@include hook-inverse-button-text-disabled();} - } - - // - // Link - // - - .uk-button-link { - color: $inverse-button-link-color; - @if(mixin-exists(hook-inverse-button-link)) {@include hook-inverse-button-link();} - } - - .uk-button-link:hover, - .uk-button-link:focus { color: $inverse-button-link-hover-color; } - - -} -@mixin hook-card(){ transition: box-shadow 0.1s ease-in-out; } -@mixin hook-card-body(){} -@mixin hook-card-header(){} -@mixin hook-card-footer(){} -@mixin hook-card-media(){} -@mixin hook-card-media-top(){} -@mixin hook-card-media-bottom(){} -@mixin hook-card-media-left(){} -@mixin hook-card-media-right(){} -@mixin hook-card-title(){} -@mixin hook-card-badge(){} -@mixin hook-card-hover(){ box-shadow: $card-hover-box-shadow; } -@mixin hook-card-default(){ box-shadow: $card-default-box-shadow; } -@mixin hook-card-default-title(){} -@mixin hook-card-default-hover(){ box-shadow: $card-default-hover-box-shadow; } -@mixin hook-card-default-header(){ border-bottom: $card-default-header-border-width solid $card-default-header-border; } -@mixin hook-card-default-footer(){ border-top: $card-default-footer-border-width solid $card-default-footer-border; } -@mixin hook-card-primary(){ box-shadow: $card-primary-box-shadow; } -@mixin hook-card-primary-title(){} -@mixin hook-card-primary-hover(){ box-shadow: $card-primary-hover-box-shadow; } -@mixin hook-card-secondary(){ box-shadow: $card-secondary-box-shadow; } -@mixin hook-card-secondary-title(){} -@mixin hook-card-secondary-hover(){ box-shadow: $card-secondary-hover-box-shadow; } -@mixin hook-card-misc(){ - - /* - * Default - */ - - .uk-card-body .uk-nav-default { margin: (-$card-body-padding-vertical + 15px) (-$card-body-padding-horizontal); } - .uk-card-title + .uk-nav-default { margin-top: 0; } - - .uk-card-body .uk-nav-default > li > a, - .uk-card-body .uk-nav-default .uk-nav-header, - .uk-card-body .uk-nav-default .uk-nav-divider { - padding-left: $card-body-padding-horizontal; - padding-right: $card-body-padding-horizontal; - } - - .uk-card-body .uk-nav-default .uk-nav-sub { padding-left: $nav-sublist-deeper-padding-left + $card-body-padding-horizontal; } - - - /* Desktop and bigger */ - @media (min-width: $breakpoint-large) { - - .uk-card-body .uk-nav-default { margin: (-$card-body-padding-vertical-l + 15px) (-$card-body-padding-horizontal-l); } - .uk-card-title + .uk-nav-default { margin-top: 0; } - - .uk-card-body .uk-nav-default > li > a, - .uk-card-body .uk-nav-default .uk-nav-header, - .uk-card-body .uk-nav-default .uk-nav-divider { - padding-left: $card-body-padding-horizontal-l; - padding-right: $card-body-padding-horizontal-l; - } - - .uk-card-body .uk-nav-default .uk-nav-sub { padding-left: $nav-sublist-deeper-padding-left + $card-body-padding-horizontal-l; } - - } - - /* - * Small - */ - - .uk-card-small .uk-nav-default { margin: (-$card-small-body-padding-vertical + 15px) (-$card-small-body-padding-horizontal); } - .uk-card-small .uk-card-title + .uk-nav-default { margin-top: 0; } - - .uk-card-small .uk-nav-default > li > a, - .uk-card-small .uk-nav-default .uk-nav-header, - .uk-card-small .uk-nav-default .uk-nav-divider { - padding-left: $card-small-body-padding-horizontal; - padding-right: $card-small-body-padding-horizontal; - } - - .uk-card-small .uk-nav-default .uk-nav-sub { padding-left: $nav-sublist-deeper-padding-left + $card-small-body-padding-horizontal; } - - /* - * Large - */ - - /* Desktop and bigger */ - @media (min-width: $breakpoint-large) { - - .uk-card-large .uk-nav-default { margin: (-$card-large-body-padding-vertical-l + 15px) (-$card-large-body-padding-horizontal-l); } - .uk-card-large .uk-card-title + .uk-nav-default { margin-top: 0; } - - } - -} -@mixin hook-close(){ - transition: 0.1s ease-in-out; - transition-property: color, opacity; -} -@mixin hook-close-hover(){} -@mixin hook-close-misc(){} -@mixin hook-inverse-close(){} -@mixin hook-inverse-close-hover(){} -@mixin hook-inverse-component-close(){ - - .uk-close { - color: $inverse-close-color; - @if(mixin-exists(hook-inverse-close)) {@include hook-inverse-close();} - } - - .uk-close:hover, - .uk-close:focus { - color: $inverse-close-hover-color; - @if(mixin-exists(hook-inverse-close-hover)) {@include hook-inverse-close-hover();} - } - -} -@mixin hook-column-misc(){} -@mixin hook-inverse-component-column(){ - - .uk-column-divider { column-rule-color: $inverse-column-divider-rule-color; } - -} -@mixin hook-comment(){} -@mixin hook-comment-body(){} -@mixin hook-comment-header(){} -@mixin hook-comment-title(){} -@mixin hook-comment-meta(){} -@mixin hook-comment-avatar(){} -@mixin hook-comment-list-adjacent(){} -@mixin hook-comment-list-sub(){} -@mixin hook-comment-list-sub-adjacent(){} -@mixin hook-comment-primary(){ - padding: $comment-primary-padding; - background-color: $comment-primary-background; -} -@mixin hook-comment-misc(){} -@mixin hook-container-misc(){} -@mixin hook-countdown(){} -@mixin hook-countdown-item(){} -@mixin hook-countdown-number(){} -@mixin hook-countdown-separator(){} -@mixin hook-countdown-label(){} -@mixin hook-countdown-misc(){} -@mixin hook-inverse-countdown-item(){} -@mixin hook-inverse-countdown-number(){} -@mixin hook-inverse-countdown-separator(){} -@mixin hook-inverse-countdown-label(){} -@mixin hook-inverse-component-countdown(){ - - .uk-countdown-number, - .uk-countdown-separator { - @if(mixin-exists(hook-inverse-countdown-item)) {@include hook-inverse-countdown-item();} - } - - .uk-countdown-number { - @if(mixin-exists(hook-inverse-countdown-number)) {@include hook-inverse-countdown-number();} - } - - .uk-countdown-separator { - @if(mixin-exists(hook-inverse-countdown-separator)) {@include hook-inverse-countdown-separator();} - } - - .uk-countdown-label { - @if(mixin-exists(hook-inverse-countdown-label)) {@include hook-inverse-countdown-label();} - } - -} -@mixin hook-cover-misc(){} -@mixin hook-description-list-term(){ - font-size: $description-list-term-font-size; - font-weight: $description-list-term-font-weight; - text-transform: $description-list-term-text-transform; -} -@mixin hook-description-list-description(){} -@mixin hook-description-list-divider-term(){} -@mixin hook-description-list-misc(){} -@mixin svg-fill($src, $color-default, $color-new, $property: background-image){ - - $escape-color-default: escape($color-default) !default; - $escape-color-new: escape("#{$color-new}") !default; - - $data-uri: data-uri('image/svg+xml;charset=UTF-8', "#{$src}") !default; - $replace-src: replace("#{$data-uri}", "#{$escape-color-default}", "#{$escape-color-new}", "g") !default; - - #{$property}: unquote($replace-src); -} -@mixin hook-divider-icon(){} -@mixin hook-divider-icon-line(){} -@mixin hook-divider-icon-line-left(){} -@mixin hook-divider-icon-line-right(){} -@mixin hook-divider-small(){} -@mixin hook-divider-misc(){} -@mixin hook-inverse-divider-icon(){} -@mixin hook-inverse-divider-icon-line(){} -@mixin hook-inverse-divider-small(){} -@mixin hook-inverse-component-divider(){ - - .uk-divider-icon { - @include svg-fill($internal-divider-icon-image, "#000", $inverse-divider-icon-color); - @if(mixin-exists(hook-inverse-divider-icon)) {@include hook-inverse-divider-icon();} - } - - .uk-divider-icon::before, - .uk-divider-icon::after { - border-bottom-color: $inverse-divider-icon-line-border; - @if(mixin-exists(hook-inverse-divider-icon-line)) {@include hook-inverse-divider-icon-line();} - } - - .uk-divider-small::after { - border-top-color: $inverse-divider-small-border; - @if(mixin-exists(hook-inverse-divider-small)) {@include hook-inverse-divider-small();} - } - -} -@mixin hook-dotnav(){} -@mixin hook-dotnav-item(){ - border: $dotnav-item-border-width solid $dotnav-item-border; - transition: 0.2s ease-in-out; - transition-property: background-color, border-color; -} -@mixin hook-dotnav-item-hover(){ border-color: $dotnav-item-hover-border; } -@mixin hook-dotnav-item-onclick(){ border-color: $dotnav-item-onclick-border; } -@mixin hook-dotnav-item-active(){ border-color: $dotnav-item-active-border; } -@mixin hook-dotnav-misc(){} -@mixin hook-inverse-dotnav-item(){ border-color: rgba($inverse-global-color, 0.9); } -@mixin hook-inverse-dotnav-item-hover(){ border-color: transparent; } -@mixin hook-inverse-dotnav-item-onclick(){ border-color: transparent; } -@mixin hook-inverse-dotnav-item-active(){ border-color: transparent; } -@mixin hook-inverse-component-dotnav(){ - - .uk-dotnav > * > * { - background-color: $inverse-dotnav-item-background; - @if(mixin-exists(hook-inverse-dotnav-item)) {@include hook-inverse-dotnav-item();} - } - - .uk-dotnav > * > :hover, - .uk-dotnav > * > :focus { - background-color: $inverse-dotnav-item-hover-background; - @if(mixin-exists(hook-inverse-dotnav-item-hover)) {@include hook-inverse-dotnav-item-hover();} - } - - .uk-dotnav > * > :active { - background-color: $inverse-dotnav-item-onclick-background; - @if(mixin-exists(hook-inverse-dotnav-item-onclick)) {@include hook-inverse-dotnav-item-onclick();} - } - - .uk-dotnav > .uk-active > * { - background-color: $inverse-dotnav-item-active-background; - @if(mixin-exists(hook-inverse-dotnav-item-active)) {@include hook-inverse-dotnav-item-active();} - } - -} -@mixin hook-drop-misc(){} -@mixin hook-dropdown(){ box-shadow: $dropdown-box-shadow; } -@mixin hook-dropdown-nav(){ font-size: $dropdown-nav-font-size; } -@mixin hook-dropdown-nav-item(){} -@mixin hook-dropdown-nav-item-hover(){} -@mixin hook-dropdown-nav-header(){} -@mixin hook-dropdown-nav-divider(){} -@mixin hook-dropdown-misc(){} -@mixin hook-flex-misc(){} -@mixin hook-form-range(){} -@mixin hook-form-range-thumb(){ border: $form-range-thumb-border-width solid $form-range-thumb-border; } -@mixin hook-form-range-track(){ border-radius: $form-range-track-border-radius; } -@mixin hook-form-range-track-focus(){} -@mixin hook-form-range-misc(){} -@mixin hook-form(){ - border: $form-border-width solid $form-border; - transition: 0.2s ease-in-out; - transition-property: color, background-color, border; -} -@mixin hook-form-single-line(){} -@mixin hook-form-multi-line(){} -@mixin hook-form-focus(){ border-color: $form-focus-border; } -@mixin hook-form-disabled(){ border-color: $form-disabled-border; } -@mixin hook-form-danger(){ border-color: $form-danger-border; } -@mixin hook-form-success(){ border-color: $form-success-border; } -@mixin hook-form-blank(){ border-color: transparent; } -@mixin hook-form-blank-focus(){ - border-color: $form-blank-focus-border; - border-style: $form-blank-focus-border-style; -} -@mixin hook-form-radio(){ - border: $form-radio-border-width solid $form-radio-border; - transition: 0.2s ease-in-out; - transition-property: background-color, border; -} -@mixin hook-form-radio-focus(){ border-color: $form-radio-focus-border; } -@mixin hook-form-radio-checked(){ border-color: $form-radio-checked-border; } -@mixin hook-form-radio-checked-focus(){} -@mixin hook-form-radio-disabled(){ border-color: $form-radio-disabled-border; } -@mixin hook-form-legend(){} -@mixin hook-form-label(){ - color: $form-label-color; - font-size: $form-label-font-size; -} -@mixin hook-form-stacked-label(){} -@mixin hook-form-horizontal-label(){} -@mixin hook-form-misc(){} -@mixin hook-inverse-form(){ border-color: $inverse-global-border; } -@mixin hook-inverse-form-focus(){ border-color: $inverse-global-color; } -@mixin hook-inverse-form-radio(){ border-color: $inverse-global-border; } -@mixin hook-inverse-form-radio-focus(){ border-color: $inverse-global-color; } -@mixin hook-inverse-form-radio-checked(){ border-color: $inverse-global-color; } -@mixin hook-inverse-form-radio-checked-focus(){} -@mixin hook-inverse-form-label(){ color: $inverse-form-label-color; } -@mixin hook-inverse-component-form(){ - - .uk-input, - .uk-select, - .uk-textarea { - background-color: $inverse-form-background; - color: $inverse-form-color; - background-clip: padding-box; - @if(mixin-exists(hook-inverse-form)) {@include hook-inverse-form();} - - &:focus { - background-color: $inverse-form-focus-background; - color: $inverse-form-focus-color; - @if(mixin-exists(hook-inverse-form-focus)) {@include hook-inverse-form-focus();} - } - } - - // - // Placeholder - // - - .uk-input:-ms-input-placeholder { color: $inverse-form-placeholder-color !important; } - .uk-input::placeholder { color: $inverse-form-placeholder-color; } - - .uk-textarea:-ms-input-placeholder { color: $inverse-form-placeholder-color !important; } - .uk-textarea::placeholder { color: $inverse-form-placeholder-color; } - - // - // Radio and checkbox - // - - .uk-select:not([multiple]):not([size]) { @include svg-fill($internal-form-select-image, "#000", $inverse-form-select-icon-color); } - - // - // Radio and checkbox - // - - .uk-radio, - .uk-checkbox { - background-color: $inverse-form-radio-background; - @if(mixin-exists(hook-inverse-form-radio)) {@include hook-inverse-form-radio();} - } - - // Focus - .uk-radio:focus, - .uk-checkbox:focus { - @if(mixin-exists(hook-inverse-form-radio-focus)) {@include hook-inverse-form-radio-focus();} - } - - // Checked - .uk-radio:checked, - .uk-checkbox:checked, - .uk-checkbox:indeterminate { - background-color: $inverse-form-radio-checked-background; - @if(mixin-exists(hook-inverse-form-radio-checked)) {@include hook-inverse-form-radio-checked();} - } - - // Focus - .uk-radio:checked:focus, - .uk-checkbox:checked:focus, - .uk-checkbox:indeterminate:focus { - background-color: $inverse-form-radio-checked-focus-background; - @if(mixin-exists(hook-inverse-form-radio-checked-focus)) {@include hook-inverse-form-radio-checked-focus();} - } - - // Icon - .uk-radio:checked { @include svg-fill($internal-form-radio-image, "#000", $inverse-form-radio-checked-icon-color); } - .uk-checkbox:checked { @include svg-fill($internal-form-checkbox-image, "#000", $inverse-form-radio-checked-icon-color); } - .uk-checkbox:indeterminate { @include svg-fill($internal-form-checkbox-indeterminate-image, "#000", $inverse-form-radio-checked-icon-color); } - - // Label - .uk-form-label { - @if(mixin-exists(hook-inverse-form-label)) {@include hook-inverse-form-label();} - } - -} -@mixin hook-grid-misc(){} -@mixin hook-inverse-component-grid(){ - - .uk-grid-divider > :not(.uk-first-column)::before { border-left-color: $inverse-grid-divider-border; } - .uk-grid-divider.uk-grid-stack > .uk-grid-margin::before { border-top-color: $inverse-grid-divider-border; } - -} -@mixin hook-heading-primary(){} -@mixin hook-heading-hero(){} -@mixin hook-heading-divider(){} -@mixin hook-heading-bullet(){} -@mixin hook-heading-line(){} -@mixin hook-heading-misc(){} -@mixin hook-inverse-heading-primary(){} -@mixin hook-inverse-heading-hero(){} -@mixin hook-inverse-heading-divider(){} -@mixin hook-inverse-heading-bullet(){} -@mixin hook-inverse-heading-line(){} -@mixin hook-inverse-component-heading(){ - - .uk-heading-primary { - @if(mixin-exists(hook-inverse-heading-primary)) {@include hook-inverse-heading-primary();} - } - - .uk-heading-hero { - @if(mixin-exists(hook-inverse-heading-hero)) {@include hook-inverse-heading-hero();} - } - - .uk-heading-divider { - border-bottom-color: $inverse-heading-divider-border; - @if(mixin-exists(hook-inverse-heading-divider)) {@include hook-inverse-heading-divider();} - } - - .uk-heading-bullet::before { - border-left-color: $inverse-heading-bullet-border; - @if(mixin-exists(hook-inverse-heading-bullet)) {@include hook-inverse-heading-bullet();} - } - - .uk-heading-line > ::before, - .uk-heading-line > ::after { - border-bottom-color: $inverse-heading-line-border; - @if(mixin-exists(hook-inverse-heading-line)) {@include hook-inverse-heading-line();} - } - -} -@mixin hook-icon-link(){} -@mixin hook-icon-link-hover(){} -@mixin hook-icon-link-active(){} -@mixin hook-icon-button(){ - transition: 0.1s ease-in-out; - transition-property: color, background-color -} -@mixin hook-icon-button-hover(){} -@mixin hook-icon-button-active(){} -@mixin hook-icon-misc(){} -@mixin hook-inverse-icon-link(){} -@mixin hook-inverse-icon-link-hover(){} -@mixin hook-inverse-icon-link-active(){} -@mixin hook-inverse-icon-button(){} -@mixin hook-inverse-icon-button-hover(){} -@mixin hook-inverse-icon-button-active(){} -@mixin hook-inverse-component-icon(){ - - // - // Link - // - - .uk-icon-link { - color: $inverse-icon-link-color; - @if(mixin-exists(hook-inverse-icon-link)) {@include hook-inverse-icon-link();} - } - - .uk-icon-link:hover, - .uk-icon-link:focus { - color: $inverse-icon-link-hover-color; - @if(mixin-exists(hook-inverse-icon-link-hover)) {@include hook-inverse-icon-link-hover();} - } - - .uk-icon-link:active, - .uk-active > .uk-icon-link { - color: $inverse-icon-link-active-color; - @if(mixin-exists(hook-inverse-icon-link-active)) {@include hook-inverse-icon-link-active();} - } - - // - // Button - // - - .uk-icon-button { - background-color: $inverse-icon-button-background; - color: $inverse-icon-button-color; - @if(mixin-exists(hook-inverse-icon-button)) {@include hook-inverse-icon-button();} - } - - .uk-icon-button:hover, - .uk-icon-button:focus { - background-color: $inverse-icon-button-hover-background; - color: $inverse-icon-button-hover-color; - @if(mixin-exists(hook-inverse-icon-button-hover)) {@include hook-inverse-icon-button-hover();} - } - - .uk-icon-button:active { - background-color: $inverse-icon-button-active-background; - color: $inverse-icon-button-active-color; - @if(mixin-exists(hook-inverse-icon-button-active)) {@include hook-inverse-icon-button-active();} - } - -} -@mixin hook-iconnav(){} -@mixin hook-iconnav-item(){} -@mixin hook-iconnav-item-hover(){} -@mixin hook-iconnav-item-active(){} -@mixin hook-iconnav-misc(){} -@mixin hook-inverse-iconnav-item(){} -@mixin hook-inverse-iconnav-item-hover(){} -@mixin hook-inverse-iconnav-item-active(){} -@mixin hook-inverse-component-iconnav(){ - - .uk-iconnav > * > a { - color: $inverse-iconnav-item-color; - @if(mixin-exists(hook-inverse-iconnav-item)) {@include hook-inverse-iconnav-item();} - } - - .uk-iconnav > * > a:hover, - .uk-iconnav > * > a:focus { - color: $inverse-iconnav-item-hover-color; - @if(mixin-exists(hook-inverse-iconnav-item-hover)) {@include hook-inverse-iconnav-item-hover();} - } - - .uk-iconnav > .uk-active > a { - color: $inverse-iconnav-item-active-color; - @if(mixin-exists(hook-inverse-iconnav-item-active)) {@include hook-inverse-iconnav-item-active();} - } - -} -@mixin hook-inverse-component-link(){ - - a.uk-link-muted, - .uk-link-muted a { - color: $inverse-link-muted-color; - @if(mixin-exists(hook-inverse-link-muted)) {@include hook-inverse-link-muted();} - } - - a.uk-link-muted:hover, - .uk-link-muted a:hover { - color: $inverse-link-muted-hover-color; - @if(mixin-exists(hook-inverse-link-muted-hover)) {@include hook-inverse-link-muted-hover();} - } - - a.uk-link-text:hover, - .uk-link-text a:hover { - color: $inverse-link-text-hover-color; - @if(mixin-exists(hook-inverse-link-text-hover)) {@include hook-inverse-link-text-hover();} - } - - a.uk-link-heading:hover, - .uk-link-heading a:hover { - color: $inverse-link-heading-hover-color; - @if(mixin-exists(hook-inverse-link-heading-hover)) {@include hook-inverse-link-heading-hover();} - } - -} -@mixin hook-inverse-component-list(){ - - .uk-list-divider > li:nth-child(n+2) { - border-top-color: $inverse-list-divider-border; - @if(mixin-exists(hook-inverse-list-divider)) {@include hook-inverse-list-divider();} - } - - .uk-list-striped > li { - @if(mixin-exists(hook-inverse-list-striped)) {@include hook-inverse-list-striped();} - } - - .uk-list-striped > li:nth-of-type(odd) { background-color: $inverse-list-striped-background; } - - .uk-list-bullet > li::before { - @include svg-fill($internal-list-bullet-image, "#000", $inverse-list-bullet-icon-color); - @if(mixin-exists(hook-inverse-list-bullet)) {@include hook-inverse-list-bullet();} - } - -} -@mixin hook-inverse-component-totop(){ - - .uk-totop { - color: $inverse-totop-color; - @if(mixin-exists(hook-inverse-totop)) {@include hook-inverse-totop();} - } - - .uk-totop:hover, - .uk-totop:focus { - color: $inverse-totop-hover-color; - @if(mixin-exists(hook-inverse-totop-hover)) {@include hook-inverse-totop-hover();} - } - - .uk-totop:active { - color: $inverse-totop-active-color; - @if(mixin-exists(hook-inverse-totop-active)) {@include hook-inverse-totop-active();} - } - -} -@mixin hook-inverse-component-label(){ - - .uk-label { - background-color: $inverse-label-background; - color: $inverse-label-color; - @if(mixin-exists(hook-inverse-label)) {@include hook-inverse-label();} - } - -} -@mixin hook-inverse-component-search(){ - - // - // Input - // - - .uk-search-input { color: $inverse-search-color; } - - .uk-search-input:-ms-input-placeholder { color: $inverse-search-placeholder-color !important; } - .uk-search-input::placeholder { color: $inverse-search-placeholder-color; } - - - // - // Icon - // - - .uk-search .uk-search-icon { color: $inverse-search-icon-color; } - - .uk-search .uk-search-icon:hover { color: $inverse-search-icon-color; } - - // - // Style modifier - // - - .uk-search-default .uk-search-input { - background-color: $inverse-search-default-background; - @if(mixin-exists(hook-inverse-search-default-input)) {@include hook-inverse-search-default-input();} - } - .uk-search-default .uk-search-input:focus { - background-color: $inverse-search-default-background; - @if(mixin-exists(hook-inverse-search-default-input-focus)) {@include hook-inverse-search-default-input-focus();} - } - - .uk-search-navbar .uk-search-input { - background-color: $inverse-search-navbar-background; - @if(mixin-exists(hook-inverse-search-navbar-input)) {@include hook-inverse-search-navbar-input();} - } - - .uk-search-large .uk-search-input { - background-color: $inverse-search-large-background; - @if(mixin-exists(hook-inverse-search-large-input)) {@include hook-inverse-search-large-input();} - } - - // - // Toggle - // - - .uk-search-toggle { - color: $inverse-search-toggle-color; - @if(mixin-exists(hook-inverse-search-toggle)) {@include hook-inverse-search-toggle();} - } - - .uk-search-toggle:hover, - .uk-search-toggle:focus { - color: $inverse-search-toggle-hover-color; - @if(mixin-exists(hook-inverse-search-toggle-hover)) {@include hook-inverse-search-toggle-hover();} - } - -} -@mixin hook-inverse-component-nav(){ - - // - // Parent icon modifier - // - - .uk-nav-parent-icon > .uk-parent > a::after { - @include svg-fill($internal-nav-parent-close-image, "#000", $inverse-nav-parent-icon-color); - @if(mixin-exists(hook-inverse-nav-parent-icon)) {@include hook-inverse-nav-parent-icon();} - } - - .uk-nav-parent-icon > .uk-parent.uk-open > a::after { @include svg-fill($internal-nav-parent-open-image, "#000", $inverse-nav-parent-icon-color); } - - // - // Default - // - - .uk-nav-default > li > a { - color: $inverse-nav-default-item-color; - @if(mixin-exists(hook-inverse-nav-default-item)) {@include hook-inverse-nav-default-item();} - } - - .uk-nav-default > li > a:hover, - .uk-nav-default > li > a:focus { - color: $inverse-nav-default-item-hover-color; - @if(mixin-exists(hook-inverse-nav-default-item-hover)) {@include hook-inverse-nav-default-item-hover();} - } - - .uk-nav-default > li.uk-active > a { - color: $inverse-nav-default-item-active-color; - @if(mixin-exists(hook-inverse-nav-default-item-active)) {@include hook-inverse-nav-default-item-active();} - } - - .uk-nav-default .uk-nav-header { - color: $inverse-nav-default-header-color; - @if(mixin-exists(hook-inverse-nav-default-header)) {@include hook-inverse-nav-default-header();} - } - - .uk-nav-default .uk-nav-divider { - border-top-color: $inverse-nav-default-divider-border; - @if(mixin-exists(hook-inverse-nav-default-divider)) {@include hook-inverse-nav-default-divider();} - } - - .uk-nav-default .uk-nav-sub a { color: $inverse-nav-default-sublist-item-color; } - - .uk-nav-default .uk-nav-sub a:hover, - .uk-nav-default .uk-nav-sub a:focus { color: $inverse-nav-default-sublist-item-hover-color; } - - // - // Primary - // - - .uk-nav-primary > li > a { - color: $inverse-nav-primary-item-color; - @if(mixin-exists(hook-inverse-nav-primary-item)) {@include hook-inverse-nav-primary-item();} - } - - .uk-nav-primary > li > a:hover, - .uk-nav-primary > li > a:focus { - color: $inverse-nav-primary-item-hover-color; - @if(mixin-exists(hook-inverse-nav-primary-item-hover)) {@include hook-inverse-nav-primary-item-hover();} - } - - .uk-nav-primary > li.uk-active > a { - color: $inverse-nav-primary-item-active-color; - @if(mixin-exists(hook-inverse-nav-primary-item-active)) {@include hook-inverse-nav-primary-item-active();} - } - - .uk-nav-primary .uk-nav-header { - color: $inverse-nav-primary-header-color; - @if(mixin-exists(hook-inverse-nav-primary-header)) {@include hook-inverse-nav-primary-header();} - } - - .uk-nav-primary .uk-nav-divider { - border-top-color: $inverse-nav-primary-divider-border; - @if(mixin-exists(hook-inverse-nav-primary-divider)) {@include hook-inverse-nav-primary-divider();} - } - - .uk-nav-primary .uk-nav-sub a { color: $inverse-nav-primary-sublist-item-color; } - - .uk-nav-primary .uk-nav-sub a:hover, - .uk-nav-primary .uk-nav-sub a:focus { color: $inverse-nav-primary-sublist-item-hover-color; } - -} -@mixin hook-inverse-component-navbar(){ - - .uk-navbar-nav > li > a { - color: $inverse-navbar-nav-item-color; - @if(mixin-exists(hook-inverse-navbar-nav-item)) {@include hook-inverse-navbar-nav-item();} - } - - .uk-navbar-nav > li:hover > a, - .uk-navbar-nav > li > a:focus, - .uk-navbar-nav > li > a.uk-open { - color: $inverse-navbar-nav-item-hover-color; - @if(mixin-exists(hook-inverse-navbar-nav-item-hover)) {@include hook-inverse-navbar-nav-item-hover();} - } - - .uk-navbar-nav > li > a:active { - color: $inverse-navbar-nav-item-onclick-color; - @if(mixin-exists(hook-inverse-navbar-nav-item-onclick)) {@include hook-inverse-navbar-nav-item-onclick();} - } - - .uk-navbar-nav > li.uk-active > a { - color: $inverse-navbar-nav-item-active-color; - @if(mixin-exists(hook-inverse-navbar-nav-item-active)) {@include hook-inverse-navbar-nav-item-active();} - } - - .uk-navbar-item { - color: $inverse-navbar-item-color; - @if(mixin-exists(hook-inverse-navbar-item)) {@include hook-inverse-navbar-item();} - } - - .uk-navbar-toggle { - color: $inverse-navbar-toggle-color; - @if(mixin-exists(hook-inverse-navbar-toggle)) {@include hook-inverse-navbar-toggle();} - } - - .uk-navbar-toggle:hover, - .uk-navbar-toggle:focus, - .uk-navbar-toggle.uk-open { - color: $inverse-navbar-toggle-hover-color; - @if(mixin-exists(hook-inverse-navbar-toggle-hover)) {@include hook-inverse-navbar-toggle-hover();} - } - -} -@mixin hook-inverse-component-subnav(){ - - .uk-subnav > * > :first-child { - color: $inverse-subnav-item-color; - @if(mixin-exists(hook-inverse-subnav-item)) {@include hook-inverse-subnav-item();} - } - - .uk-subnav > * > a:hover, - .uk-subnav > * > a:focus { - color: $inverse-subnav-item-hover-color; - @if(mixin-exists(hook-inverse-subnav-item-hover)) {@include hook-inverse-subnav-item-hover();} - } - - .uk-subnav > .uk-active > a { - color: $inverse-subnav-item-active-color; - @if(mixin-exists(hook-inverse-subnav-item-active)) {@include hook-inverse-subnav-item-active();} - } - - // - // Divider - // - - .uk-subnav-divider > :nth-child(n+2):not(.uk-first-column)::before { - border-left-color: $inverse-subnav-divider-border; - @if(mixin-exists(hook-inverse-subnav-divider)) {@include hook-inverse-subnav-divider();} - } - - // - // Pill - // - - .uk-subnav-pill > * > :first-child { - background-color: $inverse-subnav-pill-item-background; - color: $inverse-subnav-pill-item-color; - @if(mixin-exists(hook-inverse-subnav-pill-item)) {@include hook-inverse-subnav-pill-item();} - } - - .uk-subnav-pill > * > a:hover, - .uk-subnav-pill > * > a:focus { - background-color: $inverse-subnav-pill-item-hover-background; - color: $inverse-subnav-pill-item-hover-color; - @if(mixin-exists(hook-inverse-subnav-pill-item-hover)) {@include hook-inverse-subnav-pill-item-hover();} - } - - .uk-subnav-pill > * > a:active { - background-color: $inverse-subnav-pill-item-onclick-background; - color: $inverse-subnav-pill-item-onclick-color; - @if(mixin-exists(hook-inverse-subnav-pill-item-onclick)) {@include hook-inverse-subnav-pill-item-onclick();} - } - - .uk-subnav-pill > .uk-active > a { - background-color: $inverse-subnav-pill-item-active-background; - color: $inverse-subnav-pill-item-active-color; - @if(mixin-exists(hook-inverse-subnav-pill-item-active)) {@include hook-inverse-subnav-pill-item-active();} - } - - // - // Disabled - // - - .uk-subnav > .uk-disabled > a { - color: $inverse-subnav-item-disabled-color; - @if(mixin-exists(hook-inverse-subnav-item-disabled)) {@include hook-inverse-subnav-item-disabled();} - } - -} -@mixin hook-inverse-component-pagination(){ - - .uk-pagination > * > * { - color: $inverse-pagination-item-color; - @if(mixin-exists(hook-inverse-pagination-item)) {@include hook-inverse-pagination-item();} - } - - .uk-pagination > * > :hover, - .uk-pagination > * > :focus { - color: $inverse-pagination-item-hover-color; - @if(mixin-exists(hook-inverse-pagination-item-hover)) {@include hook-inverse-pagination-item-hover();} - } - - .uk-pagination > .uk-active > * { - color: $inverse-pagination-item-active-color; - @if(mixin-exists(hook-inverse-pagination-item-active)) {@include hook-inverse-pagination-item-active();} - } - - .uk-pagination > .uk-disabled > * { - color: $inverse-pagination-item-disabled-color; - @if(mixin-exists(hook-inverse-pagination-item-disabled)) {@include hook-inverse-pagination-item-disabled();} - } - -} -@mixin hook-inverse-component-tab(){ - - .uk-tab { - @if(mixin-exists(hook-inverse-tab)) {@include hook-inverse-tab();} - } - - .uk-tab > * > a { - color: $inverse-tab-item-color; - @if(mixin-exists(hook-inverse-tab-item)) {@include hook-inverse-tab-item();} - } - - .uk-tab > * > a:hover, - .uk-tab > * > a:focus{ - color: $inverse-tab-item-hover-color; - @if(mixin-exists(hook-inverse-tab-item-hover)) {@include hook-inverse-tab-item-hover();} - } - - .uk-tab > .uk-active > a { - color: $inverse-tab-item-active-color; - @if(mixin-exists(hook-inverse-tab-item-active)) {@include hook-inverse-tab-item-active();} - } - - .uk-tab > .uk-disabled > a { - color: $inverse-tab-item-disabled-color; - @if(mixin-exists(hook-inverse-tab-item-disabled)) {@include hook-inverse-tab-item-disabled();} - } - -} -@mixin hook-inverse-component-slidenav(){ - - .uk-slidenav { - color: $inverse-slidenav-color; - @if(mixin-exists(hook-inverse-slidenav)) {@include hook-inverse-slidenav();} - } - - .uk-slidenav:hover, - .uk-slidenav:focus { - color: $inverse-slidenav-hover-color; - @if(mixin-exists(hook-inverse-slidenav-hover)) {@include hook-inverse-slidenav-hover();} - } - - .uk-slidenav:active { - color: $inverse-slidenav-active-color; - @if(mixin-exists(hook-inverse-slidenav-active)) {@include hook-inverse-slidenav-active();} - } - -} -@mixin hook-inverse-component-text(){ - - .uk-text-lead { - color: $inverse-text-lead-color; - @if(mixin-exists(hook-inverse-text-lead)) {@include hook-inverse-text-lead();} - } - - .uk-text-meta { - color: $inverse-text-meta-color; - @if(mixin-exists(hook-inverse-text-meta)) {@include hook-inverse-text-meta();} - } - - .uk-text-muted { color: $inverse-text-muted-color !important; } - .uk-text-primary { color: $inverse-text-primary-color !important; } - -} -@mixin hook-inverse-component-utility(){ - - .uk-dropcap::first-letter, - .uk-dropcap p:first-of-type::first-letter { - @if(mixin-exists(hook-inverse-dropcap)) {@include hook-inverse-dropcap();} - } - - .uk-leader-fill { - @if(mixin-exists(hook-inverse-leader)) {@include hook-inverse-leader();} - } - - .uk-logo { - color: $inverse-logo-color; - @if(mixin-exists(hook-inverse-logo)) {@include hook-inverse-logo();} - } - - .uk-logo:hover, - .uk-logo:focus { - color: $inverse-logo-hover-color; - @if(mixin-exists(hook-inverse-logo-hover)) {@include hook-inverse-logo-hover();} - } - - .uk-logo > :not(.uk-logo-inverse):not(:only-of-type) { display: none; } - .uk-logo-inverse { display: inline; } - -} -@mixin hook-inverse(){ - @include hook-inverse-component-base(); - @include hook-inverse-component-link(); - @include hook-inverse-component-heading(); - @include hook-inverse-component-divider(); - @include hook-inverse-component-list(); - @include hook-inverse-component-icon(); - @include hook-inverse-component-form(); - @include hook-inverse-component-button(); - @include hook-inverse-component-grid(); - @include hook-inverse-component-close(); - @include hook-inverse-component-totop(); - @include hook-inverse-component-badge(); - @include hook-inverse-component-label(); - @include hook-inverse-component-article(); - @include hook-inverse-component-search(); - @include hook-inverse-component-nav(); - @include hook-inverse-component-navbar(); - @include hook-inverse-component-subnav(); - @include hook-inverse-component-breadcrumb(); - @include hook-inverse-component-pagination(); - @include hook-inverse-component-tab(); - @include hook-inverse-component-slidenav(); - @include hook-inverse-component-dotnav(); - @include hook-inverse-component-accordion(); - @include hook-inverse-component-iconnav(); - @include hook-inverse-component-text(); - @include hook-inverse-component-column(); - @include hook-inverse-component-utility(); -} -@mixin hook-label(){ - border-radius: $label-border-radius; - text-transform: $label-text-transform; -} -@mixin hook-label-success(){} -@mixin hook-label-warning(){} -@mixin hook-label-danger(){} -@mixin hook-label-misc(){} -@mixin hook-inverse-label(){} -@mixin hook-lightbox(){} -@mixin hook-lightbox-item(){} -@mixin hook-lightbox-toolbar(){} -@mixin hook-lightbox-toolbar-icon(){} -@mixin hook-lightbox-toolbar-icon-hover(){} -@mixin hook-lightbox-button(){} -@mixin hook-lightbox-button-hover(){} -@mixin hook-lightbox-misc(){} -@mixin hook-link-muted(){} -@mixin hook-link-muted-hover(){} -@mixin hook-link-text(){} -@mixin hook-link-text-hover(){} -@mixin hook-link-heading(){} -@mixin hook-link-heading-hover(){} -@mixin hook-link-reset(){} -@mixin hook-link-misc(){} -@mixin hook-inverse-link-muted(){} -@mixin hook-inverse-link-muted-hover(){} -@mixin hook-inverse-link-text-hover(){} -@mixin hook-inverse-link-heading-hover(){} -@mixin hook-list-divider(){} -@mixin hook-list-striped(){ - - &:nth-of-type(odd) { - border-top: $list-striped-border-width solid $list-striped-border; - border-bottom: $list-striped-border-width solid $list-striped-border; - } - -} -@mixin hook-list-bullet(){} -@mixin hook-list-misc(){} -@mixin hook-inverse-list-divider(){} -@mixin hook-inverse-list-striped(){ - - &:nth-of-type(odd) { - border-top-color: $inverse-global-border; - border-bottom-color: $inverse-global-border; - } - -} -@mixin hook-inverse-list-bullet(){} -@mixin hook-margin-misc(){} -@mixin hook-marker(){ - border-radius: 500px; -} -@mixin hook-marker-hover(){} -@mixin hook-marker-misc(){} -@mixin hook-inverse-marker(){} -@mixin hook-inverse-marker-hover(){} -@mixin hook-inverse-component-marker(){ - - .uk-marker { - background: $inverse-marker-background; - color: $inverse-marker-color; - @if(mixin-exists(hook-inverse-marker)) {@include hook-inverse-marker();} - } - - .uk-marker:hover, - .uk-marker:focus { - color: $inverse-marker-hover-color; - @if(mixin-exists(hook-inverse-marker-hover)) {@include hook-inverse-marker-hover();} - } - -} -@mixin hook-modal(){} -@mixin hook-modal-dialog(){} -@mixin hook-modal-full(){} -@mixin hook-modal-body(){} -@mixin hook-modal-header(){ border-bottom: $modal-header-border-width solid $modal-header-border; } -@mixin hook-modal-footer(){ border-top: $modal-footer-border-width solid $modal-footer-border; } -@mixin hook-modal-title(){} -@mixin hook-modal-close(){} -@mixin hook-modal-close-hover(){} -@mixin hook-modal-close-default(){} -@mixin hook-modal-close-default-hover(){} -@mixin hook-modal-close-outside(){} -@mixin hook-modal-close-outside-hover(){} -@mixin hook-modal-close-full(){ - top: 0; - right: 0; - padding: $modal-close-full-padding; - background: $modal-close-full-background; -} -@mixin hook-modal-close-full-hover(){} -@mixin hook-modal-misc(){} -@mixin hook-nav-sub(){} -@mixin hook-nav-parent-icon(){} -@mixin hook-nav-header(){} -@mixin hook-nav-divider(){} -@mixin hook-nav-default(){ font-size: $nav-default-font-size; } -@mixin hook-nav-default-item(){} -@mixin hook-nav-default-item-hover(){} -@mixin hook-nav-default-item-active(){} -@mixin hook-nav-default-header(){} -@mixin hook-nav-default-divider(){} -@mixin hook-nav-primary(){} -@mixin hook-nav-primary-item(){} -@mixin hook-nav-primary-item-hover(){} -@mixin hook-nav-primary-item-active(){} -@mixin hook-nav-primary-header(){} -@mixin hook-nav-primary-divider(){} -@mixin hook-nav-misc(){} -@mixin hook-inverse-nav-parent-icon(){} -@mixin hook-inverse-nav-default-item(){} -@mixin hook-inverse-nav-default-item-hover(){} -@mixin hook-inverse-nav-default-item-active(){} -@mixin hook-inverse-nav-default-header(){} -@mixin hook-inverse-nav-default-divider(){} -@mixin hook-inverse-nav-primary-item(){} -@mixin hook-inverse-nav-primary-item-hover(){} -@mixin hook-inverse-nav-primary-item-active(){} -@mixin hook-inverse-nav-primary-header(){} -@mixin hook-inverse-nav-primary-divider(){} -@mixin hook-navbar(){} -@mixin hook-navbar-container(){} -@mixin hook-navbar-nav-item(){ - text-transform: $navbar-nav-item-text-transform; - transition: 0.1s ease-in-out; - transition-property: color, background-color; -} -@mixin hook-navbar-nav-item-hover(){} -@mixin hook-navbar-nav-item-onclick(){} -@mixin hook-navbar-nav-item-active(){} -@mixin hook-navbar-item(){} -@mixin hook-navbar-toggle(){} -@mixin hook-navbar-toggle-hover(){} -@mixin hook-navbar-toggle-icon(){} -@mixin hook-navbar-toggle-icon-hover(){} -@mixin hook-navbar-subtitle(){} -@mixin hook-navbar-transparent(){} -@mixin hook-navbar-sticky(){} -@mixin hook-navbar-dropdown(){ box-shadow: $navbar-dropdown-box-shadow; } -@mixin hook-navbar-dropdown-dropbar(){ box-shadow: none; } -@mixin hook-navbar-dropdown-nav(){ font-size: $navbar-dropdown-nav-font-size; } -@mixin hook-navbar-dropdown-nav-item(){} -@mixin hook-navbar-dropdown-nav-item-hover(){} -@mixin hook-navbar-dropdown-nav-item-active(){} -@mixin hook-navbar-dropdown-nav-header(){} -@mixin hook-navbar-dropdown-nav-divider(){} -@mixin hook-navbar-dropbar(){} -@mixin hook-navbar-dropbar-slide(){ box-shadow: $navbar-dropbar-box-shadow; } -@mixin hook-navbar-misc(){ - - /* - * Navbar - */ - - .uk-navbar-container > .uk-container .uk-navbar-left { - margin-left: (-$navbar-nav-item-padding-horizontal); - margin-right: (-$navbar-nav-item-padding-horizontal); - } - .uk-navbar-container > .uk-container .uk-navbar-right { margin-right: (-$navbar-nav-item-padding-horizontal); } - - /* - * Grid Divider - */ - - .uk-navbar-dropdown-grid > * { position: relative; } - - .uk-navbar-dropdown-grid > :not(.uk-first-column)::before { - content: ""; - position: absolute; - top: 0; - bottom: 0; - left: ($navbar-dropdown-grid-gutter-horizontal / 2); - border-left: $navbar-dropdown-grid-divider-border-width solid $navbar-dropdown-grid-divider-border; - } - - /* Vertical */ - .uk-navbar-dropdown-grid.uk-grid-stack > .uk-grid-margin::before { - content: ""; - position: absolute; - top: -($navbar-dropdown-grid-gutter-vertical / 2); - left: $navbar-dropdown-grid-gutter-horizontal; - right: 0; - border-top: $navbar-dropdown-grid-divider-border-width solid $navbar-dropdown-grid-divider-border; - } - -} -@mixin hook-inverse-navbar-nav-item(){} -@mixin hook-inverse-navbar-nav-item-hover(){} -@mixin hook-inverse-navbar-nav-item-onclick(){} -@mixin hook-inverse-navbar-nav-item-active(){} -@mixin hook-inverse-navbar-item(){} -@mixin hook-inverse-navbar-toggle(){} -@mixin hook-inverse-navbar-toggle-hover(){} -@mixin hook-notification(){} -@mixin hook-notification-message(){} -@mixin hook-notification-close(){} -@mixin hook-notification-message-primary(){} -@mixin hook-notification-message-success(){} -@mixin hook-notification-message-warning(){} -@mixin hook-notification-message-danger(){} -@mixin hook-notification-misc(){} -@mixin hook-offcanvas-bar(){} -@mixin hook-offcanvas-close(){} -@mixin hook-offcanvas-overlay(){} -@mixin hook-offcanvas-misc(){} -@mixin hook-overlay(){} -@mixin hook-overlay-icon(){} -@mixin hook-overlay-default(){} -@mixin hook-overlay-primary(){} -@mixin hook-overlay-misc(){} -@mixin hook-padding-misc(){} -@mixin hook-pagination(){} -@mixin hook-pagination-item(){ transition: color 0.1s ease-in-out; } -@mixin hook-pagination-item-hover(){} -@mixin hook-pagination-item-active(){} -@mixin hook-pagination-item-disabled(){} -@mixin hook-pagination-misc(){} -@mixin hook-inverse-pagination-item(){} -@mixin hook-inverse-pagination-item-hover(){} -@mixin hook-inverse-pagination-item-active(){} -@mixin hook-inverse-pagination-item-disabled(){} -@mixin hook-placeholder(){ border: $placeholder-border-width dashed $placeholder-border; } -@mixin hook-placeholder-misc(){} -@mixin hook-position-misc(){} -@mixin hook-print(){} -@mixin hook-progress(){ - border-radius: $progress-border-radius; - overflow: hidden; -} -@mixin hook-progress-bar(){} -@mixin hook-progress-misc(){} -@mixin hook-search-input(){} -@mixin hook-search-default-input(){ border: $search-default-border-width solid $search-default-border; } -@mixin hook-search-default-input-focus(){} -@mixin hook-search-navbar-input(){} -@mixin hook-search-large-input(){} -@mixin hook-search-toggle(){} -@mixin hook-search-toggle-hover(){} -@mixin hook-search-misc(){} -@mixin hook-inverse-search-default-input(){ border-color: $inverse-global-border; } -@mixin hook-inverse-search-default-input-focus(){} -@mixin hook-inverse-search-navbar-input(){} -@mixin hook-inverse-search-large-input(){} -@mixin hook-inverse-search-toggle(){} -@mixin hook-inverse-search-toggle-hover(){} -@mixin hook-section(){} -@mixin hook-section-default(){} -@mixin hook-section-muted(){} -@mixin hook-section-primary(){} -@mixin hook-section-secondary(){} -@mixin hook-section-overlap(){} -@mixin hook-section-misc(){} -@mixin hook-slidenav(){ transition: color 0.1s ease-in-out; } -@mixin hook-slidenav-hover(){} -@mixin hook-slidenav-active(){} -@mixin hook-slidenav-previous(){} -@mixin hook-slidenav-next(){} -@mixin hook-slidenav-large(){} -@mixin hook-slidenav-container(){} -@mixin hook-slidenav-misc(){} -@mixin hook-inverse-slidenav(){} -@mixin hook-inverse-slidenav-hover(){} -@mixin hook-inverse-slidenav-active(){} -@mixin hook-slider(){} -@mixin hook-slider-misc(){} -@mixin hook-slideshow(){} -@mixin hook-slideshow-misc(){} -@mixin hook-sortable(){} -@mixin hook-sortable-drag(){} -@mixin hook-sortable-placeholder(){} -@mixin hook-sortable-empty(){} -@mixin hook-sortable-misc(){} -@mixin hook-spinner(){} -@mixin hook-spinner-misc(){} -@mixin hook-sticky-misc(){} -@mixin hook-subnav(){} -@mixin hook-subnav-item(){ - font-size: $subnav-item-font-size; - text-transform: $subnav-item-text-transform; - transition: 0.1s ease-in-out; - transition-property: color, background-color; -} -@mixin hook-subnav-item-hover(){} -@mixin hook-subnav-item-active(){} -@mixin hook-subnav-divider(){} -@mixin hook-subnav-pill-item(){} -@mixin hook-subnav-pill-item-hover(){} -@mixin hook-subnav-pill-item-onclick(){} -@mixin hook-subnav-pill-item-active(){} -@mixin hook-subnav-item-disabled(){} -@mixin hook-subnav-misc(){} -@mixin hook-inverse-subnav-item(){} -@mixin hook-inverse-subnav-item-hover(){} -@mixin hook-inverse-subnav-item-active(){} -@mixin hook-inverse-subnav-divider(){} -@mixin hook-inverse-subnav-pill-item(){} -@mixin hook-inverse-subnav-pill-item-hover(){} -@mixin hook-inverse-subnav-pill-item-onclick(){} -@mixin hook-inverse-subnav-pill-item-active(){} -@mixin hook-inverse-subnav-item-disabled(){} -@mixin hook-switcher-misc(){} -@mixin hook-tab(){ - - position: relative; - - &::before { - content: ""; - position: absolute; - bottom: 0; - left: $tab-margin-horizontal; - right: 0; - border-bottom: $tab-border-width solid $tab-border; - } - -} -@mixin hook-tab-item(){ - border-bottom: $tab-item-border-width solid transparent; - font-size: $tab-item-font-size; - text-transform: $tab-item-text-transform; - transition: color 0.1s ease-in-out; -} -@mixin hook-tab-item-hover(){} -@mixin hook-tab-item-active(){ border-color: $tab-item-active-border; } -@mixin hook-tab-item-disabled(){} -@mixin hook-tab-bottom(){ - - &::before { - top: 0; - bottom: auto; - } - -} -@mixin hook-tab-bottom-item(){ - border-top: $tab-item-border-width solid transparent; - border-bottom: none; -} -@mixin hook-tab-left(){ - - &::before { - top: 0; - bottom: 0; - left: auto; - right: 0; - border-left: $tab-border-width solid $tab-border; - border-bottom: none; - } - -} -@mixin hook-tab-right(){ - - &::before { - top: 0; - bottom: 0; - left: 0; - right: auto; - border-left: $tab-border-width solid $tab-border; - border-bottom: none; - } - -} -@mixin hook-tab-left-item(){ - border-right: $tab-item-border-width solid transparent; - border-bottom: none; -} -@mixin hook-tab-right-item(){ - border-left: $tab-item-border-width solid transparent; - border-bottom: none; -} -@mixin hook-tab-misc(){ - - .uk-tab .uk-dropdown { margin-left: ($tab-margin-horizontal + $tab-item-padding-horizontal) } - -} -@mixin hook-inverse-tab(){ - - &::before { border-color: $inverse-tab-border; } - -} -@mixin hook-inverse-tab-item(){} -@mixin hook-inverse-tab-item-hover(){} -@mixin hook-inverse-tab-item-active(){ border-color: $inverse-global-primary-background; } -@mixin hook-inverse-tab-item-disabled(){} -@mixin hook-table(){} -@mixin hook-table-header-cell(){ text-transform: uppercase; } -@mixin hook-table-cell(){} -@mixin hook-table-footer(){} -@mixin hook-table-caption(){} -@mixin hook-table-row-active(){} -@mixin hook-table-divider(){} -@mixin hook-table-striped(){ - border-top: $table-striped-border-width solid $table-striped-border; - border-bottom: $table-striped-border-width solid $table-striped-border; -} -@mixin hook-table-hover(){} -@mixin hook-table-small(){} -@mixin hook-table-large(){} -@mixin hook-table-misc(){ - - .uk-table tbody tr { transition: background-color 0.1s linear; } - -} -@mixin hook-inverse-table-header-cell(){} -@mixin hook-inverse-table-caption(){} -@mixin hook-inverse-table-row-active(){} -@mixin hook-inverse-table-divider(){} -@mixin hook-inverse-table-striped(){ - border-top-color: $inverse-global-border; - border-bottom-color: $inverse-global-border; -} -@mixin hook-inverse-table-hover(){} -@mixin hook-inverse-component-table(){ - - .uk-table th { - color: $inverse-table-header-cell-color; - @if(mixin-exists(hook-inverse-table-header-cell)) {@include hook-inverse-table-header-cell();} - } - - .uk-table caption { - color: $inverse-table-caption-color; - @if(mixin-exists(hook-inverse-table-caption)) {@include hook-inverse-table-caption();} - } - - .uk-table > tr.uk-active, - .uk-table tbody tr.uk-active { - background: $inverse-table-row-active-background; - @if(mixin-exists(hook-inverse-table-row-active)) {@include hook-inverse-table-row-active();} - } - - .uk-table-divider > tr:not(:first-child), - .uk-table-divider > :not(:first-child) > tr, - .uk-table-divider > :first-child > tr:not(:first-child) { - border-top-color: $inverse-table-divider-border; - @if(mixin-exists(hook-inverse-table-divider)) {@include hook-inverse-table-divider();} - } - - .uk-table-striped > tr:nth-of-type(odd), - .uk-table-striped tbody tr:nth-of-type(odd) { - background: $inverse-table-striped-row-background; - @if(mixin-exists(hook-inverse-table-striped)) {@include hook-inverse-table-striped();} - } - - .uk-table-hover > tr:hover, - .uk-table-hover tbody tr:hover { - background: $inverse-table-hover-row-background; - @if(mixin-exists(hook-inverse-table-hover)) {@include hook-inverse-table-hover();} - } - -} -@mixin hook-text-lead(){} -@mixin hook-text-meta(){ - - a { color: $text-meta-link-color; } - - a:hover { - color: $text-meta-link-hover-color; - text-decoration: none; - } - -} -@mixin hook-text-small(){} -@mixin hook-text-large(){} -@mixin hook-text-background(){} -@mixin hook-text-misc(){} -@mixin hook-inverse-text-lead(){} -@mixin hook-inverse-text-meta(){} -@mixin hook-thumbnav(){} -@mixin hook-thumbnav-item(){ - - position: relative; - - &::after { - content: ""; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - background: $thumbnav-item-background; - transition: background-color 0.1s ease-in-out; - } - -} -@mixin hook-thumbnav-item-hover(){ - &::after { background-color: $thumbnav-item-hover-background; } -} -@mixin hook-thumbnav-item-active(){ - &::after { background-color: $thumbnav-item-active-background; } -} -@mixin hook-thumbnav-misc(){} -@mixin hook-inverse-thumbnav-item(){} -@mixin hook-inverse-thumbnav-item-hover(){} -@mixin hook-inverse-thumbnav-item-active(){} -@mixin hook-inverse-component-thumbnav(){ - - .uk-thumbnav > * > * { - @if(mixin-exists(hook-inverse-thumbnav-item)) {@include hook-inverse-thumbnav-item();} - } - - .uk-thumbnav > * > :hover, - .uk-thumbnav > * > :focus { - @if(mixin-exists(hook-inverse-thumbnav-item-hover)) {@include hook-inverse-thumbnav-item-hover();} - } - - .uk-thumbnav > .uk-active > * { - @if(mixin-exists(hook-inverse-thumbnav-item-active)) {@include hook-inverse-thumbnav-item-active();} - } - -} -@mixin hook-tile(){} -@mixin hook-tile-default(){} -@mixin hook-tile-muted(){} -@mixin hook-tile-primary(){} -@mixin hook-tile-secondary(){} -@mixin hook-tile-misc(){} -@mixin hook-tooltip(){} -@mixin hook-tooltip-misc(){} -@mixin hook-totop(){ transition: color 0.1s ease-in-out; } -@mixin hook-totop-hover(){} -@mixin hook-totop-active(){} -@mixin hook-totop-misc(){} -@mixin hook-inverse-totop(){} -@mixin hook-inverse-totop-hover(){} -@mixin hook-inverse-totop-active(){} -@mixin hook-transition-misc(){} -@mixin hook-panel-scrollable(){} -@mixin hook-box-shadow-bottom(){} -@mixin hook-dropcap(){ - // Prevent line wrap - margin-bottom: -2px; -} -@mixin hook-leader(){} -@mixin hook-logo(){} -@mixin hook-logo-hover(){} -@mixin hook-utility-misc(){} -@mixin hook-inverse-dropcap(){} -@mixin hook-inverse-leader(){} -@mixin hook-inverse-logo(){} -@mixin hook-inverse-logo-hover(){} -@mixin hook-visibility-misc(){} -@mixin hook-width-misc(){} \ No newline at end of file diff --git a/_sass/uikit/mixins.scss b/_sass/uikit/mixins.scss deleted file mode 100644 index 573b2f37f7..0000000000 --- a/_sass/uikit/mixins.scss +++ /dev/null @@ -1,1644 +0,0 @@ -@mixin hook-accordion(){} -@mixin hook-accordion-item(){} -@mixin hook-accordion-title(){} -@mixin hook-accordion-title-hover(){} -@mixin hook-accordion-content(){} -@mixin hook-accordion-misc(){} -@mixin hook-inverse-accordion-item(){} -@mixin hook-inverse-accordion-title(){} -@mixin hook-inverse-accordion-title-hover(){} -@mixin hook-inverse-component-accordion(){ - - .uk-accordion > :nth-child(n+2) { - @if(mixin-exists(hook-inverse-accordion-item)) {@include hook-inverse-accordion-item();} - } - - .uk-accordion-title { - color: $inverse-accordion-title-color; - @if(mixin-exists(hook-inverse-accordion-title)) {@include hook-inverse-accordion-title();} - } - - .uk-accordion-title:hover, - .uk-accordion-title:focus { - color: $inverse-accordion-title-hover-color; - @if(mixin-exists(hook-inverse-accordion-title-hover)) {@include hook-inverse-accordion-title-hover();} - } - -} -@mixin hook-align-misc(){} -@mixin hook-alert(){} -@mixin hook-alert-close(){} -@mixin hook-alert-close-hover(){} -@mixin hook-alert-primary(){} -@mixin hook-alert-success(){} -@mixin hook-alert-warning(){} -@mixin hook-alert-danger(){} -@mixin hook-alert-misc(){} -@mixin hook-article(){} -@mixin hook-article-adjacent(){} -@mixin hook-article-title(){} -@mixin hook-article-meta(){} -@mixin hook-article-misc(){} -@mixin hook-inverse-article-title(){} -@mixin hook-inverse-article-meta(){} -@mixin hook-inverse-component-article(){ - - .uk-article-title { - @if(mixin-exists(hook-inverse-article-title)) {@include hook-inverse-article-title();} - } - - .uk-article-meta { - color: $inverse-article-meta-color; - @if(mixin-exists(hook-inverse-article-meta)) {@include hook-inverse-article-meta();} - } - -} -@mixin hook-animation-misc(){} -@mixin hook-background-misc(){} -@mixin hook-badge(){} -@mixin hook-badge-hover(){} -@mixin hook-badge-misc(){} -@mixin hook-inverse-badge(){} -@mixin hook-inverse-badge-hover(){} -@mixin hook-inverse-component-badge(){ - - .uk-badge { - background-color: $inverse-badge-background; - color: $inverse-badge-color; - @if(mixin-exists(hook-inverse-badge)) {@include hook-inverse-badge();} - } - - .uk-badge:hover, - .uk-badge:focus { - color: $inverse-badge-hover-color; - @if(mixin-exists(hook-inverse-badge-hover)) {@include hook-inverse-badge-hover();} - } - -} -@mixin hook-base-body(){} -@mixin hook-base-link(){} -@mixin hook-base-link-hover(){} -@mixin hook-base-code(){} -@mixin hook-base-heading(){} -@mixin hook-base-h1(){} -@mixin hook-base-h2(){} -@mixin hook-base-h3(){} -@mixin hook-base-h4(){} -@mixin hook-base-h5(){} -@mixin hook-base-h6(){} -@mixin hook-base-hr(){} -@mixin hook-base-blockquote(){} -@mixin hook-base-blockquote-footer(){} -@mixin hook-base-pre(){} -@mixin hook-base-misc(){} -@mixin hook-inverse-base-link(){} -@mixin hook-inverse-base-link-hover(){} -@mixin hook-inverse-base-code(){} -@mixin hook-inverse-base-heading(){} -@mixin hook-inverse-base-h1(){} -@mixin hook-inverse-base-h2(){} -@mixin hook-inverse-base-h3(){} -@mixin hook-inverse-base-h4(){} -@mixin hook-inverse-base-h5(){} -@mixin hook-inverse-base-h6(){} -@mixin hook-inverse-base-blockquote(){} -@mixin hook-inverse-base-blockquote-footer(){} -@mixin hook-inverse-base-hr(){} -@mixin hook-inverse-component-base(){ - - color: $inverse-base-color; - - // Base - // ======================================================================== - - // - // Link - // - - a, - .uk-link { - color: $inverse-base-link-color; - @if(mixin-exists(hook-inverse-base-link)) {@include hook-inverse-base-link();} - } - - a:hover, - .uk-link:hover { - color: $inverse-base-link-hover-color; - @if(mixin-exists(hook-inverse-base-link-hover)) {@include hook-inverse-base-link-hover();} - } - - // - // Code - // - - :not(pre) > code, - :not(pre) > kbd, - :not(pre) > samp { - color: $inverse-base-code-color; - @if(mixin-exists(hook-inverse-base-code)) {@include hook-inverse-base-code();} - } - - // - // Emphasize - // - - em { color: $inverse-base-em-color; } - - // - // Headings - // - - h1, .uk-h1, - h2, .uk-h2, - h3, .uk-h3, - h4, .uk-h4, - h5, .uk-h5, - h6, .uk-h6 { - color: $inverse-base-heading-color; - @if(mixin-exists(hook-inverse-base-heading)) {@include hook-inverse-base-heading();} - } - - h1, .uk-h1 { - @if(mixin-exists(hook-inverse-base-h1)) {@include hook-inverse-base-h1();} - } - - h2, .uk-h2 { - @if(mixin-exists(hook-inverse-base-h2)) {@include hook-inverse-base-h2();} - } - - h3, .uk-h3 { - @if(mixin-exists(hook-inverse-base-h3)) {@include hook-inverse-base-h3();} - } - - h4, .uk-h4 { - @if(mixin-exists(hook-inverse-base-h4)) {@include hook-inverse-base-h4();} - } - - h5, .uk-h5 { - @if(mixin-exists(hook-inverse-base-h5)) {@include hook-inverse-base-h5();} - } - - h6, .uk-h6 { - @if(mixin-exists(hook-inverse-base-h6)) {@include hook-inverse-base-h6();} - } - - // - // Blockquotes - // - - blockquote { - @if(mixin-exists(hook-inverse-base-blockquote)) {@include hook-inverse-base-blockquote();} - } - - blockquote footer { - @if(mixin-exists(hook-inverse-base-blockquote-footer)) {@include hook-inverse-base-blockquote-footer();} - } - - // - // Horizontal rules - // - - hr, .uk-hr { - border-top-color: $inverse-base-hr-border; - @if(mixin-exists(hook-inverse-base-hr)) {@include hook-inverse-base-hr();} - } - -} -@mixin hook-breadcrumb(){} -@mixin hook-breadcrumb-item(){} -@mixin hook-breadcrumb-item-hover(){} -@mixin hook-breadcrumb-item-disabled(){} -@mixin hook-breadcrumb-item-active(){} -@mixin hook-breadcrumb-divider(){} -@mixin hook-breadcrumb-misc(){} -@mixin hook-inverse-breadcrumb-item(){} -@mixin hook-inverse-breadcrumb-item-hover(){} -@mixin hook-inverse-breadcrumb-item-disabled(){} -@mixin hook-inverse-breadcrumb-item-active(){} -@mixin hook-inverse-breadcrumb-divider(){} -@mixin hook-inverse-component-breadcrumb(){ - - .uk-breadcrumb > * > * { - color: $inverse-breadcrumb-item-color; - @if(mixin-exists(hook-inverse-breadcrumb-item)) {@include hook-inverse-breadcrumb-item();} - } - - .uk-breadcrumb > * > :hover, - .uk-breadcrumb > * > :focus { - color: $inverse-breadcrumb-item-hover-color; - @if(mixin-exists(hook-inverse-breadcrumb-item-hover)) {@include hook-inverse-breadcrumb-item-hover();} - } - - - .uk-breadcrumb > .uk-disabled > * { - @if(mixin-exists(hook-inverse-breadcrumb-item-disabled)) {@include hook-inverse-breadcrumb-item-disabled();} - } - - .uk-breadcrumb > :last-child > * { - color: $inverse-breadcrumb-item-active-color; - @if(mixin-exists(hook-inverse-breadcrumb-item-active)) {@include hook-inverse-breadcrumb-item-active();} - } - - // - // Divider - // - - .uk-breadcrumb > :nth-child(n+2):not(.uk-first-column)::before { - color: $inverse-breadcrumb-divider-color; - @if(mixin-exists(hook-inverse-breadcrumb-divider)) {@include hook-inverse-breadcrumb-divider();} - } - -} -@mixin hook-button(){} -@mixin hook-button-hover(){} -@mixin hook-button-focus(){} -@mixin hook-button-active(){} -@mixin hook-button-default(){} -@mixin hook-button-default-hover(){} -@mixin hook-button-default-active(){} -@mixin hook-button-primary(){} -@mixin hook-button-primary-hover(){} -@mixin hook-button-primary-active(){} -@mixin hook-button-secondary(){} -@mixin hook-button-secondary-hover(){} -@mixin hook-button-secondary-active(){} -@mixin hook-button-danger(){} -@mixin hook-button-danger-hover(){} -@mixin hook-button-danger-active(){} -@mixin hook-button-disabled(){} -@mixin hook-button-small(){} -@mixin hook-button-large(){} -@mixin hook-button-text(){} -@mixin hook-button-text-hover(){} -@mixin hook-button-text-disabled(){} -@mixin hook-button-link(){} -@mixin hook-button-misc(){} -@mixin hook-inverse-button-default(){} -@mixin hook-inverse-button-default-hover(){} -@mixin hook-inverse-button-default-active(){} -@mixin hook-inverse-button-primary(){} -@mixin hook-inverse-button-primary-hover(){} -@mixin hook-inverse-button-primary-active(){} -@mixin hook-inverse-button-secondary(){} -@mixin hook-inverse-button-secondary-hover(){} -@mixin hook-inverse-button-secondary-active(){} -@mixin hook-inverse-button-text(){} -@mixin hook-inverse-button-text-hover(){} -@mixin hook-inverse-button-text-disabled(){} -@mixin hook-inverse-button-link(){} -@mixin hook-inverse-component-button(){ - - // - // Default - // - - .uk-button-default { - background-color: $inverse-button-default-background; - color: $inverse-button-default-color; - @if(mixin-exists(hook-inverse-button-default)) {@include hook-inverse-button-default();} - } - - .uk-button-default:hover, - .uk-button-default:focus { - background-color: $inverse-button-default-hover-background; - color: $inverse-button-default-hover-color; - @if(mixin-exists(hook-inverse-button-default-hover)) {@include hook-inverse-button-default-hover();} - } - - .uk-button-default:active, - .uk-button-default.uk-active { - background-color: $inverse-button-default-active-background; - color: $inverse-button-default-active-color; - @if(mixin-exists(hook-inverse-button-default-active)) {@include hook-inverse-button-default-active();} - } - - // - // Primary - // - - .uk-button-primary { - background-color: $inverse-button-primary-background; - color: $inverse-button-primary-color; - @if(mixin-exists(hook-inverse-button-primary)) {@include hook-inverse-button-primary();} - } - - .uk-button-primary:hover, - .uk-button-primary:focus { - background-color: $inverse-button-primary-hover-background; - color: $inverse-button-primary-hover-color; - @if(mixin-exists(hook-inverse-button-primary-hover)) {@include hook-inverse-button-primary-hover();} - } - - .uk-button-primary:active, - .uk-button-primary.uk-active { - background-color: $inverse-button-primary-active-background; - color: $inverse-button-primary-active-color; - @if(mixin-exists(hook-inverse-button-primary-active)) {@include hook-inverse-button-primary-active();} - } - - // - // Secondary - // - - .uk-button-secondary { - background-color: $inverse-button-secondary-background; - color: $inverse-button-secondary-color; - @if(mixin-exists(hook-inverse-button-secondary)) {@include hook-inverse-button-secondary();} - } - - .uk-button-secondary:hover, - .uk-button-secondary:focus { - background-color: $inverse-button-secondary-hover-background; - color: $inverse-button-secondary-hover-color; - @if(mixin-exists(hook-inverse-button-secondary-hover)) {@include hook-inverse-button-secondary-hover();} - } - - .uk-button-secondary:active, - .uk-button-secondary.uk-active { - background-color: $inverse-button-secondary-active-background; - color: $inverse-button-secondary-active-color; - @if(mixin-exists(hook-inverse-button-secondary-active)) {@include hook-inverse-button-secondary-active();} - } - - // - // Text - // - - .uk-button-text { - color: $inverse-button-text-color; - @if(mixin-exists(hook-inverse-button-text)) {@include hook-inverse-button-text();} - } - - .uk-button-text:hover, - .uk-button-text:focus { - color: $inverse-button-text-hover-color; - @if(mixin-exists(hook-inverse-button-text-hover)) {@include hook-inverse-button-text-hover();} - } - - .uk-button-text:disabled { - color: $inverse-button-text-disabled-color; - @if(mixin-exists(hook-inverse-button-text-disabled)) {@include hook-inverse-button-text-disabled();} - } - - // - // Link - // - - .uk-button-link { - color: $inverse-button-link-color; - @if(mixin-exists(hook-inverse-button-link)) {@include hook-inverse-button-link();} - } - - .uk-button-link:hover, - .uk-button-link:focus { color: $inverse-button-link-hover-color; } - - -} -@mixin hook-card(){} -@mixin hook-card-body(){} -@mixin hook-card-header(){} -@mixin hook-card-footer(){} -@mixin hook-card-media(){} -@mixin hook-card-media-top(){} -@mixin hook-card-media-bottom(){} -@mixin hook-card-media-left(){} -@mixin hook-card-media-right(){} -@mixin hook-card-title(){} -@mixin hook-card-badge(){} -@mixin hook-card-hover(){} -@mixin hook-card-default(){} -@mixin hook-card-default-title(){} -@mixin hook-card-default-hover(){} -@mixin hook-card-default-header(){} -@mixin hook-card-default-footer(){} -@mixin hook-card-primary(){} -@mixin hook-card-primary-title(){} -@mixin hook-card-primary-hover(){} -@mixin hook-card-secondary(){} -@mixin hook-card-secondary-title(){} -@mixin hook-card-secondary-hover(){} -@mixin hook-card-misc(){} -@mixin hook-close(){} -@mixin hook-close-hover(){} -@mixin hook-close-misc(){} -@mixin hook-inverse-close(){} -@mixin hook-inverse-close-hover(){} -@mixin hook-inverse-component-close(){ - - .uk-close { - color: $inverse-close-color; - @if(mixin-exists(hook-inverse-close)) {@include hook-inverse-close();} - } - - .uk-close:hover, - .uk-close:focus { - color: $inverse-close-hover-color; - @if(mixin-exists(hook-inverse-close-hover)) {@include hook-inverse-close-hover();} - } - -} -@mixin hook-column-misc(){} -@mixin hook-inverse-component-column(){ - - .uk-column-divider { column-rule-color: $inverse-column-divider-rule-color; } - -} -@mixin hook-comment(){} -@mixin hook-comment-body(){} -@mixin hook-comment-header(){} -@mixin hook-comment-title(){} -@mixin hook-comment-meta(){} -@mixin hook-comment-avatar(){} -@mixin hook-comment-list-adjacent(){} -@mixin hook-comment-list-sub(){} -@mixin hook-comment-list-sub-adjacent(){} -@mixin hook-comment-primary(){} -@mixin hook-comment-misc(){} -@mixin hook-container-misc(){} -@mixin hook-countdown(){} -@mixin hook-countdown-item(){} -@mixin hook-countdown-number(){} -@mixin hook-countdown-separator(){} -@mixin hook-countdown-label(){} -@mixin hook-countdown-misc(){} -@mixin hook-inverse-countdown-item(){} -@mixin hook-inverse-countdown-number(){} -@mixin hook-inverse-countdown-separator(){} -@mixin hook-inverse-countdown-label(){} -@mixin hook-inverse-component-countdown(){ - - .uk-countdown-number, - .uk-countdown-separator { - @if(mixin-exists(hook-inverse-countdown-item)) {@include hook-inverse-countdown-item();} - } - - .uk-countdown-number { - @if(mixin-exists(hook-inverse-countdown-number)) {@include hook-inverse-countdown-number();} - } - - .uk-countdown-separator { - @if(mixin-exists(hook-inverse-countdown-separator)) {@include hook-inverse-countdown-separator();} - } - - .uk-countdown-label { - @if(mixin-exists(hook-inverse-countdown-label)) {@include hook-inverse-countdown-label();} - } - -} -@mixin hook-cover-misc(){} -@mixin hook-description-list-term(){} -@mixin hook-description-list-description(){} -@mixin hook-description-list-divider-term(){} -@mixin hook-description-list-misc(){} -@mixin svg-fill($src, $color-default, $color-new, $property: background-image){ - - $escape-color-default: escape($color-default) !default; - $escape-color-new: escape("#{$color-new}") !default; - - $data-uri: data-uri('image/svg+xml;charset=UTF-8', "#{$src}") !default; - $replace-src: replace("#{$data-uri}", "#{$escape-color-default}", "#{$escape-color-new}", "g") !default; - - #{$property}: unquote($replace-src); -} -@mixin hook-divider-icon(){} -@mixin hook-divider-icon-line(){} -@mixin hook-divider-icon-line-left(){} -@mixin hook-divider-icon-line-right(){} -@mixin hook-divider-small(){} -@mixin hook-divider-misc(){} -@mixin hook-inverse-divider-icon(){} -@mixin hook-inverse-divider-icon-line(){} -@mixin hook-inverse-divider-small(){} -@mixin hook-inverse-component-divider(){ - - .uk-divider-icon { - @include svg-fill($internal-divider-icon-image, "#000", $inverse-divider-icon-color); - @if(mixin-exists(hook-inverse-divider-icon)) {@include hook-inverse-divider-icon();} - } - - .uk-divider-icon::before, - .uk-divider-icon::after { - border-bottom-color: $inverse-divider-icon-line-border; - @if(mixin-exists(hook-inverse-divider-icon-line)) {@include hook-inverse-divider-icon-line();} - } - - .uk-divider-small::after { - border-top-color: $inverse-divider-small-border; - @if(mixin-exists(hook-inverse-divider-small)) {@include hook-inverse-divider-small();} - } - -} -@mixin hook-dotnav(){} -@mixin hook-dotnav-item(){} -@mixin hook-dotnav-item-hover(){} -@mixin hook-dotnav-item-onclick(){} -@mixin hook-dotnav-item-active(){} -@mixin hook-dotnav-misc(){} -@mixin hook-inverse-dotnav-item(){} -@mixin hook-inverse-dotnav-item-hover(){} -@mixin hook-inverse-dotnav-item-onclick(){} -@mixin hook-inverse-dotnav-item-active(){} -@mixin hook-inverse-component-dotnav(){ - - .uk-dotnav > * > * { - background-color: $inverse-dotnav-item-background; - @if(mixin-exists(hook-inverse-dotnav-item)) {@include hook-inverse-dotnav-item();} - } - - .uk-dotnav > * > :hover, - .uk-dotnav > * > :focus { - background-color: $inverse-dotnav-item-hover-background; - @if(mixin-exists(hook-inverse-dotnav-item-hover)) {@include hook-inverse-dotnav-item-hover();} - } - - .uk-dotnav > * > :active { - background-color: $inverse-dotnav-item-onclick-background; - @if(mixin-exists(hook-inverse-dotnav-item-onclick)) {@include hook-inverse-dotnav-item-onclick();} - } - - .uk-dotnav > .uk-active > * { - background-color: $inverse-dotnav-item-active-background; - @if(mixin-exists(hook-inverse-dotnav-item-active)) {@include hook-inverse-dotnav-item-active();} - } - -} -@mixin hook-drop-misc(){} -@mixin hook-dropdown(){} -@mixin hook-dropdown-nav(){} -@mixin hook-dropdown-nav-item(){} -@mixin hook-dropdown-nav-item-hover(){} -@mixin hook-dropdown-nav-header(){} -@mixin hook-dropdown-nav-divider(){} -@mixin hook-dropdown-misc(){} -@mixin hook-flex-misc(){} -@mixin hook-form-range(){} -@mixin hook-form-range-thumb(){} -@mixin hook-form-range-track(){} -@mixin hook-form-range-track-focus(){} -@mixin hook-form-range-misc(){} -@mixin hook-form(){} -@mixin hook-form-single-line(){} -@mixin hook-form-multi-line(){} -@mixin hook-form-focus(){} -@mixin hook-form-disabled(){} -@mixin hook-form-danger(){} -@mixin hook-form-success(){} -@mixin hook-form-blank(){} -@mixin hook-form-blank-focus(){} -@mixin hook-form-radio(){} -@mixin hook-form-radio-focus(){} -@mixin hook-form-radio-checked(){} -@mixin hook-form-radio-checked-focus(){} -@mixin hook-form-radio-disabled(){} -@mixin hook-form-legend(){} -@mixin hook-form-label(){} -@mixin hook-form-stacked-label(){} -@mixin hook-form-horizontal-label(){} -@mixin hook-form-misc(){} -@mixin hook-inverse-form(){} -@mixin hook-inverse-form-focus(){} -@mixin hook-inverse-form-radio(){} -@mixin hook-inverse-form-radio-focus(){} -@mixin hook-inverse-form-radio-checked(){} -@mixin hook-inverse-form-radio-checked-focus(){} -@mixin hook-inverse-form-label(){} -@mixin hook-inverse-component-form(){ - - .uk-input, - .uk-select, - .uk-textarea { - background-color: $inverse-form-background; - color: $inverse-form-color; - background-clip: padding-box; - @if(mixin-exists(hook-inverse-form)) {@include hook-inverse-form();} - - &:focus { - background-color: $inverse-form-focus-background; - color: $inverse-form-focus-color; - @if(mixin-exists(hook-inverse-form-focus)) {@include hook-inverse-form-focus();} - } - } - - // - // Placeholder - // - - .uk-input:-ms-input-placeholder { color: $inverse-form-placeholder-color !important; } - .uk-input::placeholder { color: $inverse-form-placeholder-color; } - - .uk-textarea:-ms-input-placeholder { color: $inverse-form-placeholder-color !important; } - .uk-textarea::placeholder { color: $inverse-form-placeholder-color; } - - // - // Radio and checkbox - // - - .uk-select:not([multiple]):not([size]) { @include svg-fill($internal-form-select-image, "#000", $inverse-form-select-icon-color); } - - // - // Radio and checkbox - // - - .uk-radio, - .uk-checkbox { - background-color: $inverse-form-radio-background; - @if(mixin-exists(hook-inverse-form-radio)) {@include hook-inverse-form-radio();} - } - - // Focus - .uk-radio:focus, - .uk-checkbox:focus { - @if(mixin-exists(hook-inverse-form-radio-focus)) {@include hook-inverse-form-radio-focus();} - } - - // Checked - .uk-radio:checked, - .uk-checkbox:checked, - .uk-checkbox:indeterminate { - background-color: $inverse-form-radio-checked-background; - @if(mixin-exists(hook-inverse-form-radio-checked)) {@include hook-inverse-form-radio-checked();} - } - - // Focus - .uk-radio:checked:focus, - .uk-checkbox:checked:focus, - .uk-checkbox:indeterminate:focus { - background-color: $inverse-form-radio-checked-focus-background; - @if(mixin-exists(hook-inverse-form-radio-checked-focus)) {@include hook-inverse-form-radio-checked-focus();} - } - - // Icon - .uk-radio:checked { @include svg-fill($internal-form-radio-image, "#000", $inverse-form-radio-checked-icon-color); } - .uk-checkbox:checked { @include svg-fill($internal-form-checkbox-image, "#000", $inverse-form-radio-checked-icon-color); } - .uk-checkbox:indeterminate { @include svg-fill($internal-form-checkbox-indeterminate-image, "#000", $inverse-form-radio-checked-icon-color); } - - // Label - .uk-form-label { - @if(mixin-exists(hook-inverse-form-label)) {@include hook-inverse-form-label();} - } - -} -@mixin hook-grid-misc(){} -@mixin hook-inverse-component-grid(){ - - .uk-grid-divider > :not(.uk-first-column)::before { border-left-color: $inverse-grid-divider-border; } - .uk-grid-divider.uk-grid-stack > .uk-grid-margin::before { border-top-color: $inverse-grid-divider-border; } - -} -@mixin hook-heading-primary(){} -@mixin hook-heading-hero(){} -@mixin hook-heading-divider(){} -@mixin hook-heading-bullet(){} -@mixin hook-heading-line(){} -@mixin hook-heading-misc(){} -@mixin hook-inverse-heading-primary(){} -@mixin hook-inverse-heading-hero(){} -@mixin hook-inverse-heading-divider(){} -@mixin hook-inverse-heading-bullet(){} -@mixin hook-inverse-heading-line(){} -@mixin hook-inverse-component-heading(){ - - .uk-heading-primary { - @if(mixin-exists(hook-inverse-heading-primary)) {@include hook-inverse-heading-primary();} - } - - .uk-heading-hero { - @if(mixin-exists(hook-inverse-heading-hero)) {@include hook-inverse-heading-hero();} - } - - .uk-heading-divider { - border-bottom-color: $inverse-heading-divider-border; - @if(mixin-exists(hook-inverse-heading-divider)) {@include hook-inverse-heading-divider();} - } - - .uk-heading-bullet::before { - border-left-color: $inverse-heading-bullet-border; - @if(mixin-exists(hook-inverse-heading-bullet)) {@include hook-inverse-heading-bullet();} - } - - .uk-heading-line > ::before, - .uk-heading-line > ::after { - border-bottom-color: $inverse-heading-line-border; - @if(mixin-exists(hook-inverse-heading-line)) {@include hook-inverse-heading-line();} - } - -} -@mixin hook-icon-link(){} -@mixin hook-icon-link-hover(){} -@mixin hook-icon-link-active(){} -@mixin hook-icon-button(){} -@mixin hook-icon-button-hover(){} -@mixin hook-icon-button-active(){} -@mixin hook-icon-misc(){} -@mixin hook-inverse-icon-link(){} -@mixin hook-inverse-icon-link-hover(){} -@mixin hook-inverse-icon-link-active(){} -@mixin hook-inverse-icon-button(){} -@mixin hook-inverse-icon-button-hover(){} -@mixin hook-inverse-icon-button-active(){} -@mixin hook-inverse-component-icon(){ - - // - // Link - // - - .uk-icon-link { - color: $inverse-icon-link-color; - @if(mixin-exists(hook-inverse-icon-link)) {@include hook-inverse-icon-link();} - } - - .uk-icon-link:hover, - .uk-icon-link:focus { - color: $inverse-icon-link-hover-color; - @if(mixin-exists(hook-inverse-icon-link-hover)) {@include hook-inverse-icon-link-hover();} - } - - .uk-icon-link:active, - .uk-active > .uk-icon-link { - color: $inverse-icon-link-active-color; - @if(mixin-exists(hook-inverse-icon-link-active)) {@include hook-inverse-icon-link-active();} - } - - // - // Button - // - - .uk-icon-button { - background-color: $inverse-icon-button-background; - color: $inverse-icon-button-color; - @if(mixin-exists(hook-inverse-icon-button)) {@include hook-inverse-icon-button();} - } - - .uk-icon-button:hover, - .uk-icon-button:focus { - background-color: $inverse-icon-button-hover-background; - color: $inverse-icon-button-hover-color; - @if(mixin-exists(hook-inverse-icon-button-hover)) {@include hook-inverse-icon-button-hover();} - } - - .uk-icon-button:active { - background-color: $inverse-icon-button-active-background; - color: $inverse-icon-button-active-color; - @if(mixin-exists(hook-inverse-icon-button-active)) {@include hook-inverse-icon-button-active();} - } - -} -@mixin hook-iconnav(){} -@mixin hook-iconnav-item(){} -@mixin hook-iconnav-item-hover(){} -@mixin hook-iconnav-item-active(){} -@mixin hook-iconnav-misc(){} -@mixin hook-inverse-iconnav-item(){} -@mixin hook-inverse-iconnav-item-hover(){} -@mixin hook-inverse-iconnav-item-active(){} -@mixin hook-inverse-component-iconnav(){ - - .uk-iconnav > * > a { - color: $inverse-iconnav-item-color; - @if(mixin-exists(hook-inverse-iconnav-item)) {@include hook-inverse-iconnav-item();} - } - - .uk-iconnav > * > a:hover, - .uk-iconnav > * > a:focus { - color: $inverse-iconnav-item-hover-color; - @if(mixin-exists(hook-inverse-iconnav-item-hover)) {@include hook-inverse-iconnav-item-hover();} - } - - .uk-iconnav > .uk-active > a { - color: $inverse-iconnav-item-active-color; - @if(mixin-exists(hook-inverse-iconnav-item-active)) {@include hook-inverse-iconnav-item-active();} - } - -} -@mixin hook-inverse-component-link(){ - - a.uk-link-muted, - .uk-link-muted a { - color: $inverse-link-muted-color; - @if(mixin-exists(hook-inverse-link-muted)) {@include hook-inverse-link-muted();} - } - - a.uk-link-muted:hover, - .uk-link-muted a:hover { - color: $inverse-link-muted-hover-color; - @if(mixin-exists(hook-inverse-link-muted-hover)) {@include hook-inverse-link-muted-hover();} - } - - a.uk-link-text:hover, - .uk-link-text a:hover { - color: $inverse-link-text-hover-color; - @if(mixin-exists(hook-inverse-link-text-hover)) {@include hook-inverse-link-text-hover();} - } - - a.uk-link-heading:hover, - .uk-link-heading a:hover { - color: $inverse-link-heading-hover-color; - @if(mixin-exists(hook-inverse-link-heading-hover)) {@include hook-inverse-link-heading-hover();} - } - -} -@mixin hook-inverse-component-list(){ - - .uk-list-divider > li:nth-child(n+2) { - border-top-color: $inverse-list-divider-border; - @if(mixin-exists(hook-inverse-list-divider)) {@include hook-inverse-list-divider();} - } - - .uk-list-striped > li { - @if(mixin-exists(hook-inverse-list-striped)) {@include hook-inverse-list-striped();} - } - - .uk-list-striped > li:nth-of-type(odd) { background-color: $inverse-list-striped-background; } - - .uk-list-bullet > li::before { - @include svg-fill($internal-list-bullet-image, "#000", $inverse-list-bullet-icon-color); - @if(mixin-exists(hook-inverse-list-bullet)) {@include hook-inverse-list-bullet();} - } - -} -@mixin hook-inverse-component-totop(){ - - .uk-totop { - color: $inverse-totop-color; - @if(mixin-exists(hook-inverse-totop)) {@include hook-inverse-totop();} - } - - .uk-totop:hover, - .uk-totop:focus { - color: $inverse-totop-hover-color; - @if(mixin-exists(hook-inverse-totop-hover)) {@include hook-inverse-totop-hover();} - } - - .uk-totop:active { - color: $inverse-totop-active-color; - @if(mixin-exists(hook-inverse-totop-active)) {@include hook-inverse-totop-active();} - } - -} -@mixin hook-inverse-component-label(){ - - .uk-label { - background-color: $inverse-label-background; - color: $inverse-label-color; - @if(mixin-exists(hook-inverse-label)) {@include hook-inverse-label();} - } - -} -@mixin hook-inverse-component-search(){ - - // - // Input - // - - .uk-search-input { color: $inverse-search-color; } - - .uk-search-input:-ms-input-placeholder { color: $inverse-search-placeholder-color !important; } - .uk-search-input::placeholder { color: $inverse-search-placeholder-color; } - - - // - // Icon - // - - .uk-search .uk-search-icon { color: $inverse-search-icon-color; } - - .uk-search .uk-search-icon:hover { color: $inverse-search-icon-color; } - - // - // Style modifier - // - - .uk-search-default .uk-search-input { - background-color: $inverse-search-default-background; - @if(mixin-exists(hook-inverse-search-default-input)) {@include hook-inverse-search-default-input();} - } - .uk-search-default .uk-search-input:focus { - background-color: $inverse-search-default-background; - @if(mixin-exists(hook-inverse-search-default-input-focus)) {@include hook-inverse-search-default-input-focus();} - } - - .uk-search-navbar .uk-search-input { - background-color: $inverse-search-navbar-background; - @if(mixin-exists(hook-inverse-search-navbar-input)) {@include hook-inverse-search-navbar-input();} - } - - .uk-search-large .uk-search-input { - background-color: $inverse-search-large-background; - @if(mixin-exists(hook-inverse-search-large-input)) {@include hook-inverse-search-large-input();} - } - - // - // Toggle - // - - .uk-search-toggle { - color: $inverse-search-toggle-color; - @if(mixin-exists(hook-inverse-search-toggle)) {@include hook-inverse-search-toggle();} - } - - .uk-search-toggle:hover, - .uk-search-toggle:focus { - color: $inverse-search-toggle-hover-color; - @if(mixin-exists(hook-inverse-search-toggle-hover)) {@include hook-inverse-search-toggle-hover();} - } - -} -@mixin hook-inverse-component-nav(){ - - // - // Parent icon modifier - // - - .uk-nav-parent-icon > .uk-parent > a::after { - @include svg-fill($internal-nav-parent-close-image, "#000", $inverse-nav-parent-icon-color); - @if(mixin-exists(hook-inverse-nav-parent-icon)) {@include hook-inverse-nav-parent-icon();} - } - - .uk-nav-parent-icon > .uk-parent.uk-open > a::after { @include svg-fill($internal-nav-parent-open-image, "#000", $inverse-nav-parent-icon-color); } - - // - // Default - // - - .uk-nav-default > li > a { - color: $inverse-nav-default-item-color; - @if(mixin-exists(hook-inverse-nav-default-item)) {@include hook-inverse-nav-default-item();} - } - - .uk-nav-default > li > a:hover, - .uk-nav-default > li > a:focus { - color: $inverse-nav-default-item-hover-color; - @if(mixin-exists(hook-inverse-nav-default-item-hover)) {@include hook-inverse-nav-default-item-hover();} - } - - .uk-nav-default > li.uk-active > a { - color: $inverse-nav-default-item-active-color; - @if(mixin-exists(hook-inverse-nav-default-item-active)) {@include hook-inverse-nav-default-item-active();} - } - - .uk-nav-default .uk-nav-header { - color: $inverse-nav-default-header-color; - @if(mixin-exists(hook-inverse-nav-default-header)) {@include hook-inverse-nav-default-header();} - } - - .uk-nav-default .uk-nav-divider { - border-top-color: $inverse-nav-default-divider-border; - @if(mixin-exists(hook-inverse-nav-default-divider)) {@include hook-inverse-nav-default-divider();} - } - - .uk-nav-default .uk-nav-sub a { color: $inverse-nav-default-sublist-item-color; } - - .uk-nav-default .uk-nav-sub a:hover, - .uk-nav-default .uk-nav-sub a:focus { color: $inverse-nav-default-sublist-item-hover-color; } - - // - // Primary - // - - .uk-nav-primary > li > a { - color: $inverse-nav-primary-item-color; - @if(mixin-exists(hook-inverse-nav-primary-item)) {@include hook-inverse-nav-primary-item();} - } - - .uk-nav-primary > li > a:hover, - .uk-nav-primary > li > a:focus { - color: $inverse-nav-primary-item-hover-color; - @if(mixin-exists(hook-inverse-nav-primary-item-hover)) {@include hook-inverse-nav-primary-item-hover();} - } - - .uk-nav-primary > li.uk-active > a { - color: $inverse-nav-primary-item-active-color; - @if(mixin-exists(hook-inverse-nav-primary-item-active)) {@include hook-inverse-nav-primary-item-active();} - } - - .uk-nav-primary .uk-nav-header { - color: $inverse-nav-primary-header-color; - @if(mixin-exists(hook-inverse-nav-primary-header)) {@include hook-inverse-nav-primary-header();} - } - - .uk-nav-primary .uk-nav-divider { - border-top-color: $inverse-nav-primary-divider-border; - @if(mixin-exists(hook-inverse-nav-primary-divider)) {@include hook-inverse-nav-primary-divider();} - } - - .uk-nav-primary .uk-nav-sub a { color: $inverse-nav-primary-sublist-item-color; } - - .uk-nav-primary .uk-nav-sub a:hover, - .uk-nav-primary .uk-nav-sub a:focus { color: $inverse-nav-primary-sublist-item-hover-color; } - -} -@mixin hook-inverse-component-navbar(){ - - .uk-navbar-nav > li > a { - color: $inverse-navbar-nav-item-color; - @if(mixin-exists(hook-inverse-navbar-nav-item)) {@include hook-inverse-navbar-nav-item();} - } - - .uk-navbar-nav > li:hover > a, - .uk-navbar-nav > li > a:focus, - .uk-navbar-nav > li > a.uk-open { - color: $inverse-navbar-nav-item-hover-color; - @if(mixin-exists(hook-inverse-navbar-nav-item-hover)) {@include hook-inverse-navbar-nav-item-hover();} - } - - .uk-navbar-nav > li > a:active { - color: $inverse-navbar-nav-item-onclick-color; - @if(mixin-exists(hook-inverse-navbar-nav-item-onclick)) {@include hook-inverse-navbar-nav-item-onclick();} - } - - .uk-navbar-nav > li.uk-active > a { - color: $inverse-navbar-nav-item-active-color; - @if(mixin-exists(hook-inverse-navbar-nav-item-active)) {@include hook-inverse-navbar-nav-item-active();} - } - - .uk-navbar-item { - color: $inverse-navbar-item-color; - @if(mixin-exists(hook-inverse-navbar-item)) {@include hook-inverse-navbar-item();} - } - - .uk-navbar-toggle { - color: $inverse-navbar-toggle-color; - @if(mixin-exists(hook-inverse-navbar-toggle)) {@include hook-inverse-navbar-toggle();} - } - - .uk-navbar-toggle:hover, - .uk-navbar-toggle:focus, - .uk-navbar-toggle.uk-open { - color: $inverse-navbar-toggle-hover-color; - @if(mixin-exists(hook-inverse-navbar-toggle-hover)) {@include hook-inverse-navbar-toggle-hover();} - } - -} -@mixin hook-inverse-component-subnav(){ - - .uk-subnav > * > :first-child { - color: $inverse-subnav-item-color; - @if(mixin-exists(hook-inverse-subnav-item)) {@include hook-inverse-subnav-item();} - } - - .uk-subnav > * > a:hover, - .uk-subnav > * > a:focus { - color: $inverse-subnav-item-hover-color; - @if(mixin-exists(hook-inverse-subnav-item-hover)) {@include hook-inverse-subnav-item-hover();} - } - - .uk-subnav > .uk-active > a { - color: $inverse-subnav-item-active-color; - @if(mixin-exists(hook-inverse-subnav-item-active)) {@include hook-inverse-subnav-item-active();} - } - - // - // Divider - // - - .uk-subnav-divider > :nth-child(n+2):not(.uk-first-column)::before { - border-left-color: $inverse-subnav-divider-border; - @if(mixin-exists(hook-inverse-subnav-divider)) {@include hook-inverse-subnav-divider();} - } - - // - // Pill - // - - .uk-subnav-pill > * > :first-child { - background-color: $inverse-subnav-pill-item-background; - color: $inverse-subnav-pill-item-color; - @if(mixin-exists(hook-inverse-subnav-pill-item)) {@include hook-inverse-subnav-pill-item();} - } - - .uk-subnav-pill > * > a:hover, - .uk-subnav-pill > * > a:focus { - background-color: $inverse-subnav-pill-item-hover-background; - color: $inverse-subnav-pill-item-hover-color; - @if(mixin-exists(hook-inverse-subnav-pill-item-hover)) {@include hook-inverse-subnav-pill-item-hover();} - } - - .uk-subnav-pill > * > a:active { - background-color: $inverse-subnav-pill-item-onclick-background; - color: $inverse-subnav-pill-item-onclick-color; - @if(mixin-exists(hook-inverse-subnav-pill-item-onclick)) {@include hook-inverse-subnav-pill-item-onclick();} - } - - .uk-subnav-pill > .uk-active > a { - background-color: $inverse-subnav-pill-item-active-background; - color: $inverse-subnav-pill-item-active-color; - @if(mixin-exists(hook-inverse-subnav-pill-item-active)) {@include hook-inverse-subnav-pill-item-active();} - } - - // - // Disabled - // - - .uk-subnav > .uk-disabled > a { - color: $inverse-subnav-item-disabled-color; - @if(mixin-exists(hook-inverse-subnav-item-disabled)) {@include hook-inverse-subnav-item-disabled();} - } - -} -@mixin hook-inverse-component-pagination(){ - - .uk-pagination > * > * { - color: $inverse-pagination-item-color; - @if(mixin-exists(hook-inverse-pagination-item)) {@include hook-inverse-pagination-item();} - } - - .uk-pagination > * > :hover, - .uk-pagination > * > :focus { - color: $inverse-pagination-item-hover-color; - @if(mixin-exists(hook-inverse-pagination-item-hover)) {@include hook-inverse-pagination-item-hover();} - } - - .uk-pagination > .uk-active > * { - color: $inverse-pagination-item-active-color; - @if(mixin-exists(hook-inverse-pagination-item-active)) {@include hook-inverse-pagination-item-active();} - } - - .uk-pagination > .uk-disabled > * { - color: $inverse-pagination-item-disabled-color; - @if(mixin-exists(hook-inverse-pagination-item-disabled)) {@include hook-inverse-pagination-item-disabled();} - } - -} -@mixin hook-inverse-component-tab(){ - - .uk-tab { - @if(mixin-exists(hook-inverse-tab)) {@include hook-inverse-tab();} - } - - .uk-tab > * > a { - color: $inverse-tab-item-color; - @if(mixin-exists(hook-inverse-tab-item)) {@include hook-inverse-tab-item();} - } - - .uk-tab > * > a:hover, - .uk-tab > * > a:focus{ - color: $inverse-tab-item-hover-color; - @if(mixin-exists(hook-inverse-tab-item-hover)) {@include hook-inverse-tab-item-hover();} - } - - .uk-tab > .uk-active > a { - color: $inverse-tab-item-active-color; - @if(mixin-exists(hook-inverse-tab-item-active)) {@include hook-inverse-tab-item-active();} - } - - .uk-tab > .uk-disabled > a { - color: $inverse-tab-item-disabled-color; - @if(mixin-exists(hook-inverse-tab-item-disabled)) {@include hook-inverse-tab-item-disabled();} - } - -} -@mixin hook-inverse-component-slidenav(){ - - .uk-slidenav { - color: $inverse-slidenav-color; - @if(mixin-exists(hook-inverse-slidenav)) {@include hook-inverse-slidenav();} - } - - .uk-slidenav:hover, - .uk-slidenav:focus { - color: $inverse-slidenav-hover-color; - @if(mixin-exists(hook-inverse-slidenav-hover)) {@include hook-inverse-slidenav-hover();} - } - - .uk-slidenav:active { - color: $inverse-slidenav-active-color; - @if(mixin-exists(hook-inverse-slidenav-active)) {@include hook-inverse-slidenav-active();} - } - -} -@mixin hook-inverse-component-text(){ - - .uk-text-lead { - color: $inverse-text-lead-color; - @if(mixin-exists(hook-inverse-text-lead)) {@include hook-inverse-text-lead();} - } - - .uk-text-meta { - color: $inverse-text-meta-color; - @if(mixin-exists(hook-inverse-text-meta)) {@include hook-inverse-text-meta();} - } - - .uk-text-muted { color: $inverse-text-muted-color !important; } - .uk-text-primary { color: $inverse-text-primary-color !important; } - -} -@mixin hook-inverse-component-utility(){ - - .uk-dropcap::first-letter, - .uk-dropcap p:first-of-type::first-letter { - @if(mixin-exists(hook-inverse-dropcap)) {@include hook-inverse-dropcap();} - } - - .uk-leader-fill { - @if(mixin-exists(hook-inverse-leader)) {@include hook-inverse-leader();} - } - - .uk-logo { - color: $inverse-logo-color; - @if(mixin-exists(hook-inverse-logo)) {@include hook-inverse-logo();} - } - - .uk-logo:hover, - .uk-logo:focus { - color: $inverse-logo-hover-color; - @if(mixin-exists(hook-inverse-logo-hover)) {@include hook-inverse-logo-hover();} - } - - .uk-logo > :not(.uk-logo-inverse):not(:only-of-type) { display: none; } - .uk-logo-inverse { display: inline; } - -} -@mixin hook-inverse(){ - @include hook-inverse-component-base(); - @include hook-inverse-component-link(); - @include hook-inverse-component-heading(); - @include hook-inverse-component-divider(); - @include hook-inverse-component-list(); - @include hook-inverse-component-icon(); - @include hook-inverse-component-form(); - @include hook-inverse-component-button(); - @include hook-inverse-component-grid(); - @include hook-inverse-component-close(); - @include hook-inverse-component-totop(); - @include hook-inverse-component-badge(); - @include hook-inverse-component-label(); - @include hook-inverse-component-article(); - @include hook-inverse-component-search(); - @include hook-inverse-component-nav(); - @include hook-inverse-component-navbar(); - @include hook-inverse-component-subnav(); - @include hook-inverse-component-breadcrumb(); - @include hook-inverse-component-pagination(); - @include hook-inverse-component-tab(); - @include hook-inverse-component-slidenav(); - @include hook-inverse-component-dotnav(); - @include hook-inverse-component-accordion(); - @include hook-inverse-component-iconnav(); - @include hook-inverse-component-text(); - @include hook-inverse-component-column(); - @include hook-inverse-component-utility(); -} -@mixin hook-label(){} -@mixin hook-label-success(){} -@mixin hook-label-warning(){} -@mixin hook-label-danger(){} -@mixin hook-label-misc(){} -@mixin hook-inverse-label(){} -@mixin hook-lightbox(){} -@mixin hook-lightbox-item(){} -@mixin hook-lightbox-toolbar(){} -@mixin hook-lightbox-toolbar-icon(){} -@mixin hook-lightbox-toolbar-icon-hover(){} -@mixin hook-lightbox-button(){} -@mixin hook-lightbox-button-hover(){} -@mixin hook-lightbox-misc(){} -@mixin hook-link-muted(){} -@mixin hook-link-muted-hover(){} -@mixin hook-link-text(){} -@mixin hook-link-text-hover(){} -@mixin hook-link-heading(){} -@mixin hook-link-heading-hover(){} -@mixin hook-link-reset(){} -@mixin hook-link-misc(){} -@mixin hook-inverse-link-muted(){} -@mixin hook-inverse-link-muted-hover(){} -@mixin hook-inverse-link-text-hover(){} -@mixin hook-inverse-link-heading-hover(){} -@mixin hook-list-divider(){} -@mixin hook-list-striped(){} -@mixin hook-list-bullet(){} -@mixin hook-list-misc(){} -@mixin hook-inverse-list-divider(){} -@mixin hook-inverse-list-striped(){} -@mixin hook-inverse-list-bullet(){} -@mixin hook-margin-misc(){} -@mixin hook-marker(){} -@mixin hook-marker-hover(){} -@mixin hook-marker-misc(){} -@mixin hook-inverse-marker(){} -@mixin hook-inverse-marker-hover(){} -@mixin hook-inverse-component-marker(){ - - .uk-marker { - background: $inverse-marker-background; - color: $inverse-marker-color; - @if(mixin-exists(hook-inverse-marker)) {@include hook-inverse-marker();} - } - - .uk-marker:hover, - .uk-marker:focus { - color: $inverse-marker-hover-color; - @if(mixin-exists(hook-inverse-marker-hover)) {@include hook-inverse-marker-hover();} - } - -} -@mixin hook-modal(){} -@mixin hook-modal-dialog(){} -@mixin hook-modal-full(){} -@mixin hook-modal-body(){} -@mixin hook-modal-header(){} -@mixin hook-modal-footer(){} -@mixin hook-modal-title(){} -@mixin hook-modal-close(){} -@mixin hook-modal-close-hover(){} -@mixin hook-modal-close-default(){} -@mixin hook-modal-close-default-hover(){} -@mixin hook-modal-close-outside(){} -@mixin hook-modal-close-outside-hover(){} -@mixin hook-modal-close-full(){} -@mixin hook-modal-close-full-hover(){} -@mixin hook-modal-misc(){} -@mixin hook-nav-sub(){} -@mixin hook-nav-parent-icon(){} -@mixin hook-nav-header(){} -@mixin hook-nav-divider(){} -@mixin hook-nav-default(){} -@mixin hook-nav-default-item(){} -@mixin hook-nav-default-item-hover(){} -@mixin hook-nav-default-item-active(){} -@mixin hook-nav-default-header(){} -@mixin hook-nav-default-divider(){} -@mixin hook-nav-primary(){} -@mixin hook-nav-primary-item(){} -@mixin hook-nav-primary-item-hover(){} -@mixin hook-nav-primary-item-active(){} -@mixin hook-nav-primary-header(){} -@mixin hook-nav-primary-divider(){} -@mixin hook-nav-misc(){} -@mixin hook-inverse-nav-parent-icon(){} -@mixin hook-inverse-nav-default-item(){} -@mixin hook-inverse-nav-default-item-hover(){} -@mixin hook-inverse-nav-default-item-active(){} -@mixin hook-inverse-nav-default-header(){} -@mixin hook-inverse-nav-default-divider(){} -@mixin hook-inverse-nav-primary-item(){} -@mixin hook-inverse-nav-primary-item-hover(){} -@mixin hook-inverse-nav-primary-item-active(){} -@mixin hook-inverse-nav-primary-header(){} -@mixin hook-inverse-nav-primary-divider(){} -@mixin hook-navbar(){} -@mixin hook-navbar-container(){} -@mixin hook-navbar-nav-item(){} -@mixin hook-navbar-nav-item-hover(){} -@mixin hook-navbar-nav-item-onclick(){} -@mixin hook-navbar-nav-item-active(){} -@mixin hook-navbar-item(){} -@mixin hook-navbar-toggle(){} -@mixin hook-navbar-toggle-hover(){} -@mixin hook-navbar-toggle-icon(){} -@mixin hook-navbar-toggle-icon-hover(){} -@mixin hook-navbar-subtitle(){} -@mixin hook-navbar-transparent(){} -@mixin hook-navbar-sticky(){} -@mixin hook-navbar-dropdown(){} -@mixin hook-navbar-dropdown-dropbar(){} -@mixin hook-navbar-dropdown-nav(){} -@mixin hook-navbar-dropdown-nav-item(){} -@mixin hook-navbar-dropdown-nav-item-hover(){} -@mixin hook-navbar-dropdown-nav-item-active(){} -@mixin hook-navbar-dropdown-nav-header(){} -@mixin hook-navbar-dropdown-nav-divider(){} -@mixin hook-navbar-dropbar(){} -@mixin hook-navbar-dropbar-slide(){} -@mixin hook-navbar-misc(){} -@mixin hook-inverse-navbar-nav-item(){} -@mixin hook-inverse-navbar-nav-item-hover(){} -@mixin hook-inverse-navbar-nav-item-onclick(){} -@mixin hook-inverse-navbar-nav-item-active(){} -@mixin hook-inverse-navbar-item(){} -@mixin hook-inverse-navbar-toggle(){} -@mixin hook-inverse-navbar-toggle-hover(){} -@mixin hook-notification(){} -@mixin hook-notification-message(){} -@mixin hook-notification-close(){} -@mixin hook-notification-message-primary(){} -@mixin hook-notification-message-success(){} -@mixin hook-notification-message-warning(){} -@mixin hook-notification-message-danger(){} -@mixin hook-notification-misc(){} -@mixin hook-offcanvas-bar(){} -@mixin hook-offcanvas-close(){} -@mixin hook-offcanvas-overlay(){} -@mixin hook-offcanvas-misc(){} -@mixin hook-overlay(){} -@mixin hook-overlay-icon(){} -@mixin hook-overlay-default(){} -@mixin hook-overlay-primary(){} -@mixin hook-overlay-misc(){} -@mixin hook-padding-misc(){} -@mixin hook-pagination(){} -@mixin hook-pagination-item(){} -@mixin hook-pagination-item-hover(){} -@mixin hook-pagination-item-active(){} -@mixin hook-pagination-item-disabled(){} -@mixin hook-pagination-misc(){} -@mixin hook-inverse-pagination-item(){} -@mixin hook-inverse-pagination-item-hover(){} -@mixin hook-inverse-pagination-item-active(){} -@mixin hook-inverse-pagination-item-disabled(){} -@mixin hook-placeholder(){} -@mixin hook-placeholder-misc(){} -@mixin hook-position-misc(){} -@mixin hook-print(){} -@mixin hook-progress(){} -@mixin hook-progress-bar(){} -@mixin hook-progress-misc(){} -@mixin hook-search-input(){} -@mixin hook-search-default-input(){} -@mixin hook-search-default-input-focus(){} -@mixin hook-search-navbar-input(){} -@mixin hook-search-large-input(){} -@mixin hook-search-toggle(){} -@mixin hook-search-toggle-hover(){} -@mixin hook-search-misc(){} -@mixin hook-inverse-search-default-input(){} -@mixin hook-inverse-search-default-input-focus(){} -@mixin hook-inverse-search-navbar-input(){} -@mixin hook-inverse-search-large-input(){} -@mixin hook-inverse-search-toggle(){} -@mixin hook-inverse-search-toggle-hover(){} -@mixin hook-section(){} -@mixin hook-section-default(){} -@mixin hook-section-muted(){} -@mixin hook-section-primary(){} -@mixin hook-section-secondary(){} -@mixin hook-section-overlap(){} -@mixin hook-section-misc(){} -@mixin hook-slidenav(){} -@mixin hook-slidenav-hover(){} -@mixin hook-slidenav-active(){} -@mixin hook-slidenav-previous(){} -@mixin hook-slidenav-next(){} -@mixin hook-slidenav-large(){} -@mixin hook-slidenav-container(){} -@mixin hook-slidenav-misc(){} -@mixin hook-inverse-slidenav(){} -@mixin hook-inverse-slidenav-hover(){} -@mixin hook-inverse-slidenav-active(){} -@mixin hook-slider(){} -@mixin hook-slider-misc(){} -@mixin hook-slideshow(){} -@mixin hook-slideshow-misc(){} -@mixin hook-sortable(){} -@mixin hook-sortable-drag(){} -@mixin hook-sortable-placeholder(){} -@mixin hook-sortable-empty(){} -@mixin hook-sortable-misc(){} -@mixin hook-spinner(){} -@mixin hook-spinner-misc(){} -@mixin hook-sticky-misc(){} -@mixin hook-subnav(){} -@mixin hook-subnav-item(){} -@mixin hook-subnav-item-hover(){} -@mixin hook-subnav-item-active(){} -@mixin hook-subnav-divider(){} -@mixin hook-subnav-pill-item(){} -@mixin hook-subnav-pill-item-hover(){} -@mixin hook-subnav-pill-item-onclick(){} -@mixin hook-subnav-pill-item-active(){} -@mixin hook-subnav-item-disabled(){} -@mixin hook-subnav-misc(){} -@mixin hook-inverse-subnav-item(){} -@mixin hook-inverse-subnav-item-hover(){} -@mixin hook-inverse-subnav-item-active(){} -@mixin hook-inverse-subnav-divider(){} -@mixin hook-inverse-subnav-pill-item(){} -@mixin hook-inverse-subnav-pill-item-hover(){} -@mixin hook-inverse-subnav-pill-item-onclick(){} -@mixin hook-inverse-subnav-pill-item-active(){} -@mixin hook-inverse-subnav-item-disabled(){} -@mixin hook-switcher-misc(){} -@mixin hook-tab(){} -@mixin hook-tab-item(){} -@mixin hook-tab-item-hover(){} -@mixin hook-tab-item-active(){} -@mixin hook-tab-item-disabled(){} -@mixin hook-tab-bottom(){} -@mixin hook-tab-bottom-item(){} -@mixin hook-tab-left(){} -@mixin hook-tab-right(){} -@mixin hook-tab-left-item(){} -@mixin hook-tab-right-item(){} -@mixin hook-tab-misc(){} -@mixin hook-inverse-tab(){} -@mixin hook-inverse-tab-item(){} -@mixin hook-inverse-tab-item-hover(){} -@mixin hook-inverse-tab-item-active(){} -@mixin hook-inverse-tab-item-disabled(){} -@mixin hook-table(){} -@mixin hook-table-header-cell(){} -@mixin hook-table-cell(){} -@mixin hook-table-footer(){} -@mixin hook-table-caption(){} -@mixin hook-table-row-active(){} -@mixin hook-table-divider(){} -@mixin hook-table-striped(){} -@mixin hook-table-hover(){} -@mixin hook-table-small(){} -@mixin hook-table-large(){} -@mixin hook-table-misc(){} -@mixin hook-inverse-table-header-cell(){} -@mixin hook-inverse-table-caption(){} -@mixin hook-inverse-table-row-active(){} -@mixin hook-inverse-table-divider(){} -@mixin hook-inverse-table-striped(){} -@mixin hook-inverse-table-hover(){} -@mixin hook-inverse-component-table(){ - - .uk-table th { - color: $inverse-table-header-cell-color; - @if(mixin-exists(hook-inverse-table-header-cell)) {@include hook-inverse-table-header-cell();} - } - - .uk-table caption { - color: $inverse-table-caption-color; - @if(mixin-exists(hook-inverse-table-caption)) {@include hook-inverse-table-caption();} - } - - .uk-table > tr.uk-active, - .uk-table tbody tr.uk-active { - background: $inverse-table-row-active-background; - @if(mixin-exists(hook-inverse-table-row-active)) {@include hook-inverse-table-row-active();} - } - - .uk-table-divider > tr:not(:first-child), - .uk-table-divider > :not(:first-child) > tr, - .uk-table-divider > :first-child > tr:not(:first-child) { - border-top-color: $inverse-table-divider-border; - @if(mixin-exists(hook-inverse-table-divider)) {@include hook-inverse-table-divider();} - } - - .uk-table-striped > tr:nth-of-type(odd), - .uk-table-striped tbody tr:nth-of-type(odd) { - background: $inverse-table-striped-row-background; - @if(mixin-exists(hook-inverse-table-striped)) {@include hook-inverse-table-striped();} - } - - .uk-table-hover > tr:hover, - .uk-table-hover tbody tr:hover { - background: $inverse-table-hover-row-background; - @if(mixin-exists(hook-inverse-table-hover)) {@include hook-inverse-table-hover();} - } - -} -@mixin hook-text-lead(){} -@mixin hook-text-meta(){} -@mixin hook-text-small(){} -@mixin hook-text-large(){} -@mixin hook-text-background(){} -@mixin hook-text-misc(){} -@mixin hook-inverse-text-lead(){} -@mixin hook-inverse-text-meta(){} -@mixin hook-thumbnav(){} -@mixin hook-thumbnav-item(){} -@mixin hook-thumbnav-item-hover(){} -@mixin hook-thumbnav-item-active(){} -@mixin hook-thumbnav-misc(){} -@mixin hook-inverse-thumbnav-item(){} -@mixin hook-inverse-thumbnav-item-hover(){} -@mixin hook-inverse-thumbnav-item-active(){} -@mixin hook-inverse-component-thumbnav(){ - - .uk-thumbnav > * > * { - @if(mixin-exists(hook-inverse-thumbnav-item)) {@include hook-inverse-thumbnav-item();} - } - - .uk-thumbnav > * > :hover, - .uk-thumbnav > * > :focus { - @if(mixin-exists(hook-inverse-thumbnav-item-hover)) {@include hook-inverse-thumbnav-item-hover();} - } - - .uk-thumbnav > .uk-active > * { - @if(mixin-exists(hook-inverse-thumbnav-item-active)) {@include hook-inverse-thumbnav-item-active();} - } - -} -@mixin hook-tile(){} -@mixin hook-tile-default(){} -@mixin hook-tile-muted(){} -@mixin hook-tile-primary(){} -@mixin hook-tile-secondary(){} -@mixin hook-tile-misc(){} -@mixin hook-tooltip(){} -@mixin hook-tooltip-misc(){} -@mixin hook-totop(){} -@mixin hook-totop-hover(){} -@mixin hook-totop-active(){} -@mixin hook-totop-misc(){} -@mixin hook-inverse-totop(){} -@mixin hook-inverse-totop-hover(){} -@mixin hook-inverse-totop-active(){} -@mixin hook-transition-misc(){} -@mixin hook-panel-scrollable(){} -@mixin hook-box-shadow-bottom(){} -@mixin hook-dropcap(){} -@mixin hook-leader(){} -@mixin hook-logo(){} -@mixin hook-logo-hover(){} -@mixin hook-utility-misc(){} -@mixin hook-inverse-dropcap(){} -@mixin hook-inverse-leader(){} -@mixin hook-inverse-logo(){} -@mixin hook-inverse-logo-hover(){} -@mixin hook-visibility-misc(){} -@mixin hook-width-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/_import.scss b/_sass/uikit/theme/_import.scss deleted file mode 100644 index b08e50b6a3..0000000000 --- a/_sass/uikit/theme/_import.scss +++ /dev/null @@ -1,78 +0,0 @@ -// Base -@import "variables.scss"; -@import "base.scss"; - -// Elements -@import "link.scss"; -@import "heading.scss"; -@import "divider.scss"; -@import "list.scss"; -@import "description-list.scss"; -@import "table.scss"; -@import "icon.scss"; -@import "form-range.scss"; -@import "form.scss"; -@import "button.scss"; - -// Layout -@import "section.scss"; -@import "container.scss"; -@import "grid.scss"; -@import "tile.scss"; -@import "card.scss"; - -// Common -@import "close.scss"; -@import "spinner.scss"; -@import "marker.scss"; -@import "totop.scss"; -@import "alert.scss"; -@import "badge.scss"; -@import "label.scss"; -@import "overlay.scss"; -@import "article.scss"; -@import "comment.scss"; -@import "search.scss"; - -// Navs -@import "nav.scss"; -@import "navbar.scss"; -@import "subnav.scss"; -@import "breadcrumb.scss"; -@import "pagination.scss"; -@import "tab.scss"; -@import "slidenav.scss"; -@import "dotnav.scss"; -@import "thumbnav.scss"; - -// JavaScript -@import "accordion.scss"; -@import "drop.scss"; -@import "dropdown.scss"; -@import "modal.scss"; -@import "lightbox.scss"; -@import "sticky.scss"; -@import "offcanvas.scss"; - -// Additional -@import "iconnav.scss"; -@import "notification.scss"; -@import "tooltip.scss"; -@import "placeholder.scss"; -@import "progress.scss"; -@import "sortable.scss"; -@import "countdown.scss"; - -// Utilities -@import "animation.scss"; -@import "width.scss"; -@import "text.scss"; -@import "column.scss"; -@import "background.scss"; -@import "align.scss"; -@import "utility.scss"; -@import "margin.scss"; -@import "padding.scss"; -@import "position.scss"; -@import "transition.scss"; -@import "inverse.scss"; diff --git a/_sass/uikit/theme/accordion.scss b/_sass/uikit/theme/accordion.scss deleted file mode 100644 index 3e44609f4f..0000000000 --- a/_sass/uikit/theme/accordion.scss +++ /dev/null @@ -1,58 +0,0 @@ -// -// Component: Accordion -// -// ======================================================================== - - -// Variables -// ======================================================================== - -// -// New -// - -$accordion-icon-color: $global-color !default; -$internal-accordion-open-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2213%22%20height%3D%2213%22%20viewBox%3D%220%200%2013%2013%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22#000%22%20width%3D%2213%22%20height%3D%221%22%20x%3D%220%22%20y%3D%226%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$internal-accordion-close-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2213%22%20height%3D%2213%22%20viewBox%3D%220%200%2013%2013%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22#000%22%20width%3D%2213%22%20height%3D%221%22%20x%3D%220%22%20y%3D%226%22%20%2F%3E%0A%20%20%20%20%3Crect%20fill%3D%22#000%22%20width%3D%221%22%20height%3D%2213%22%20x%3D%226%22%20y%3D%220%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; - - -// Component -// ======================================================================== - -// @mixin hook-accordion(){} - - -// Item -// ======================================================================== - -// @mixin hook-accordion-item(){} - - -// Title -// ======================================================================== - - - -// @mixin hook-accordion-title-hover(){} - - -// Content -// ======================================================================== - -// @mixin hook-accordion-content(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-accordion-misc(){} - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-accordion-item(){} - -// @mixin hook-inverse-accordion-title(){} -// @mixin hook-inverse-accordion-title-hover(){} - - diff --git a/_sass/uikit/theme/alert.scss b/_sass/uikit/theme/alert.scss deleted file mode 100644 index c4baa7ca90..0000000000 --- a/_sass/uikit/theme/alert.scss +++ /dev/null @@ -1,46 +0,0 @@ -// -// Component: Alert -// -// ======================================================================== - - -// Variables -// ======================================================================== - -// -// New -// - -$alert-close-opacity: 0.4 !default; -$alert-close-hover-opacity: 0.8 !default; - - -// Component -// ======================================================================== - -// @mixin hook-alert(){} - - -// Close -// ======================================================================== - - - - - - -// Style modifiers -// ======================================================================== - -// @mixin hook-alert-primary(){} - -// @mixin hook-alert-success(){} - -// @mixin hook-alert-warning(){} - -// @mixin hook-alert-danger(){} - - -// Miscellaneous -// ======================================================================== - diff --git a/_sass/uikit/theme/align.scss b/_sass/uikit/theme/align.scss deleted file mode 100644 index 290abd4115..0000000000 --- a/_sass/uikit/theme/align.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Align -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-align-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/animation.scss b/_sass/uikit/theme/animation.scss deleted file mode 100644 index 03ebbc6eae..0000000000 --- a/_sass/uikit/theme/animation.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Animation -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-animation-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/article.scss b/_sass/uikit/theme/article.scss deleted file mode 100644 index a698e3ed18..0000000000 --- a/_sass/uikit/theme/article.scss +++ /dev/null @@ -1,51 +0,0 @@ -// -// Component: Article -// -// ======================================================================== - - -// Variables -// ======================================================================== - -// -// New -// - -$article-meta-link-color: $article-meta-color !default; -$article-meta-link-hover-color: $global-color !default; - - -// Component -// ======================================================================== - -// @mixin hook-article(){} - - -// Adjacent sibling -// ======================================================================== - -// @mixin hook-article-adjacent(){} - - -// Title -// ======================================================================== - -// @mixin hook-article-title(){} - - -// Meta -// ======================================================================== - - - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-article-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-article-meta(){} \ No newline at end of file diff --git a/_sass/uikit/theme/background.scss b/_sass/uikit/theme/background.scss deleted file mode 100644 index 29e062e9d2..0000000000 --- a/_sass/uikit/theme/background.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Background -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-background-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/badge.scss b/_sass/uikit/theme/badge.scss deleted file mode 100644 index 22ae937122..0000000000 --- a/_sass/uikit/theme/badge.scss +++ /dev/null @@ -1,29 +0,0 @@ -// -// Component: Badge -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - -// @mixin hook-badge(){} - -// @mixin hook-badge-hover(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-badge-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-badge(){} -// @mixin hook-inverse-badge-hover(){} \ No newline at end of file diff --git a/_sass/uikit/theme/base.scss b/_sass/uikit/theme/base.scss deleted file mode 100644 index 2c1c33569e..0000000000 --- a/_sass/uikit/theme/base.scss +++ /dev/null @@ -1,116 +0,0 @@ -// -// Component: Base -// -// ======================================================================== - - -// Variables -// ======================================================================== - -// -// New -// - -$base-code-padding-horizontal: 6px !default; -$base-code-padding-vertical: 2px !default; -$base-code-background: $global-muted-background !default; - -$base-blockquote-color: $global-emphasis-color !default; - -$base-blockquote-footer-color: $global-color !default; - -$base-pre-padding: 10px !default; -$base-pre-background: $global-background !default; -$base-pre-border-width: $global-border-width !default; -$base-pre-border: $global-border !default; -$base-pre-border-radius: 3px !default; - - -// Body -// ======================================================================== - -// @mixin hook-base-body(){} - - -// Links -// ======================================================================== - -// @mixin hook-base-link(){} - -// @mixin hook-base-link-hover(){} - - -// Text-level semantics -// ======================================================================== - - - - -// Headings -// ======================================================================== - -// @mixin hook-base-heading(){} - -// @mixin hook-base-h1(){} - -// @mixin hook-base-h2(){} - -// @mixin hook-base-h3(){} - -// @mixin hook-base-h4(){} - -// @mixin hook-base-h5(){} - -// @mixin hook-base-h6(){} - - -// Horizontal rules -// ======================================================================== - -// @mixin hook-base-hr(){} - - -// Blockquotes -// ======================================================================== - - - - - - -// Preformatted text -// ======================================================================== - - - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-base-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-base-blockquote-color: $inverse-global-emphasis-color !default; -$inverse-base-blockquote-footer-color: $inverse-global-color !default; - -// @mixin hook-inverse-base-link(){} -// @mixin hook-inverse-base-link-hover(){} - - - -// @mixin hook-inverse-base-heading(){} - -// @mixin hook-inverse-base-h1(){} -// @mixin hook-inverse-base-h2(){} -// @mixin hook-inverse-base-h3(){} -// @mixin hook-inverse-base-h4(){} -// @mixin hook-inverse-base-h5(){} -// @mixin hook-inverse-base-h6(){} - - - - -// @mixin hook-inverse-base-hr(){} \ No newline at end of file diff --git a/_sass/uikit/theme/breadcrumb.scss b/_sass/uikit/theme/breadcrumb.scss deleted file mode 100644 index 40c04e5d9e..0000000000 --- a/_sass/uikit/theme/breadcrumb.scss +++ /dev/null @@ -1,45 +0,0 @@ -// -// Component: Breadcrumb -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - -// @mixin hook-breadcrumb(){} - - -// Items -// ======================================================================== - -// @mixin hook-breadcrumb-item(){} - -// @mixin hook-breadcrumb-item-hover(){} - -// @mixin hook-breadcrumb-item-disabled(){} - -// @mixin hook-breadcrumb-item-active(){} - -// @mixin hook-breadcrumb-divider(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-breadcrumb-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-breadcrumb-item(){} -// @mixin hook-inverse-breadcrumb-item-hover(){} -// @mixin hook-inverse-breadcrumb-item-disabled(){} -// @mixin hook-inverse-breadcrumb-item-active(){} - -// @mixin hook-inverse-breadcrumb-divider(){} \ No newline at end of file diff --git a/_sass/uikit/theme/button.scss b/_sass/uikit/theme/button.scss deleted file mode 100644 index 6acb5094e1..0000000000 --- a/_sass/uikit/theme/button.scss +++ /dev/null @@ -1,161 +0,0 @@ -// -// Component: Button -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$button-line-height: $global-control-height - ($button-border-width * 2) !default; -$button-small-line-height: $global-control-small-height - ($button-border-width * 2) !default; -$button-large-line-height: $global-control-large-height - ($button-border-width * 2) !default; - -$button-font-size: $global-small-font-size !default; -$button-large-font-size: $global-small-font-size !default; - -$button-default-background: transparent !default; -$button-default-hover-background: transparent !default; -$button-default-active-background: transparent !default; - -$button-disabled-background: transparent !default; - -$button-text-color: $global-emphasis-color !default; -$button-text-hover-color: $global-emphasis-color !default; - -// -// New -// - -$button-text-transform: uppercase !default; - -$button-border-width: $global-border-width !default; - -$button-default-border: $global-border !default; -$button-default-hover-border: darken($global-border, 20%) !default; -$button-default-active-border: darken($global-border, 30%) !default; - -$button-disabled-border: $global-border !default; - -$button-text-border-width: $global-border-width !default; -$button-text-border: $button-text-hover-color !default; - - -// Component -// ======================================================================== - - - -// @mixin hook-button-hover(){} - -// @mixin hook-button-focus(){} - -// @mixin hook-button-active(){} - - -// Style modifiers -// ======================================================================== - - - - - - - -// -// Primary -// - - - -// @mixin hook-button-primary-hover(){} - -// @mixin hook-button-primary-active(){} - -// -// Secondary -// - - - -// @mixin hook-button-secondary-hover(){} - -// @mixin hook-button-secondary-active(){} - -// -// Danger -// - - - -// @mixin hook-button-danger-hover(){} - -// @mixin hook-button-danger-active(){} - - -// Disabled -// ======================================================================== - - - - -// Size modifiers -// ======================================================================== - -// @mixin hook-button-small(){} - -// @mixin hook-button-large(){} - - -// Text modifier -// ======================================================================== - - - - - - - - -// Link modifier -// ======================================================================== - -// @mixin hook-button-link(){} - - -// Miscellaneous -// ======================================================================== - - - - -// Inverse -// ======================================================================== - -$inverse-button-default-background: transparent !default; -$inverse-button-default-color: $inverse-global-emphasis-color !default; -$inverse-button-default-hover-background: transparent !default; -$inverse-button-default-hover-color: $inverse-global-emphasis-color !default; -$inverse-button-default-active-background: transparent !default; -$inverse-button-default-active-color: $inverse-global-emphasis-color !default; - -$inverse-button-text-color: $inverse-global-emphasis-color !default; -$inverse-button-text-hover-color: $inverse-global-emphasis-color !default; - - - - - -// @mixin hook-inverse-button-primary(){} -// @mixin hook-inverse-button-primary-hover(){} -// @mixin hook-inverse-button-primary-active(){} - -// @mixin hook-inverse-button-secondary(){} -// @mixin hook-inverse-button-secondary-hover(){} -// @mixin hook-inverse-button-secondary-active(){} - - -// @mixin hook-inverse-button-text-hover(){} -// @mixin hook-inverse-button-text-disabled(){} - -// @mixin hook-inverse-button-link(){} \ No newline at end of file diff --git a/_sass/uikit/theme/card.scss b/_sass/uikit/theme/card.scss deleted file mode 100644 index 9e8fe2bc85..0000000000 --- a/_sass/uikit/theme/card.scss +++ /dev/null @@ -1,125 +0,0 @@ -// -// Component: Card -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$card-hover-background: $global-background !default; - -$card-default-background: $global-background !default; -$card-default-hover-background: $card-default-background !default; - -$card-primary-hover-background: $card-primary-background !default; - -$card-secondary-hover-background: $card-secondary-background !default; - -// -// New -// - -$card-hover-box-shadow: $global-large-box-shadow !default; - -$card-default-box-shadow: $global-medium-box-shadow !default; -$card-default-hover-box-shadow: $global-large-box-shadow !default; - -$card-default-header-border-width: $global-border-width !default; -$card-default-header-border: $global-border !default; - -$card-default-footer-border-width: $global-border-width !default; -$card-default-footer-border: $global-border !default; - -$card-primary-box-shadow: $global-medium-box-shadow !default; -$card-primary-hover-box-shadow: $global-large-box-shadow !default; - -$card-secondary-box-shadow: $global-medium-box-shadow !default; -$card-secondary-hover-box-shadow: $global-large-box-shadow !default; - - -// Component -// ======================================================================== - - - - -// Sections -// ======================================================================== - -// @mixin hook-card-body(){} - -// @mixin hook-card-header(){} - -// @mixin hook-card-footer(){} - - -// Media -// ======================================================================== - -// @mixin hook-card-media(){} - -// @mixin hook-card-media-top(){} - -// @mixin hook-card-media-bottom(){} - -// @mixin hook-card-media-left(){} - -// @mixin hook-card-media-right(){} - - -// Title -// ======================================================================== - -// @mixin hook-card-title(){} - - -// Badge -// ======================================================================== - -// @mixin hook-card-badge(){} - - -// Hover modifier -// ======================================================================== - - - - -// Style modifiers -// ======================================================================== - - - -// @mixin hook-card-default-title(){} - - - - - - - -// -// Primary -// - - - -// @mixin hook-card-primary-title(){} - - - -// -// Secondary -// - - - -// @mixin hook-card-secondary-title(){} - - - - -// Miscellaneous -// ======================================================================== - diff --git a/_sass/uikit/theme/close.scss b/_sass/uikit/theme/close.scss deleted file mode 100644 index f0762942f8..0000000000 --- a/_sass/uikit/theme/close.scss +++ /dev/null @@ -1,29 +0,0 @@ -// -// Component: Close -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - - - -// @mixin hook-close-hover(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-close-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-close(){} -// @mixin hook-inverse-close-hover(){} \ No newline at end of file diff --git a/_sass/uikit/theme/column.scss b/_sass/uikit/theme/column.scss deleted file mode 100644 index 80be850572..0000000000 --- a/_sass/uikit/theme/column.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Column -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-column-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/comment.scss b/_sass/uikit/theme/comment.scss deleted file mode 100644 index a486c59142..0000000000 --- a/_sass/uikit/theme/comment.scss +++ /dev/null @@ -1,69 +0,0 @@ -// -// Component: Comment -// -// ======================================================================== - - -// Variables -// ======================================================================== - -// -// New -// - -$comment-primary-padding: $global-gutter !default; -$comment-primary-background: $global-muted-background !default; - - -// Component -// ======================================================================== - -// @mixin hook-comment(){} - - -// Sections -// ======================================================================== - -// @mixin hook-comment-body(){} - -// @mixin hook-comment-header(){} - - -// Title -// ======================================================================== - -// @mixin hook-comment-title(){} - - -// Meta -// ======================================================================== - -// @mixin hook-comment-meta(){} - - -// Avatar -// ======================================================================== - -// @mixin hook-comment-avatar(){} - - -// List -// ======================================================================== - -// @mixin hook-comment-list-adjacent(){} - -// @mixin hook-comment-list-sub(){} - -// @mixin hook-comment-list-sub-adjacent(){} - - -// Style modifier -// ======================================================================== - - - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-comment-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/container.scss b/_sass/uikit/theme/container.scss deleted file mode 100644 index ba77ded72e..0000000000 --- a/_sass/uikit/theme/container.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Container -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-container-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/countdown.scss b/_sass/uikit/theme/countdown.scss deleted file mode 100644 index 01f1761ca3..0000000000 --- a/_sass/uikit/theme/countdown.scss +++ /dev/null @@ -1,53 +0,0 @@ -// -// Component: Countdown -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - -// @mixin hook-countdown(){} - - -// Item -// ======================================================================== - -// @mixin hook-countdown-item(){} - - -// Number -// ======================================================================== - -// @mixin hook-countdown-number(){} - - -// Separator -// ======================================================================== - -// @mixin hook-countdown-separator(){} - - -// Label -// ======================================================================== - -// @mixin hook-countdown-label(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-countdown-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-countdown-item(){} -// @mixin hook-inverse-countdown-number(){} -// @mixin hook-inverse-countdown-separator(){} -// @mixin hook-inverse-countdown-label(){} diff --git a/_sass/uikit/theme/description-list.scss b/_sass/uikit/theme/description-list.scss deleted file mode 100644 index 8f836d6321..0000000000 --- a/_sass/uikit/theme/description-list.scss +++ /dev/null @@ -1,32 +0,0 @@ -// -// Component: Description list -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$description-list-term-font-size: $global-small-font-size !default; -$description-list-term-font-weight: normal !default; -$description-list-term-text-transform: uppercase !default; - - -// Component -// ======================================================================== - - - -// @mixin hook-description-list-description(){} - - -// Style modifier -// ======================================================================== - -// @mixin hook-description-list-divider-term(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-description-list-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/divider.scss b/_sass/uikit/theme/divider.scss deleted file mode 100644 index 59e2c9cc62..0000000000 --- a/_sass/uikit/theme/divider.scss +++ /dev/null @@ -1,41 +0,0 @@ -// -// Component: Divider -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Icon -// ======================================================================== - -// @mixin hook-divider-icon(){} - -// @mixin hook-divider-icon-line(){} - -// @mixin hook-divider-icon-line-left(){} - -// @mixin hook-divider-icon-line-right(){} - - -// Small -// ======================================================================== - -// @mixin hook-divider-small(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-divider-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-divider-icon(){} -// @mixin hook-inverse-divider-icon-line(){} - -// @mixin hook-inverse-divider-small(){} \ No newline at end of file diff --git a/_sass/uikit/theme/dotnav.scss b/_sass/uikit/theme/dotnav.scss deleted file mode 100644 index 1bc835970f..0000000000 --- a/_sass/uikit/theme/dotnav.scss +++ /dev/null @@ -1,52 +0,0 @@ -// -// Component: Dotnav -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$dotnav-item-background: transparent !default; - -// -// New -// - -$dotnav-item-border-width: 1px !default; - -$dotnav-item-border: rgba($global-color, 0.4) !default; -$dotnav-item-hover-border: transparent !default; -$dotnav-item-onclick-border: transparent !default; -$dotnav-item-active-border: transparent !default; - - -// Component -// ======================================================================== - -// @mixin hook-dotnav(){} - - - - - - - - - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-dotnav-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-dotnav-item-background: transparent !default; - -// @mixin hook-inverse-dotnav(){} - - - diff --git a/_sass/uikit/theme/drop.scss b/_sass/uikit/theme/drop.scss deleted file mode 100644 index 6940984871..0000000000 --- a/_sass/uikit/theme/drop.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Drop -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-drop-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/dropdown.scss b/_sass/uikit/theme/dropdown.scss deleted file mode 100644 index c5aa02ef44..0000000000 --- a/_sass/uikit/theme/dropdown.scss +++ /dev/null @@ -1,45 +0,0 @@ -// -// Component: Dropdown -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$dropdown-padding: 25px !default; -$dropdown-background: $global-background !default; - -// -// New -// - -$dropdown-nav-font-size: $global-small-font-size !default; - -$dropdown-box-shadow: 0 5px 12px rgba(0,0,0,0.15) !default; - - -// Component -// ======================================================================== - - - - -// Nav -// ======================================================================== - - - -// @mixin hook-dropdown-nav-item(){} - -// @mixin hook-dropdown-nav-item-hover(){} - -// @mixin hook-dropdown-nav-header(){} - -// @mixin hook-dropdown-nav-divider(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-dropdown-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/form-range.scss b/_sass/uikit/theme/form-range.scss deleted file mode 100644 index ca424f30e3..0000000000 --- a/_sass/uikit/theme/form-range.scss +++ /dev/null @@ -1,45 +0,0 @@ -// -// Component: Form Range -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$form-range-thumb-background: $global-background !default; - -// -// New -// - -$form-range-thumb-border-width: $global-border-width !default; -$form-range-thumb-border: darken($global-border, 10%) !default; - -$form-range-track-border-radius: 500px !default; - - -// Component -// ======================================================================== - -// @mixin hook-form-range(){} - - -// Thumb -// ======================================================================== - - - - -// Track -// ======================================================================== - - - -// @mixin hook-form-range-track-focus(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-form-range-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/form.scss b/_sass/uikit/theme/form.scss deleted file mode 100644 index ef80695839..0000000000 --- a/_sass/uikit/theme/form.scss +++ /dev/null @@ -1,131 +0,0 @@ -// -// Component: Form -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$form-line-height: $form-height - (2* $form-border-width) !default; - -$form-background: $global-background !default; -$form-focus-background: $global-background !default; - -$form-small-line-height: $form-small-height - (2* $form-border-width) !default; -$form-large-line-height: $form-large-height - (2* $form-border-width) !default; - -$form-radio-background: transparent !default; - -$form-stacked-margin-bottom: 5px !default; - -// -// New -// - -$form-border-width: $global-border-width !default; -$form-border: $global-border !default; - -$form-focus-border: $global-primary-background !default; - -$form-disabled-border: $global-border !default; - -$form-danger-border: $global-danger-background !default; -$form-success-border: $global-success-background !default; - -$form-blank-focus-border: $global-border !default; -$form-blank-focus-border-style: dashed !default; - -$form-radio-border-width: $global-border-width !default; -$form-radio-border: darken($global-border, 10%) !default; - -$form-radio-focus-border: $global-primary-background !default; - -$form-radio-checked-border: transparent !default; - -$form-radio-disabled-border: $global-border !default; - -$form-label-color: $global-emphasis-color !default; -$form-label-font-size: $global-small-font-size !default; - - -// Component -// ======================================================================== - - - -// @mixin hook-form-single-line(){} - -// @mixin hook-form-multi-line(){} - - - - - - -// Style modifiers -// ======================================================================== - - - - - - - - - - -// Radio and checkbox -// ======================================================================== - - - - - - - -// @mixin hook-form-radio-checked-focus(){} - - - - -// Legend -// ======================================================================== - -// @mixin hook-form-legend(){} - - -// Label -// ======================================================================== - - - - -// Layout -// ======================================================================== - -// @mixin hook-form-stacked-label(){} - -// @mixin hook-form-horizontal-label(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-form-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-form-label-color: $inverse-global-emphasis-color !default; - - - - - - - - -// @mixin hook-inverse-form-radio-checked-focus(){} - diff --git a/_sass/uikit/theme/grid.scss b/_sass/uikit/theme/grid.scss deleted file mode 100644 index adc18adbc7..0000000000 --- a/_sass/uikit/theme/grid.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Grid -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-grid-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/heading.scss b/_sass/uikit/theme/heading.scss deleted file mode 100644 index c6409f0bad..0000000000 --- a/_sass/uikit/theme/heading.scss +++ /dev/null @@ -1,59 +0,0 @@ -// -// Component: Heading -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Primary -// ======================================================================== - -// @mixin hook-heading-primary(){} - - -// Hero -// ======================================================================== - -// @mixin hook-heading-hero(){} - - -// Divider -// ======================================================================== - -// @mixin hook-heading-divider(){} - - -// Bullet -// ======================================================================== - -// @mixin hook-heading-bullet(){} - - -// Line -// ======================================================================== - -// @mixin hook-heading-line(){} - - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-heading-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-heading-primary(){} - -// @mixin hook-inverse-heading-hero(){} - -// @mixin hook-inverse-heading-divider(){} - -// @mixin hook-inverse-heading-bullet(){} - -// @mixin hook-inverse-heading-line(){} diff --git a/_sass/uikit/theme/icon.scss b/_sass/uikit/theme/icon.scss deleted file mode 100644 index b81c79abd8..0000000000 --- a/_sass/uikit/theme/icon.scss +++ /dev/null @@ -1,50 +0,0 @@ -// -// Component: Icon -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Style modifiers -// ======================================================================== - -// -// Link -// - -// @mixin hook-icon-link(){} - -// @mixin hook-icon-link-hover(){} - -// @mixin hook-icon-link-active(){} - -// -// Button -// - - - -// @mixin hook-icon-button-hover(){} - -// @mixin hook-icon-button-active(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-icon-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-icon-link(){} -// @mixin hook-inverse-icon-link-hover(){} -// @mixin hook-inverse-icon-link-active(){} - -// @mixin hook-inverse-icon-button(){} -// @mixin hook-inverse-icon-button-hover(){} -// @mixin hook-inverse-icon-button-active(){} \ No newline at end of file diff --git a/_sass/uikit/theme/iconnav.scss b/_sass/uikit/theme/iconnav.scss deleted file mode 100644 index 94b4bbf103..0000000000 --- a/_sass/uikit/theme/iconnav.scss +++ /dev/null @@ -1,34 +0,0 @@ -// -// Component: Iconnav -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - -// @mixin hook-iconnav(){} - -// @mixin hook-iconnav-item(){} - -// @mixin hook-iconnav-item-hover(){} - -// @mixin hook-iconnav-item-active(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-iconnav-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-iconnav-item(){} -// @mixin hook-inverse-iconnav-item-hover(){} -// @mixin hook-inverse-iconnav-item-active(){} \ No newline at end of file diff --git a/_sass/uikit/theme/inverse.scss b/_sass/uikit/theme/inverse.scss deleted file mode 100644 index 75a5a3b168..0000000000 --- a/_sass/uikit/theme/inverse.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Inverse -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - -// @mixin hook-inverse(){} \ No newline at end of file diff --git a/_sass/uikit/theme/label.scss b/_sass/uikit/theme/label.scss deleted file mode 100644 index ff09ac92cf..0000000000 --- a/_sass/uikit/theme/label.scss +++ /dev/null @@ -1,43 +0,0 @@ -// -// Component: Label -// -// ======================================================================== - - -// Variables -// ======================================================================== - -// -// New -// - -$label-border-radius: 2px !default; -$label-text-transform: uppercase !default; - - -// Component -// ======================================================================== - - - - -// Color modifiers -// ======================================================================== - -// @mixin hook-label-success(){} - -// @mixin hook-label-warning(){} - -// @mixin hook-label-danger(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-label-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-label(){} \ No newline at end of file diff --git a/_sass/uikit/theme/lightbox.scss b/_sass/uikit/theme/lightbox.scss deleted file mode 100644 index caabc6250e..0000000000 --- a/_sass/uikit/theme/lightbox.scss +++ /dev/null @@ -1,48 +0,0 @@ -// -// Component: Lightbox -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - -// @mixin hook-lightbox(){} - - -// Item -// ======================================================================== - -// @mixin hook-lightbox-item(){} - - -// Toolbar -// ======================================================================== - -// @mixin hook-lightbox-toolbar(){} - - -// Toolbar Icon -// ======================================================================== - -// @mixin hook-lightbox-toolbar-icon(){} - -// @mixin hook-lightbox-toolbar-icon-hover(){} - - -// Button -// ======================================================================== - -// @mixin hook-lightbox-button(){} - -// @mixin hook-lightbox-button-hover(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-lightbox-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/link.scss b/_sass/uikit/theme/link.scss deleted file mode 100644 index 0658b58a6d..0000000000 --- a/_sass/uikit/theme/link.scss +++ /dev/null @@ -1,55 +0,0 @@ -// -// Component: Link -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Muted -// ======================================================================== - -// @mixin hook-link-muted(){} - -// @mixin hook-link-muted-hover(){} - - -// Text -// ======================================================================== - -// @mixin hook-link-text(){} - -// @mixin hook-link-text-hover(){} - - -// Heading -// ======================================================================== - -// @mixin hook-link-heading(){} - -// @mixin hook-link-heading-hover(){} - - -// Reset -// ======================================================================== - -// @mixin hook-link-reset(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-link-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-link-muted(){} -// @mixin hook-inverse-link-muted-hover(){} - -// @mixin hook-inverse-link-text-hover(){} - -// @mixin hook-inverse-link-heading-hover(){} diff --git a/_sass/uikit/theme/list.scss b/_sass/uikit/theme/list.scss deleted file mode 100644 index 67e3c72c8d..0000000000 --- a/_sass/uikit/theme/list.scss +++ /dev/null @@ -1,39 +0,0 @@ -// -// Component: List -// -// ======================================================================== - - -// Variables -// ======================================================================== - -// -// New -// - -$list-striped-border-width: $global-border-width !default; -$list-striped-border: $global-border !default; - - -// Style modifiers -// ======================================================================== - -// @mixin hook-list-divider(){} - - - -// @mixin hook-list-bullet(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-list-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-list-divider(){} - -// @mixin hook-inverse-list-bullet(){} \ No newline at end of file diff --git a/_sass/uikit/theme/margin.scss b/_sass/uikit/theme/margin.scss deleted file mode 100644 index a2cdb5ec99..0000000000 --- a/_sass/uikit/theme/margin.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Margin -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-margin-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/marker.scss b/_sass/uikit/theme/marker.scss deleted file mode 100644 index 1e4fd5f379..0000000000 --- a/_sass/uikit/theme/marker.scss +++ /dev/null @@ -1,29 +0,0 @@ -// -// Component: Marker -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - - - -// @mixin hook-marker-hover(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-marker-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-marker(){} -// @mixin hook-inverse-marker-hover(){} diff --git a/_sass/uikit/theme/modal.scss b/_sass/uikit/theme/modal.scss deleted file mode 100644 index adc2135808..0000000000 --- a/_sass/uikit/theme/modal.scss +++ /dev/null @@ -1,84 +0,0 @@ -// -// Component: Modal -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$modal-header-background: $modal-dialog-background !default; -$modal-footer-background: $modal-dialog-background !default; - -// -// New -// - -$modal-header-border-width: $global-border-width !default; -$modal-header-border: $global-border !default; - -$modal-footer-border-width: $global-border-width !default; -$modal-footer-border: $global-border !default; - -$modal-close-full-padding: $global-margin !default; -$modal-close-full-background: $modal-dialog-background !default; - - -// Component -// ======================================================================== - -// @mixin hook-modal(){} - - -// Dialog -// ======================================================================== - -// @mixin hook-modal-dialog(){} - - -// Full -// ======================================================================== - -// @mixin hook-modal-full(){} - - -// Sections -// ======================================================================== - - - -// @mixin hook-modal-body(){} - - - - -// Title -// ======================================================================== - -// @mixin hook-modal-title(){} - - -// Close -// ======================================================================== - -// @mixin hook-modal-close(){} - -// @mixin hook-modal-close-hover(){} - -// @mixin hook-modal-close-default(){} - -// @mixin hook-modal-close-default-hover(){} - -// @mixin hook-modal-close-outside(){} - -// @mixin hook-modal-close-outside-hover(){} - - - -// @mixin hook-modal-close-full-hover(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-modal-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/nav.scss b/_sass/uikit/theme/nav.scss deleted file mode 100644 index 3138498273..0000000000 --- a/_sass/uikit/theme/nav.scss +++ /dev/null @@ -1,94 +0,0 @@ -// -// Component: Nav -// -// ======================================================================== - - -// Variables -// ======================================================================== - -// -// New -// - -$nav-default-font-size: $global-small-font-size !default; - - -// Sublists -// ======================================================================== - -// @mixin hook-nav-sub(){} - - -// Parent icon modifier -// ======================================================================== - -// @mixin hook-nav-parent-icon(){} - - -// Header -// ======================================================================== - -// @mixin hook-nav-header(){} - - -// Divider -// ======================================================================== - -// @mixin hook-nav-divider(){} - - -// Default style modifier -// ======================================================================== - - - -// @mixin hook-nav-default-item(){} - -// @mixin hook-nav-default-item-hover(){} - -// @mixin hook-nav-default-item-active(){} - -// @mixin hook-nav-default-header(){} - -// @mixin hook-nav-default-divider(){} - - -// Primary style modifier -// ======================================================================== - -// @mixin hook-nav-primary(){} - -// @mixin hook-nav-primary-item(){} - -// @mixin hook-nav-primary-item-hover(){} - -// @mixin hook-nav-primary-item-active(){} - -// @mixin hook-nav-primary-header(){} - -// @mixin hook-nav-primary-divider(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-nav-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-nav-parent-icon(){} - -// @mixin hook-inverse-nav-default-item(){} -// @mixin hook-inverse-nav-default-item-hover(){} -// @mixin hook-inverse-nav-default-item-active(){} -// @mixin hook-inverse-nav-default-header(){} -// @mixin hook-inverse-nav-default-divider(){} - -// @mixin hook-inverse-nav-primary-item(){} -// @mixin hook-inverse-nav-primary-item-hover(){} -// @mixin hook-inverse-nav-primary-item-active(){} -// @mixin hook-inverse-nav-primary-header(){} -// @mixin hook-inverse-nav-primary-divider(){} \ No newline at end of file diff --git a/_sass/uikit/theme/navbar.scss b/_sass/uikit/theme/navbar.scss deleted file mode 100644 index 2a6f237f4e..0000000000 --- a/_sass/uikit/theme/navbar.scss +++ /dev/null @@ -1,136 +0,0 @@ -// -// Component: Navbar -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$navbar-nav-item-font-size: $global-small-font-size !default; - -$navbar-dropdown-margin: 15px !default; -$navbar-dropdown-padding: 25px !default; -$navbar-dropdown-background: $global-background !default; -$navbar-dropdown-grid-gutter-horizontal: ($navbar-dropdown-padding * 2) !default; - -// -// New -// - -$navbar-nav-item-text-transform: uppercase !default; - -$navbar-dropdown-nav-font-size: $global-small-font-size !default; - -$navbar-dropdown-box-shadow: 0 5px 12px rgba(0,0,0,0.15) !default; - -$navbar-dropbar-box-shadow: 0 5px 7px rgba(0, 0, 0, 0.05) !default; - -$navbar-dropdown-grid-divider-border-width: $global-border-width !default; -$navbar-dropdown-grid-divider-border: $navbar-dropdown-nav-divider-border !default; - - -// Component -// ======================================================================== - -// @mixin hook-navbar(){} - - -// Container -// ======================================================================== - -// @mixin hook-navbar-container(){} - - -// Nav -// ======================================================================== - - - -// @mixin hook-navbar-nav-item-hover(){} - -// @mixin hook-navbar-nav-item-onclick(){} - -// @mixin hook-navbar-nav-item-active(){} - - -// Item -// ======================================================================== - -// @mixin hook-navbar-item(){} - - -// Toggle -// ======================================================================== - -// @mixin hook-navbar-toggle(){} - -// @mixin hook-navbar-toggle-hover(){} - -// @mixin hook-navbar-toggle-icon(){} - -// @mixin hook-navbar-toggle-icon-hover(){} - - -// Subtitle -// ======================================================================== - -// @mixin hook-navbar-subtitle(){} - - -// Style modifiers -// ======================================================================== - -// @mixin hook-navbar-transparent(){} - -// @mixin hook-navbar-sticky(){} - - -// Dropdown -// ======================================================================== - - - - - - -// Dropdown nav -// ======================================================================== - - - -// @mixin hook-navbar-dropdown-nav-item(){} - -// @mixin hook-navbar-dropdown-nav-item-hover(){} - -// @mixin hook-navbar-dropdown-nav-header(){} - -// @mixin hook-navbar-dropdown-nav-divider(){} - - -// Dropbar -// ======================================================================== - -// @mixin hook-navbar-dropbar(){} - - - - -// Miscellaneous -// ======================================================================== - - - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-navbar-nav-item(){} -// @mixin hook-inverse-navbar-nav-item-hover(){} -// @mixin hook-inverse-navbar-nav-item-onclick(){} -// @mixin hook-inverse-navbar-nav-item-active(){} - -// @mixin hook-inverse-navbar-item(){} - -// @mixin hook-inverse-navbar-toggle(){} -// @mixin hook-inverse-navbar-toggle-hover(){} \ No newline at end of file diff --git a/_sass/uikit/theme/notification.scss b/_sass/uikit/theme/notification.scss deleted file mode 100644 index 57d5b5531b..0000000000 --- a/_sass/uikit/theme/notification.scss +++ /dev/null @@ -1,44 +0,0 @@ -// -// Component: Notification -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - -// @mixin hook-notification(){} - - -// Message -// ======================================================================== - -// @mixin hook-notification-message(){} - - -// Close -// ======================================================================== - -// @mixin hook-notification-close(){} - - -// Style modifiers -// ======================================================================== - -// @mixin hook-notification-primary(){} - -// @mixin hook-notification-success(){} - -// @mixin hook-notification-warning(){} - -// @mixin hook-notification-danger(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-notification-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/offcanvas.scss b/_sass/uikit/theme/offcanvas.scss deleted file mode 100644 index 283078ef36..0000000000 --- a/_sass/uikit/theme/offcanvas.scss +++ /dev/null @@ -1,32 +0,0 @@ -// -// Component: Off-canvas -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Bar -// ======================================================================== - -// @mixin hook-offcanvas-bar(){} - - -// Close -// ======================================================================== - -// @mixin hook-offcanvas-close(){} - - -// Overlay -// ======================================================================== - -// @mixin hook-offcanvas-overlay(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-offcanvas-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/overlay.scss b/_sass/uikit/theme/overlay.scss deleted file mode 100644 index 68cda45259..0000000000 --- a/_sass/uikit/theme/overlay.scss +++ /dev/null @@ -1,33 +0,0 @@ -// -// Component: Overlay -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - -// @mixin hook-overlay(){} - -// Icon -// ======================================================================== - -// @mixin hook-overlay-icon(){} - - -// Style modifiers -// ======================================================================== - -// @mixin hook-overlay-default(){} - -// @mixin hook-overlay-primary(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-overlay-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/padding.scss b/_sass/uikit/theme/padding.scss deleted file mode 100644 index f0737b873a..0000000000 --- a/_sass/uikit/theme/padding.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Padding -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-padding-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/pagination.scss b/_sass/uikit/theme/pagination.scss deleted file mode 100644 index a777e0c900..0000000000 --- a/_sass/uikit/theme/pagination.scss +++ /dev/null @@ -1,41 +0,0 @@ -// -// Component: Pagination -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - -// @mixin hook-pagination(){} - - -// Items -// ======================================================================== - - - -// @mixin hook-pagination-item-hover(){} - -// @mixin hook-pagination-item-active(){} - -// @mixin hook-pagination-item-disabled(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-pagination-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-pagination-item(){} -// @mixin hook-inverse-pagination-item-hover(){} -// @mixin hook-inverse-pagination-item-active(){} -// @mixin hook-inverse-pagination-item-disabled(){} \ No newline at end of file diff --git a/_sass/uikit/theme/placeholder.scss b/_sass/uikit/theme/placeholder.scss deleted file mode 100644 index 4ab662cb67..0000000000 --- a/_sass/uikit/theme/placeholder.scss +++ /dev/null @@ -1,29 +0,0 @@ -// -// Component: Placeholder -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$placeholder-background: transparent !default; - -// -// New -// - -$placeholder-border-width: $global-border-width !default; -$placeholder-border: $global-border !default; - - -// Component -// ======================================================================== - - - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-placeholder-misc(){} diff --git a/_sass/uikit/theme/position.scss b/_sass/uikit/theme/position.scss deleted file mode 100644 index fc69520898..0000000000 --- a/_sass/uikit/theme/position.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Position -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-position-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/progress.scss b/_sass/uikit/theme/progress.scss deleted file mode 100644 index 9ca100a3e6..0000000000 --- a/_sass/uikit/theme/progress.scss +++ /dev/null @@ -1,24 +0,0 @@ -// -// Component: Progress -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$progress-border-radius: 500px !default; - - -// Component -// ======================================================================== - - - -// @mixin hook-progress-bar(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-progress-misc(){} diff --git a/_sass/uikit/theme/search.scss b/_sass/uikit/theme/search.scss deleted file mode 100644 index f9e710e2d7..0000000000 --- a/_sass/uikit/theme/search.scss +++ /dev/null @@ -1,73 +0,0 @@ -// -// Component: Search -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$search-default-background: transparent !default; - -// -// New -// - -$search-default-border-width: $global-border-width !default; -$search-default-border: $global-border !default; - - -// Component -// ======================================================================== - -// @mixin hook-search-input(){} - - -// Default modifiers -// ======================================================================== - - - - -// Navbar modifiers -// ======================================================================== - -// @mixin hook-search-navbar-input(){} - -// @mixin hook-search-default-input-focus(){} - - -// Large modifiers -// ======================================================================== - -// @mixin hook-search-large-input(){} - - -// Toggle -// ======================================================================== - -// @mixin hook-search-toggle(){} - -// @mixin hook-search-toggle-hover(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-search-misc(){} - - -// Inverse -// ======================================================================== - -$inverse-search-default-background: transparent !default; - - -// @mixin hook-inverse-search-default-input-focus(){} - -// @mixin hook-inverse-search-navbar-input(){} - -// @mixin hook-inverse-search-large-input(){} - -// @mixin hook-inverse-search-toggle(){} -// @mixin hook-inverse-search-toggle-hover(){} \ No newline at end of file diff --git a/_sass/uikit/theme/section.scss b/_sass/uikit/theme/section.scss deleted file mode 100644 index 6d7f761b6e..0000000000 --- a/_sass/uikit/theme/section.scss +++ /dev/null @@ -1,32 +0,0 @@ -// -// Component: Section -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - -// @mixin hook-section(){} - - -// Style modifiers -// ======================================================================== - -// @mixin hook-section-default(){} - -// @mixin hook-section-muted(){} - -// @mixin hook-section-primary(){} - -// @mixin hook-section-secondary(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-section-misc(){} diff --git a/_sass/uikit/theme/slidenav.scss b/_sass/uikit/theme/slidenav.scss deleted file mode 100644 index c1654e77f1..0000000000 --- a/_sass/uikit/theme/slidenav.scss +++ /dev/null @@ -1,52 +0,0 @@ -// -// Component: Slidenav -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - - - -// @mixin hook-slidenav-hover(){} - -// @mixin hook-slidenav-active(){} - - -// Icon modifier -// ======================================================================== - -// @mixin hook-slidenav-previous(){} - -// @mixin hook-slidenav-next(){} - - -// Size modifier -// ======================================================================== - -// @mixin hook-slidenav-large(){} - - -// Container -// ======================================================================== - -// @mixin hook-slidenav-container(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-icon-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-slidenav(){} -// @mixin hook-inverse-slidenav-hover(){} -// @mixin hook-inverse-slidenav-active(){} \ No newline at end of file diff --git a/_sass/uikit/theme/sortable.scss b/_sass/uikit/theme/sortable.scss deleted file mode 100644 index 3ab18c3db1..0000000000 --- a/_sass/uikit/theme/sortable.scss +++ /dev/null @@ -1,38 +0,0 @@ -// -// Component: Sortable -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - -// @mixin hook-sortable(){} - - -// Drag -// ======================================================================== - -// @mixin hook-sortable-drag(){} - - -// Placeholder -// ======================================================================== - -// @mixin hook-sortable-placeholder(){} - - -// Empty -// ======================================================================== - -// @mixin hook-sortable-empty(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-sortable-misc(){} diff --git a/_sass/uikit/theme/spinner.scss b/_sass/uikit/theme/spinner.scss deleted file mode 100644 index d70e10fa81..0000000000 --- a/_sass/uikit/theme/spinner.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Spinner -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-spinner-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/sticky.scss b/_sass/uikit/theme/sticky.scss deleted file mode 100644 index 94e5ee69ee..0000000000 --- a/_sass/uikit/theme/sticky.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Sticky -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-sticky-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/subnav.scss b/_sass/uikit/theme/subnav.scss deleted file mode 100644 index f4d1c7fd06..0000000000 --- a/_sass/uikit/theme/subnav.scss +++ /dev/null @@ -1,74 +0,0 @@ -// -// Component: Subnav -// -// ======================================================================== - - -// Variables -// ======================================================================== - -// -// New -// - -$subnav-item-font-size: $global-small-font-size !default; -$subnav-item-text-transform: uppercase !default; - - -// Component -// ======================================================================== - -// @mixin hook-subnav(){} - - - -// @mixin hook-subnav-item-hover(){} - -// @mixin hook-subnav-item-active(){} - - -// Divider modifier -// ======================================================================== - -// @mixin hook-subnav-divider(){} - - -// Pill modifier -// ======================================================================== - -// @mixin hook-subnav-pill-item(){} - -// @mixin hook-subnav-pill-item-hover(){} - -// @mixin hook-subnav-pill-item-onclick(){} - -// @mixin hook-subnav-pill-item-active(){} - - -// Disabled -// ======================================================================== - -// @mixin hook-subnav-item-disabled(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-subnav-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-subnav-item(){} -// @mixin hook-inverse-subnav-item-hover(){} -// @mixin hook-inverse-subnav-item-active(){} - -// @mixin hook-inverse-subnav-divider(){} - -// @mixin hook-inverse-subnav-pill-item(){} -// @mixin hook-inverse-subnav-pill-item-hover(){} -// @mixin hook-inverse-subnav-pill-item-onclick(){} -// @mixin hook-inverse-subnav-pill-item-active(){} - -// @mixin hook-inverse-subnav-item-disabled(){} diff --git a/_sass/uikit/theme/tab.scss b/_sass/uikit/theme/tab.scss deleted file mode 100644 index 51c4ba2866..0000000000 --- a/_sass/uikit/theme/tab.scss +++ /dev/null @@ -1,74 +0,0 @@ -// -// Component: Tab -// -// ======================================================================== - - -// Variables -// ======================================================================== - -// -// New -// - -$tab-border-width: $global-border-width !default; -$tab-border: $global-border !default; - -$tab-item-border-width: $global-border-width !default; -$tab-item-font-size: $global-small-font-size !default; -$tab-item-text-transform: uppercase !default; - -$tab-item-active-border: $global-primary-background !default; - - -// Component -// ======================================================================== - - - - -// Items -// ======================================================================== - - - -// @mixin hook-tab-item-hover(){} - - - -// @mixin hook-tab-item-disabled(){} - - -// Position modifiers -// ======================================================================== - - - - - - - - - - - - - - -// Miscellaneous -// ======================================================================== - - - - -// Inverse -// ======================================================================== - -$inverse-tab-border: $inverse-global-border !default; - - - -// @mixin hook-inverse-tab-item(){} -// @mixin hook-inverse-tab-item-hover(){} - -// @mixin hook-inverse-tab-item-disabled(){} \ No newline at end of file diff --git a/_sass/uikit/theme/table.scss b/_sass/uikit/theme/table.scss deleted file mode 100644 index d6a660793b..0000000000 --- a/_sass/uikit/theme/table.scss +++ /dev/null @@ -1,68 +0,0 @@ -// -// Component: Table -// -// ======================================================================== - - -// Variables -// ======================================================================== - -$table-header-cell-font-size: $global-small-font-size !default; -$table-header-cell-font-weight: normal !default; -$table-header-cell-color: $global-muted-color !default; - -// -// New -// - -$table-striped-border-width: $global-border-width !default; -$table-striped-border: $global-border !default; - - -// Component -// ======================================================================== - - - -// @mixin hook-table-cell(){} - -// @mixin hook-table-footer(){} - -// @mixin hook-table-caption(){} - -// @mixin hook-table-row-active(){} - - -// Style modifiers -// ======================================================================== - -// @mixin hook-table-divider(){} - - - -// @mixin hook-table-hover(){} - - -// Size modifier -// ======================================================================== - -// @mixin hook-table-small(){} - -// @mixin hook-table-large(){} - - -// Miscellaneous -// ======================================================================== - - - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-table-header-cell(){} -// @mixin hook-inverse-table-caption(){} -// @mixin hook-inverse-table-row-active(){} -// @mixin hook-inverse-table-divider(){} - -// @mixin hook-inverse-table-hover(){} \ No newline at end of file diff --git a/_sass/uikit/theme/text.scss b/_sass/uikit/theme/text.scss deleted file mode 100644 index b6e35c431f..0000000000 --- a/_sass/uikit/theme/text.scss +++ /dev/null @@ -1,50 +0,0 @@ -// -// Component: Text -// -// ======================================================================== - - -// Variables -// ======================================================================== - -// -// New -// - -$text-meta-link-color: $text-meta-color !default; -$text-meta-link-hover-color: $global-color !default; - - -// Style modifiers -// ======================================================================== - -// @mixin hook-text-lead(){} - - - - -// Size modifiers -// ======================================================================== - -// @mixin hook-text-small(){} - -// @mixin hook-text-large(){} - - -// Background modifier -// ======================================================================== - -// @mixin hook-text-background(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-text-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-text-lead(){} -// @mixin hook-inverse-text-meta(){} diff --git a/_sass/uikit/theme/thumbnav.scss b/_sass/uikit/theme/thumbnav.scss deleted file mode 100644 index 7f26c38aa8..0000000000 --- a/_sass/uikit/theme/thumbnav.scss +++ /dev/null @@ -1,42 +0,0 @@ -// -// Component: Thumbnav -// -// ======================================================================== - - -// Variables -// ======================================================================== - -// -// New -// - -$thumbnav-item-background: rgba($global-background, 0.4) !default; -$thumbnav-item-hover-background: transparent !default; -$thumbnav-item-active-background: transparent !default; - - -// Component -// ======================================================================== - -// @mixin hook-thumbnav(){} - - - - - - - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-thumbnav-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-thumbnav-item(){} -// @mixin hook-inverse-thumbnav-item-hover(){} -// @mixin hook-inverse-thumbnav-item-active(){} \ No newline at end of file diff --git a/_sass/uikit/theme/tile.scss b/_sass/uikit/theme/tile.scss deleted file mode 100644 index 2d043a6338..0000000000 --- a/_sass/uikit/theme/tile.scss +++ /dev/null @@ -1,32 +0,0 @@ -// -// Component: Tile -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - -// @mixin hook-tile(){} - - -// Style modifiers -// ======================================================================== - -// @mixin hook-tile-default(){} - -// @mixin hook-tile-muted(){} - -// @mixin hook-tile-primary(){} - -// @mixin hook-tile-secondary(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-tile-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/tooltip.scss b/_sass/uikit/theme/tooltip.scss deleted file mode 100644 index 5115139c31..0000000000 --- a/_sass/uikit/theme/tooltip.scss +++ /dev/null @@ -1,20 +0,0 @@ -// -// Component: Tooltip -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - -// @mixin hook-tooltip(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-tooltip-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/totop.scss b/_sass/uikit/theme/totop.scss deleted file mode 100644 index feb7165a1d..0000000000 --- a/_sass/uikit/theme/totop.scss +++ /dev/null @@ -1,32 +0,0 @@ -// -// Component: Totop -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Component -// ======================================================================== - - - -// @mixin hook-totop-hover(){} - -// @mixin hook-totop-active(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-icon-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-totop(){} -// @mixin hook-inverse-totop-hover(){} -// @mixin hook-inverse-totop-active(){} \ No newline at end of file diff --git a/_sass/uikit/theme/transition.scss b/_sass/uikit/theme/transition.scss deleted file mode 100644 index fd7bdede45..0000000000 --- a/_sass/uikit/theme/transition.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Transition -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-transition-misc(){} \ No newline at end of file diff --git a/_sass/uikit/theme/utility.scss b/_sass/uikit/theme/utility.scss deleted file mode 100644 index ae24e15cf5..0000000000 --- a/_sass/uikit/theme/utility.scss +++ /dev/null @@ -1,57 +0,0 @@ -// -// Component: Utility -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Panel -// ======================================================================== - -// @mixin hook-panel-scrollable(){} - - -// Box-shadow bottom -// ======================================================================== - -// @mixin hook-box-shadow-bottom(){} - - -// Drop cap -// ======================================================================== - - - - -// Leader -// ======================================================================== - -// @mixin hook-leader(){} - - -// Logo -// ======================================================================== - -// @mixin hook-logo(){} - -// @mixin hook-logo-hover(){} - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-utility-misc(){} - - -// Inverse -// ======================================================================== - -// @mixin hook-inverse-dropcap(){} - -// @mixin hook-inverse-leader(){} - -// @mixin hook-inverse-logo(){} -// @mixin hook-inverse-logo-hover(){} \ No newline at end of file diff --git a/_sass/uikit/theme/variables.scss b/_sass/uikit/theme/variables.scss deleted file mode 100644 index d74b3a9971..0000000000 --- a/_sass/uikit/theme/variables.scss +++ /dev/null @@ -1,36 +0,0 @@ -// -// Component: Variables -// -// ======================================================================== - - -// Global variables -// ======================================================================== - -// -// Typography -// - -// -// Colors -// - -// -// Backgrounds -// - -// -// Borders -// - -// -// Spacings -// - -// -// Controls -// - -// -// Z-index -// \ No newline at end of file diff --git a/_sass/uikit/theme/width.scss b/_sass/uikit/theme/width.scss deleted file mode 100644 index b67a7954ed..0000000000 --- a/_sass/uikit/theme/width.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Component: Width -// -// ======================================================================== - - -// Variables -// ======================================================================== - - -// Miscellaneous -// ======================================================================== - -// @mixin hook-width-misc(){} \ No newline at end of file diff --git a/_sass/uikit/uikit-theme.scss b/_sass/uikit/uikit-theme.scss deleted file mode 100644 index 13dbd02217..0000000000 --- a/_sass/uikit/uikit-theme.scss +++ /dev/null @@ -1,9 +0,0 @@ -// -// Theme -// - -@import "theme/_import.scss"; - -@import "components/_import.scss"; - - diff --git a/_sass/uikit/uikit.scss b/_sass/uikit/uikit.scss deleted file mode 100644 index 662482314f..0000000000 --- a/_sass/uikit/uikit.scss +++ /dev/null @@ -1,5 +0,0 @@ -// -// Core -// - -@import "components/_import.scss"; \ No newline at end of file diff --git a/_sass/uikit/variables-theme.scss b/_sass/uikit/variables-theme.scss deleted file mode 100644 index 8aa5a9c40b..0000000000 --- a/_sass/uikit/variables-theme.scss +++ /dev/null @@ -1,1093 +0,0 @@ -$global-margin: 20px !default; -$accordion-item-margin-top: $global-margin !default; -$global-medium-font-size: 1.25rem !default; -$accordion-title-font-size: $global-medium-font-size !default; -$accordion-title-line-height: 1.4 !default; -$global-emphasis-color: #333 !default; -$accordion-title-color: $global-emphasis-color !default; -$global-color: #666 !default; -$accordion-title-hover-color: $global-color !default; -$accordion-content-margin-top: $global-margin !default; -$global-inverse-color: #fff !default; -$inverse-global-emphasis-color: $global-inverse-color !default; -$inverse-accordion-title-color: $inverse-global-emphasis-color !default; -$inverse-global-inverse-color: $global-color !default; -$inverse-accordion-title-hover-color: $inverse-global-inverse-color !default; -$global-gutter: 30px !default; -$align-margin-horizontal: $global-gutter !default; -$align-margin-vertical: $global-gutter !default; -$global-medium-gutter: 40px !default; -$align-margin-horizontal-l: $global-medium-gutter !default; -$alert-margin-vertical: $global-margin !default; -$global-small-gutter: 15px !default; -$alert-padding: $global-small-gutter !default; -$alert-padding-right: $alert-padding + 14px !default; -$global-muted-background: #f8f8f8 !default; -$alert-background: $global-muted-background !default; -$alert-color: $global-color !default; -$alert-close-top: $alert-padding + 5px !default; -$alert-close-right: $alert-padding !default; -$global-primary-background: #1e87f0 !default; -$alert-primary-background: lighten(mix(white, $global-primary-background, 40%), 20%) !default; -$alert-primary-color: $global-primary-background !default; -$global-success-background: #32d296 !default; -$alert-success-background: lighten(mix(white, $global-success-background, 40%), 25%) !default; -$alert-success-color: $global-success-background !default; -$global-warning-background: #faa05a !default; -$alert-warning-background: lighten(mix(white, $global-warning-background, 45%), 15%) !default; -$alert-warning-color: $global-warning-background !default; -$global-danger-background: #f0506e !default; -$alert-danger-background: lighten(mix(white, $global-danger-background, 40%), 20%) !default; -$alert-danger-color: $global-danger-background !default; -$global-large-margin: 70px !default; -$article-margin-top: $global-large-margin !default; -$global-xxlarge-font-size: 2.625rem !default; -$article-title-font-size: $global-xxlarge-font-size !default; -$article-title-line-height: 1.2 !default; -$global-small-font-size: 0.875rem !default; -$article-meta-font-size: $global-small-font-size !default; -$article-meta-line-height: 1.4 !default; -$global-muted-color: #999 !default; -$article-meta-color: $global-muted-color !default; -$inverse-global-muted-color: rgba($global-inverse-color, 0.5) !default; -$inverse-article-meta-color: $inverse-global-muted-color !default; -$animation-duration: 0.5s !default; -$animation-fade-duration: 0.8s !default; -$animation-kenburns-duration: 15s !default; -$animation-fast-duration: 0.1s !default; -$animation-slide-small-translate: 10px !default; -$animation-slide-medium-translate: 50px !default; -$global-background: #fff !default; -$background-default-background: $global-background !default; -$background-muted-background: $global-muted-background !default; -$background-primary-background: $global-primary-background !default; -$global-secondary-background: #222 !default; -$background-secondary-background: $global-secondary-background !default; -$badge-size: 22px !default; -$badge-padding-vertical: 0 !default; -$badge-padding-horizontal: 5px !default; -$badge-border-radius: 500px !default; -$badge-background: $global-primary-background !default; -$badge-color: $global-inverse-color !default; -$badge-font-size: $global-small-font-size !default; -$badge-hover-color: $global-inverse-color !default; -$inverse-global-primary-background: $global-inverse-color !default; -$inverse-badge-background: $inverse-global-primary-background !default; -$inverse-badge-color: $inverse-global-inverse-color !default; -$inverse-badge-hover-color: $inverse-global-inverse-color !default; -$base-body-background: $global-background !default; -$global-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !default; -$base-body-font-family: $global-font-family !default; -$base-body-font-weight: normal !default; -$global-font-size: 16px !default; -$base-body-font-size: $global-font-size !default; -$global-line-height: 1.5 !default; -$base-body-line-height: $global-line-height !default; -$base-body-color: $global-color !default; -$global-link-color: #1e87f0 !default; -$base-link-color: $global-link-color !default; -$base-link-text-decoration: none !default; -$global-link-hover-color: #0f6ecd !default; -$base-link-hover-color: $global-link-hover-color !default; -$base-link-hover-text-decoration: underline !default; -$base-strong-font-weight: bolder !default; -$base-code-font-size: $global-small-font-size !default; -$base-code-font-family: Consolas, monaco, monospace !default; -$base-code-color: $global-danger-background !default; -$base-em-color: $global-danger-background !default; -$base-ins-background: #ffd !default; -$base-ins-color: $global-color !default; -$base-mark-background: #ffd !default; -$base-mark-color: $global-color !default; -$base-quote-font-style: italic !default; -$base-small-font-size: 80% !default; -$base-margin-vertical: $global-margin !default; -$base-heading-font-family: $global-font-family !default; -$base-heading-font-weight: normal !default; -$base-heading-color: $global-emphasis-color !default; -$base-heading-text-transform: none !default; -$global-medium-margin: 40px !default; -$base-heading-margin-top: $global-medium-margin !default; -$base-h1-font-size: $global-xxlarge-font-size !default; -$base-h1-line-height: 1.2 !default; -$global-xlarge-font-size: 2rem !default; -$base-h2-font-size: $global-xlarge-font-size !default; -$base-h2-line-height: 1.3 !default; -$global-large-font-size: 1.5rem !default; -$base-h3-font-size: $global-large-font-size !default; -$base-h3-line-height: 1.4 !default; -$base-h4-font-size: $global-medium-font-size !default; -$base-h4-line-height: 1.4 !default; -$base-h5-font-size: $global-font-size !default; -$base-h5-line-height: 1.4 !default; -$base-h6-font-size: $global-small-font-size !default; -$base-h6-line-height: 1.4 !default; -$base-list-padding-left: 30px !default; -$base-hr-margin-vertical: $global-margin !default; -$global-border-width: 1px !default; -$base-hr-border-width: $global-border-width !default; -$global-border: #e5e5e5 !default; -$base-hr-border: $global-border !default; -$base-blockquote-font-size: $global-medium-font-size !default; -$base-blockquote-line-height: 1.5 !default; -$base-blockquote-font-style: italic !default; -$base-blockquote-margin-vertical: $global-margin !default; -$global-small-margin: 10px !default; -$base-blockquote-footer-margin-top: $global-small-margin !default; -$base-blockquote-footer-font-size: $global-small-font-size !default; -$base-blockquote-footer-line-height: 1.5 !default; -$base-pre-font-size: $global-small-font-size !default; -$base-pre-line-height: 1.5 !default; -$base-pre-font-family: $base-code-font-family !default; -$base-pre-color: $global-color !default; -$base-selection-background: #39f !default; -$base-selection-color: $global-inverse-color !default; -$inverse-global-color: rgba($global-inverse-color, 0.7) !default; -$inverse-base-color: $inverse-global-color !default; -$inverse-base-link-color: $inverse-global-emphasis-color !default; -$inverse-base-link-hover-color: $inverse-global-emphasis-color !default; -$inverse-base-code-color: $inverse-global-color !default; -$inverse-base-em-color: $inverse-global-emphasis-color !default; -$inverse-base-heading-color: $inverse-global-emphasis-color !default; -$inverse-global-border: rgba($global-inverse-color, 0.2) !default; -$inverse-base-hr-border: $inverse-global-border !default; -$breadcrumb-item-font-size: $global-small-font-size !default; -$breadcrumb-item-color: $global-muted-color !default; -$breadcrumb-item-hover-color: $global-color !default; -$breadcrumb-item-hover-text-decoration: none !default; -$breadcrumb-item-active-color: $global-color !default; -$breadcrumb-divider: "/" !default; -$breadcrumb-divider-margin-horizontal: 20px !default; -$breadcrumb-divider-color: $global-muted-color !default; -$inverse-breadcrumb-item-color: $inverse-global-muted-color !default; -$inverse-breadcrumb-item-hover-color: $inverse-global-color !default; -$inverse-breadcrumb-item-active-color: $inverse-global-color !default; -$inverse-breadcrumb-divider-color: $inverse-global-muted-color !default; -$global-control-height: 40px !default; -$button-border-width: $global-border-width !default; -$button-line-height: $global-control-height - ($button-border-width * 2) !default; -$global-control-small-height: 30px !default; -$button-small-line-height: $global-control-small-height - ($button-border-width * 2) !default; -$global-control-large-height: 55px !default; -$button-large-line-height: $global-control-large-height - ($button-border-width * 2) !default; -$button-font-size: $global-small-font-size !default; -$button-small-font-size: $global-small-font-size !default; -$button-large-font-size: $global-small-font-size !default; -$button-padding-horizontal: $global-gutter !default; -$button-small-padding-horizontal: $global-small-gutter !default; -$button-large-padding-horizontal: $global-medium-gutter !default; -$button-default-background: transparent !default; -$button-default-color: $global-emphasis-color !default; -$button-default-hover-background: transparent !default; -$button-default-hover-color: $global-emphasis-color !default; -$button-default-active-background: transparent !default; -$button-default-active-color: $global-emphasis-color !default; -$button-primary-background: $global-primary-background !default; -$button-primary-color: $global-inverse-color !default; -$button-primary-hover-background: darken($button-primary-background, 5%) !default; -$button-primary-hover-color: $global-inverse-color !default; -$button-primary-active-background: darken($button-primary-background, 10%) !default; -$button-primary-active-color: $global-inverse-color !default; -$button-secondary-background: $global-secondary-background !default; -$button-secondary-color: $global-inverse-color !default; -$button-secondary-hover-background: darken($button-secondary-background, 5%) !default; -$button-secondary-hover-color: $global-inverse-color !default; -$button-secondary-active-background: darken($button-secondary-background, 10%) !default; -$button-secondary-active-color: $global-inverse-color !default; -$button-danger-background: $global-danger-background !default; -$button-danger-color: $global-inverse-color !default; -$button-danger-hover-background: darken($button-danger-background, 5%) !default; -$button-danger-hover-color: $global-inverse-color !default; -$button-danger-active-background: darken($button-danger-background, 10%) !default; -$button-danger-active-color: $global-inverse-color !default; -$button-disabled-background: transparent !default; -$button-disabled-color: $global-muted-color !default; -$button-text-line-height: $global-line-height !default; -$button-text-color: $global-emphasis-color !default; -$button-text-hover-color: $global-emphasis-color !default; -$button-text-disabled-color: $global-muted-color !default; -$button-link-line-height: $global-line-height !default; -$button-link-color: $global-link-color !default; -$button-link-hover-color: $global-link-hover-color !default; -$button-link-hover-text-decoration: underline !default; -$button-link-disabled-color: $global-muted-color !default; -$inverse-button-default-background: transparent !default; -$inverse-button-default-color: $inverse-global-emphasis-color !default; -$inverse-button-default-hover-background: transparent !default; -$inverse-button-default-hover-color: $inverse-global-emphasis-color !default; -$inverse-button-default-active-background: transparent !default; -$inverse-button-default-active-color: $inverse-global-emphasis-color !default; -$inverse-button-primary-background: $inverse-global-primary-background !default; -$inverse-button-primary-color: $inverse-global-inverse-color !default; -$inverse-button-primary-hover-background: darken($inverse-button-primary-background, 5%) !default; -$inverse-button-primary-hover-color: $inverse-global-inverse-color !default; -$inverse-button-primary-active-background: darken($inverse-button-primary-background, 10%) !default; -$inverse-button-primary-active-color: $inverse-global-inverse-color !default; -$inverse-button-secondary-background: $inverse-global-primary-background !default; -$inverse-button-secondary-color: $inverse-global-inverse-color !default; -$inverse-button-secondary-hover-background: darken($inverse-button-secondary-background, 5%) !default; -$inverse-button-secondary-hover-color: $inverse-global-inverse-color !default; -$inverse-button-secondary-active-background: darken($inverse-button-secondary-background, 10%) !default; -$inverse-button-secondary-active-color: $inverse-global-inverse-color !default; -$inverse-button-text-color: $inverse-global-emphasis-color !default; -$inverse-button-text-hover-color: $inverse-global-emphasis-color !default; -$inverse-button-text-disabled-color: $inverse-global-muted-color !default; -$inverse-button-link-color: $inverse-global-muted-color !default; -$inverse-button-link-hover-color: $inverse-global-color !default; -$card-body-padding-horizontal: $global-gutter !default; -$card-body-padding-vertical: $global-gutter !default; -$card-body-padding-horizontal-l: $global-medium-gutter !default; -$card-body-padding-vertical-l: $global-medium-gutter !default; -$card-header-padding-horizontal: $global-gutter !default; -$card-header-padding-vertical: round($global-gutter / 2) !default; -$card-header-padding-horizontal-l: $global-medium-gutter !default; -$card-header-padding-vertical-l: round($global-medium-gutter / 2) !default; -$card-footer-padding-horizontal: $global-gutter !default; -$card-footer-padding-vertical: ($global-gutter / 2) !default; -$card-footer-padding-horizontal-l: $global-medium-gutter !default; -$card-footer-padding-vertical-l: round($global-medium-gutter / 2) !default; -$card-title-font-size: $global-large-font-size !default; -$card-title-line-height: 1.4 !default; -$card-badge-top: $global-gutter !default; -$card-badge-right: $card-badge-top !default; -$card-hover-background: $global-background !default; -$card-default-background: $global-background !default; -$card-default-color: $global-color !default; -$card-default-title-color: $global-emphasis-color !default; -$card-default-hover-background: $card-default-background !default; -$card-primary-background: $global-primary-background !default; -$card-primary-color: $global-inverse-color !default; -$card-primary-title-color: $card-primary-color !default; -$card-primary-hover-background: $card-primary-background !default; -$card-primary-color-mode: light !default; -$card-secondary-background: $global-secondary-background !default; -$card-secondary-color: $global-inverse-color !default; -$card-secondary-title-color: $card-secondary-color !default; -$card-secondary-hover-background: $card-secondary-background !default; -$card-secondary-color-mode: light !default; -$card-small-body-padding-horizontal: $global-margin !default; -$card-small-body-padding-vertical: $global-margin !default; -$card-small-header-padding-horizontal: $global-margin !default; -$card-small-header-padding-vertical: round($global-margin / 1.5) !default; -$card-small-footer-padding-horizontal: $global-margin !default; -$card-small-footer-padding-vertical: round($global-margin / 1.5) !default; -$global-large-gutter: 70px !default; -$card-large-body-padding-horizontal-l: $global-large-gutter !default; -$card-large-body-padding-vertical-l: $global-large-gutter !default; -$card-large-header-padding-horizontal-l: $global-large-gutter !default; -$card-large-header-padding-vertical-l: round($global-large-gutter / 2) !default; -$card-large-footer-padding-horizontal-l: $global-large-gutter !default; -$card-large-footer-padding-vertical-l: round($global-large-gutter / 2) !default; -$close-color: $global-muted-color !default; -$close-hover-color: $global-color !default; -$inverse-close-color: $inverse-global-muted-color !default; -$inverse-close-hover-color: $inverse-global-color !default; -$column-gutter: $global-gutter !default; -$column-gutter-l: $global-medium-gutter !default; -$column-divider-rule-color: $global-border !default; -$column-divider-rule-width: 1px !default; -$inverse-column-divider-rule-color: $inverse-global-border !default; -$comment-header-margin-bottom: $global-margin !default; -$comment-title-font-size: $global-medium-font-size !default; -$comment-title-line-height: 1.4 !default; -$comment-meta-font-size: $global-small-font-size !default; -$comment-meta-line-height: 1.4 !default; -$comment-meta-color: $global-muted-color !default; -$comment-list-margin-top: $global-large-margin !default; -$comment-list-padding-left: 30px !default; -$comment-list-padding-left-m: 100px !default; -$container-max-width: 1200px !default; -$container-small-max-width: 900px !default; -$container-large-max-width: 1600px !default; -$container-padding-horizontal: 15px !default; -$container-padding-horizontal-s: $global-gutter !default; -$container-padding-horizontal-m: $global-medium-gutter !default; -$countdown-item-line-height: 70px !default; -$countdown-number-font-size: 2rem !default; -$countdown-number-font-size-s: 4rem !default; -$countdown-number-font-size-m: 6rem !default; -$countdown-separator-font-size: 1rem !default; -$countdown-separator-font-size-s: 2rem !default; -$countdown-separator-font-size-m: 3rem !default; -$description-list-term-color: $global-emphasis-color !default; -$description-list-term-margin-top: $global-margin !default; -$description-list-divider-term-margin-top: $global-margin !default; -$description-list-divider-term-border-width: $global-border-width !default; -$description-list-divider-term-border: $global-border !default; -$divider-margin-vertical: $global-margin !default; -$divider-icon-width: 50px !default; -$divider-icon-height: 20px !default; -$divider-icon-color: $global-border !default; -$divider-icon-line-top: 50% !default; -$divider-icon-line-width: 100% !default; -$divider-icon-line-border-width: $global-border-width !default; -$divider-icon-line-border: $global-border !default; -$internal-divider-icon-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22none%22%20stroke%3D%22#000%22%20stroke-width%3D%222%22%20cx%3D%2210%22%20cy%3D%2210%22%20r%3D%227%22%20%2F%3E%0A%3C%2Fsvg%3E%0A" !default; -$divider-small-width: 100px !default; -$divider-small-border-width: $global-border-width !default; -$divider-small-border: $global-border !default; -$inverse-divider-icon-color: $inverse-global-border !default; -$inverse-divider-icon-line-border: $inverse-global-border !default; -$inverse-divider-small-border: $inverse-global-border !default; -$dotnav-margin-horizontal: 12px !default; -$dotnav-margin-vertical: $dotnav-margin-horizontal !default; -$dotnav-item-width: 10px !default; -$dotnav-item-height: $dotnav-item-width !default; -$dotnav-item-border-radius: 50% !default; -$dotnav-item-background: transparent !default; -$dotnav-item-hover-background: rgba($global-color, 0.6) !default; -$dotnav-item-onclick-background: rgba($global-color, 0.2) !default; -$dotnav-item-active-background: rgba($global-color, 0.6) !default; -$inverse-dotnav-item-background: transparent !default; -$inverse-dotnav-item-hover-background: rgba($inverse-global-color, 0.9) !default; -$inverse-dotnav-item-onclick-background: rgba($inverse-global-color, 0.5) !default; -$inverse-dotnav-item-active-background: rgba($inverse-global-color, 0.9) !default; -$global-z-index: 1000 !default; -$drop-z-index: $global-z-index + 20 !default; -$drop-width: 300px !default; -$drop-margin: $global-margin !default; -$dropdown-z-index: $global-z-index + 20 !default; -$dropdown-min-width: 200px !default; -$dropdown-padding: 25px !default; -$dropdown-background: $global-background !default; -$dropdown-color: $global-color !default; -$dropdown-margin: $global-small-margin !default; -$dropdown-nav-item-color: $global-muted-color !default; -$dropdown-nav-item-hover-color: $global-color !default; -$dropdown-nav-header-color: $global-emphasis-color !default; -$dropdown-nav-divider-border-width: $global-border-width !default; -$dropdown-nav-divider-border: $global-border !default; -$dropdown-nav-sublist-item-color: $global-muted-color !default; -$dropdown-nav-sublist-item-hover-color: $global-color !default; -$form-range-thumb-height: 15px !default; -$form-range-thumb-border-radius: 500px !default; -$form-range-thumb-background: $global-background !default; -$form-range-track-height: 3px !default; -$form-range-track-background: darken($global-muted-background, 5%) !default; -$form-range-track-focus-background: darken($global-muted-background, 15%) !default; -$form-height: $global-control-height !default; -$form-border-width: $global-border-width !default; -$form-line-height: $form-height - (2* $form-border-width) !default; -$form-padding-horizontal: 10px !default; -$form-padding-vertical: 4px !default; -$form-background: $global-background !default; -$form-color: $global-color !default; -$form-focus-background: $global-background !default; -$form-focus-color: $global-color !default; -$form-disabled-background: $global-muted-background !default; -$form-disabled-color: $global-muted-color !default; -$form-placeholder-color: $global-muted-color !default; -$form-small-height: $global-control-small-height !default; -$form-small-padding-horizontal: 8px !default; -$form-small-line-height: $form-small-height - (2* $form-border-width) !default; -$form-small-font-size: $global-small-font-size !default; -$form-large-height: $global-control-large-height !default; -$form-large-padding-horizontal: 12px !default; -$form-large-line-height: $form-large-height - (2* $form-border-width) !default; -$form-large-font-size: $global-medium-font-size !default; -$form-danger-color: $global-danger-background !default; -$form-success-color: $global-success-background !default; -$form-width-xsmall: 50px !default; -$form-width-small: 130px !default; -$form-width-medium: 200px !default; -$form-width-large: 500px !default; -$form-select-padding-right: 20px !default; -$form-select-icon-color: $global-color !default; -$form-select-disabled-icon-color: $global-muted-color !default; -$form-radio-size: 16px !default; -$form-radio-margin-top: -4px !default; -$form-radio-background: transparent !default; -$form-radio-checked-background: $global-primary-background !default; -$form-radio-checked-icon-color: $global-inverse-color !default; -$form-radio-checked-focus-background: darken($global-primary-background, 10%) !default; -$form-radio-disabled-background: $global-muted-background !default; -$form-radio-disabled-icon-color: $global-muted-color !default; -$form-legend-font-size: $global-large-font-size !default; -$form-legend-line-height: 1.4 !default; -$form-stacked-margin-bottom: 5px !default; -$form-horizontal-label-width: 200px !default; -$form-horizontal-label-margin-top: 7px !default; -$form-horizontal-controls-margin-left: 215px !default; -$form-horizontal-controls-text-padding-top: 7px !default; -$form-icon-width: $form-height !default; -$form-icon-font-size: $global-font-size !default; -$form-icon-color: $global-muted-color !default; -$form-icon-hover-color: $global-color !default; -$internal-form-select-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2224%22%20height%3D%2216%22%20viewBox%3D%220%200%2024%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22#000%22%20points%3D%2212%201%209%206%2015%206%22%20%2F%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22#000%22%20points%3D%2212%2013%209%208%2015%208%22%20%2F%3E%0A%3C%2Fsvg%3E%0A" !default; -$internal-form-radio-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22#000%22%20cx%3D%228%22%20cy%3D%228%22%20r%3D%222%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$internal-form-checkbox-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2211%22%20viewBox%3D%220%200%2014%2011%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22#000%22%20points%3D%2212%201%205%207.5%202%205%201%205.5%205%2010%2013%201.5%22%20%2F%3E%0A%3C%2Fsvg%3E%0A" !default; -$internal-form-checkbox-indeterminate-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22#000%22%20x%3D%223%22%20y%3D%228%22%20width%3D%2210%22%20height%3D%221%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$inverse-global-muted-background: rgba($global-inverse-color, 0.1) !default; -$inverse-form-background: $inverse-global-muted-background !default; -$inverse-form-color: $inverse-global-color !default; -$inverse-form-focus-background: $inverse-global-muted-background !default; -$inverse-form-focus-color: $inverse-global-color !default; -$inverse-form-placeholder-color: $inverse-global-muted-color !default; -$inverse-form-select-icon-color: $inverse-global-color !default; -$inverse-form-radio-background: darken($inverse-global-muted-background, 5%) !default; -$inverse-form-radio-checked-background: $inverse-global-primary-background !default; -$inverse-form-radio-checked-icon-color: $inverse-global-inverse-color !default; -$inverse-form-radio-checked-focus-background: darken($inverse-global-primary-background, 10%) !default; -$grid-gutter-horizontal: $global-gutter !default; -$grid-gutter-vertical: $grid-gutter-horizontal !default; -$grid-gutter-horizontal-l: $global-medium-gutter !default; -$grid-gutter-vertical-l: $grid-gutter-horizontal-l !default; -$grid-small-gutter-horizontal: $global-small-gutter !default; -$grid-small-gutter-vertical: $grid-small-gutter-horizontal !default; -$grid-medium-gutter-horizontal: $global-gutter !default; -$grid-medium-gutter-vertical: $grid-medium-gutter-horizontal !default; -$grid-large-gutter-horizontal: $global-medium-gutter !default; -$grid-large-gutter-vertical: $grid-large-gutter-horizontal !default; -$grid-large-gutter-horizontal-l: $global-large-gutter !default; -$grid-large-gutter-vertical-l: $grid-large-gutter-horizontal-l !default; -$grid-divider-border-width: $global-border-width !default; -$grid-divider-border: $global-border !default; -$inverse-grid-divider-border: $inverse-global-border !default; -$heading-primary-font-size: $global-xxlarge-font-size !default; -$heading-primary-line-height: 1.2 !default; -$heading-primary-font-size-m: 3.75rem !default; -$heading-primary-line-height-m: 1.1 !default; -$heading-hero-font-size: 4rem !default; -$heading-hero-line-height: 1.1 !default; -$heading-hero-font-size-s: 6rem !default; -$heading-hero-line-height-s: 1 !default; -$heading-hero-font-size-m: 8rem !default; -$heading-hero-line-height-m: 1 !default; -$heading-divider-padding-bottom: 10px !default; -$heading-divider-border-width: $global-border-width !default; -$heading-divider-border: $global-border !default; -$heading-bullet-top: unquote('calc(-0.1 * 1em)') !default; -$heading-bullet-height: 0.9em !default; -$heading-bullet-margin-right: 10px !default; -$heading-bullet-border-width: 5px !default; -$heading-bullet-border: $global-border !default; -$heading-line-top: 50% !default; -$heading-line-border-width: $global-border-width !default; -$heading-line-height: $heading-line-border-width !default; -$heading-line-width: 2000px !default; -$heading-line-border: $global-border !default; -$heading-line-margin-horizontal: 0.6em !default; -$inverse-heading-divider-border: $inverse-global-border !default; -$inverse-heading-bullet-border: $inverse-global-border !default; -$inverse-heading-line-border: $inverse-global-border !default; -$icon-image-size: 20px !default; -$icon-link-color: $global-muted-color !default; -$icon-link-hover-color: $global-color !default; -$icon-link-active-color: darken($global-color, 5%) !default; -$icon-button-size: 36px !default; -$icon-button-border-radius: 500px !default; -$icon-button-background: $global-muted-background !default; -$icon-button-color: $global-muted-color !default; -$icon-button-hover-background: darken($icon-button-background, 5%) !default; -$icon-button-hover-color: $global-color !default; -$icon-button-active-background: darken($icon-button-background, 10%) !default; -$icon-button-active-color: $global-color !default; -$inverse-icon-link-color: $inverse-global-muted-color !default; -$inverse-icon-link-hover-color: $inverse-global-color !default; -$inverse-icon-link-active-color: $inverse-global-color !default; -$inverse-icon-button-background: $inverse-global-muted-background !default; -$inverse-icon-button-color: $inverse-global-muted-color !default; -$inverse-icon-button-hover-background: darken($inverse-icon-button-background, 5%) !default; -$inverse-icon-button-hover-color: $inverse-global-color !default; -$inverse-icon-button-active-background: darken($inverse-icon-button-background, 10%) !default; -$inverse-icon-button-active-color: $inverse-global-color !default; -$iconnav-margin-horizontal: $global-small-margin !default; -$iconnav-margin-vertical: $iconnav-margin-horizontal !default; -$iconnav-item-color: $global-muted-color !default; -$iconnav-item-hover-color: $global-color !default; -$iconnav-item-active-color: $global-color !default; -$inverse-iconnav-item-color: $inverse-global-muted-color !default; -$inverse-iconnav-item-hover-color: $inverse-global-color !default; -$inverse-iconnav-item-active-color: $inverse-global-color !default; -$inverse-global-color-mode: light !default; -$label-padding-vertical: 0 !default; -$label-padding-horizontal: $global-small-margin !default; -$label-background: $global-primary-background !default; -$label-line-height: $global-line-height !default; -$label-font-size: $global-small-font-size !default; -$label-color: $global-inverse-color !default; -$label-success-background: $global-success-background !default; -$label-success-color: $global-inverse-color !default; -$label-warning-background: $global-warning-background !default; -$label-warning-color: $global-inverse-color !default; -$label-danger-background: $global-danger-background !default; -$label-danger-color: $global-inverse-color !default; -$inverse-label-background: $inverse-global-primary-background !default; -$inverse-label-color: $inverse-global-inverse-color !default; -$lightbox-z-index: $global-z-index + 10 !default; -$lightbox-background: #000 !default; -$lightbox-item-color: rgba(255,255,255,0.7) !default; -$lightbox-toolbar-padding-vertical: 10px !default; -$lightbox-toolbar-padding-horizontal: 10px !default; -$lightbox-toolbar-background: rgba(0,0,0,0.3) !default; -$lightbox-toolbar-color: rgba(255,255,255,0.7) !default; -$lightbox-toolbar-icon-padding: 5px !default; -$lightbox-toolbar-icon-color: rgba(255,255,255,0.7) !default; -$lightbox-toolbar-icon-hover-color: #fff !default; -$lightbox-button-size: 50px !default; -$lightbox-button-background: $lightbox-toolbar-background !default; -$lightbox-button-color: rgba(255,255,255,0.7) !default; -$lightbox-button-hover-color: #fff !default; -$link-muted-color: $global-muted-color !default; -$link-muted-hover-color: $global-color !default; -$link-text-hover-color: $global-muted-color !default; -$link-heading-hover-color: $global-primary-background !default; -$link-heading-hover-text-decoration: none !default; -$inverse-link-muted-color: $inverse-global-muted-color !default; -$inverse-link-muted-hover-color: $inverse-global-color !default; -$inverse-link-text-hover-color: $inverse-global-muted-color !default; -$inverse-link-heading-hover-color: $inverse-global-primary-background !default; -$list-margin-top: $global-small-margin !default; -$list-nested-padding-left: $global-gutter !default; -$list-divider-margin-top: $global-small-margin !default; -$list-divider-border-width: $global-border-width !default; -$list-divider-border: $global-border !default; -$list-striped-padding-vertical: $global-small-margin !default; -$list-striped-padding-horizontal: $global-small-margin !default; -$list-striped-background: $global-muted-background !default; -$list-bullet-width: ($global-line-height * 1em) !default; -$list-bullet-height: $list-bullet-width !default; -$list-bullet-margin-right: $global-small-margin !default; -$list-bullet-icon-color: $global-color !default; -$list-large-margin-top: $global-margin !default; -$list-large-divider-margin-top: $global-margin !default; -$list-large-striped-padding-vertical: $global-margin !default; -$list-large-striped-padding-horizontal: $global-small-margin !default; -$internal-list-bullet-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%226%22%20height%3D%226%22%20viewBox%3D%220%200%206%206%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22#000%22%20cx%3D%223%22%20cy%3D%223%22%20r%3D%223%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$inverse-list-divider-border: $inverse-global-border !default; -$inverse-list-striped-background: $inverse-global-muted-background !default; -$inverse-list-bullet-icon-color: $inverse-global-color !default; -$margin-margin: $global-margin !default; -$margin-small-margin: $global-small-margin !default; -$margin-medium-margin: $global-medium-margin !default; -$margin-large-margin: $global-medium-margin !default; -$margin-large-margin-l: $global-large-margin !default; -$margin-xlarge-margin: $global-large-margin !default; -$global-xlarge-margin: 140px !default; -$margin-xlarge-margin-l: $global-xlarge-margin !default; -$marker-padding: 5px !default; -$marker-background: $global-secondary-background !default; -$marker-color: $global-inverse-color !default; -$marker-hover-color: $global-inverse-color !default; -$inverse-marker-background: $global-muted-background !default; -$inverse-marker-color: $global-color !default; -$inverse-marker-hover-color: $global-color !default; -$modal-z-index: $global-z-index + 10 !default; -$modal-background: rgba(0,0,0,0.6) !default; -$modal-padding-horizontal: 15px !default; -$modal-padding-horizontal-s: $global-gutter !default; -$modal-padding-horizontal-m: $global-medium-gutter !default; -$modal-padding-vertical: $modal-padding-horizontal !default; -$modal-padding-vertical-s: 50px !default; -$modal-dialog-width: 600px !default; -$modal-dialog-background: $global-background !default; -$modal-container-width: 1200px !default; -$modal-body-padding-horizontal: $global-gutter !default; -$modal-body-padding-vertical: $global-gutter !default; -$modal-header-padding-horizontal: $global-gutter !default; -$modal-header-padding-vertical: ($modal-header-padding-horizontal / 2) !default; -$modal-header-background: $modal-dialog-background !default; -$modal-footer-padding-horizontal: $global-gutter !default; -$modal-footer-padding-vertical: ($modal-footer-padding-horizontal / 2) !default; -$modal-footer-background: $modal-dialog-background !default; -$modal-title-font-size: $global-xlarge-font-size !default; -$modal-title-line-height: 1.3 !default; -$modal-close-position: $global-small-margin !default; -$modal-close-padding: 5px !default; -$modal-close-outside-position: 0 !default; -$modal-close-outside-translate: 100% !default; -$modal-close-outside-color: lighten($global-inverse-color, 20%) !default; -$modal-close-outside-hover-color: $global-inverse-color !default; -$nav-item-padding-vertical: 5px !default; -$nav-item-padding-horizontal: 0 !default; -$nav-sublist-padding-vertical: 5px !default; -$nav-sublist-padding-left: 15px !default; -$nav-sublist-deeper-padding-left: 15px !default; -$nav-sublist-item-padding-vertical: 2px !default; -$nav-parent-icon-width: ($global-line-height * 1em) !default; -$nav-parent-icon-height: $nav-parent-icon-width !default; -$nav-parent-icon-color: $global-color !default; -$nav-header-padding-vertical: $nav-item-padding-vertical !default; -$nav-header-padding-horizontal: $nav-item-padding-horizontal !default; -$nav-header-font-size: $global-small-font-size !default; -$nav-header-text-transform: uppercase !default; -$nav-header-margin-top: $global-margin !default; -$nav-divider-margin-vertical: 5px !default; -$nav-divider-margin-horizontal: 0 !default; -$nav-default-item-color: $global-muted-color !default; -$nav-default-item-hover-color: $global-color !default; -$nav-default-item-active-color: $global-emphasis-color !default; -$nav-default-header-color: $global-emphasis-color !default; -$nav-default-divider-border-width: $global-border-width !default; -$nav-default-divider-border: $global-border !default; -$nav-default-sublist-item-color: $global-muted-color !default; -$nav-default-sublist-item-hover-color: $global-color !default; -$nav-primary-item-font-size: $global-large-font-size !default; -$nav-primary-item-line-height: $global-line-height !default; -$nav-primary-item-color: $global-muted-color !default; -$nav-primary-item-hover-color: $global-color !default; -$nav-primary-item-active-color: $global-emphasis-color !default; -$nav-primary-header-color: $global-emphasis-color !default; -$nav-primary-divider-border-width: $global-border-width !default; -$nav-primary-divider-border: $global-border !default; -$nav-primary-sublist-item-color: $global-muted-color !default; -$nav-primary-sublist-item-hover-color: $global-color !default; -$internal-nav-parent-close-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolyline%20fill%3D%22none%22%20stroke%3D%22#000%22%20stroke-width%3D%221.1%22%20points%3D%2210%201%204%207%2010%2013%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$internal-nav-parent-open-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolyline%20fill%3D%22none%22%20stroke%3D%22#000%22%20stroke-width%3D%221.1%22%20points%3D%221%204%207%2010%2013%204%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$inverse-nav-parent-icon-color: $inverse-global-color !default; -$inverse-nav-default-item-color: $inverse-global-muted-color !default; -$inverse-nav-default-item-hover-color: $inverse-global-color !default; -$inverse-nav-default-item-active-color: $inverse-global-emphasis-color !default; -$inverse-nav-default-header-color: $inverse-global-emphasis-color !default; -$inverse-nav-default-divider-border: $inverse-global-border !default; -$inverse-nav-default-sublist-item-color: $inverse-global-muted-color !default; -$inverse-nav-default-sublist-item-hover-color: $inverse-global-color !default; -$inverse-nav-primary-item-color: $inverse-global-muted-color !default; -$inverse-nav-primary-item-hover-color: $inverse-global-color !default; -$inverse-nav-primary-item-active-color: $inverse-global-emphasis-color !default; -$inverse-nav-primary-header-color: $inverse-global-emphasis-color !default; -$inverse-nav-primary-divider-border: $inverse-global-border !default; -$inverse-nav-primary-sublist-item-color: $inverse-global-muted-color !default; -$inverse-nav-primary-sublist-item-hover-color: $inverse-global-color !default; -$navbar-background: $global-muted-background !default; -$navbar-color-mode: none !default; -$navbar-nav-item-height: 80px !default; -$navbar-nav-item-padding-horizontal: 15px !default; -$navbar-nav-item-color: $global-muted-color !default; -$navbar-nav-item-font-size: $global-small-font-size !default; -$navbar-nav-item-font-family: $global-font-family !default; -$navbar-nav-item-hover-color: $global-color !default; -$navbar-nav-item-onclick-color: $global-emphasis-color !default; -$navbar-nav-item-active-color: $global-emphasis-color !default; -$navbar-item-color: $global-color !default; -$navbar-toggle-color: $global-muted-color !default; -$navbar-toggle-hover-color: $global-color !default; -$navbar-subtitle-font-size: $global-small-font-size !default; -$navbar-dropdown-z-index: $global-z-index + 20 !default; -$navbar-dropdown-width: 200px !default; -$navbar-dropdown-margin: 15px !default; -$navbar-dropdown-padding: 25px !default; -$navbar-dropdown-background: $global-background !default; -$navbar-dropdown-color: $global-color !default; -$navbar-dropdown-grid-gutter-horizontal: ($navbar-dropdown-padding * 2) !default; -$navbar-dropdown-grid-gutter-vertical: $navbar-dropdown-grid-gutter-horizontal !default; -$navbar-dropdown-dropbar-margin-top: 0 !default; -$navbar-dropdown-dropbar-margin-bottom: $navbar-dropdown-dropbar-margin-top !default; -$navbar-dropdown-nav-item-color: $global-muted-color !default; -$navbar-dropdown-nav-item-hover-color: $global-color !default; -$navbar-dropdown-nav-item-active-color: $global-emphasis-color !default; -$navbar-dropdown-nav-header-color: $global-emphasis-color !default; -$navbar-dropdown-nav-divider-border-width: $global-border-width !default; -$navbar-dropdown-nav-divider-border: $global-border !default; -$navbar-dropdown-nav-sublist-item-color: $global-muted-color !default; -$navbar-dropdown-nav-sublist-item-hover-color: $global-color !default; -$navbar-dropbar-background: $navbar-dropdown-background !default; -$navbar-dropbar-z-index: $global-z-index - 20 !default; -$inverse-navbar-nav-item-color: $inverse-global-muted-color !default; -$inverse-navbar-nav-item-hover-color: $inverse-global-color !default; -$inverse-navbar-nav-item-onclick-color: $inverse-global-emphasis-color !default; -$inverse-navbar-nav-item-active-color: $inverse-global-emphasis-color !default; -$inverse-navbar-item-color: $inverse-global-color !default; -$inverse-navbar-toggle-color: $inverse-global-muted-color !default; -$inverse-navbar-toggle-hover-color: $inverse-global-color !default; -$notification-position: 10px !default; -$notification-z-index: $global-z-index + 40 !default; -$notification-width: 350px !default; -$notification-message-margin-bottom: 10px !default; -$notification-message-padding: $global-small-gutter !default; -$notification-message-background: $global-muted-background !default; -$notification-message-color: $global-color !default; -$notification-message-font-size: $global-medium-font-size !default; -$notification-message-line-height: 1.4 !default; -$notification-close-top: $notification-message-padding + 5px !default; -$notification-close-right: $notification-message-padding !default; -$notification-message-primary-color: $global-primary-background !default; -$notification-message-success-color: $global-success-background !default; -$notification-message-warning-color: $global-warning-background !default; -$notification-message-danger-color: $global-danger-background !default; -$offcanvas-z-index: $global-z-index !default; -$offcanvas-bar-width: 270px !default; -$offcanvas-bar-padding-vertical: $global-margin !default; -$offcanvas-bar-padding-horizontal: $global-margin !default; -$offcanvas-bar-background: $global-secondary-background !default; -$offcanvas-bar-color-mode: light !default; -$offcanvas-bar-width-m: 350px !default; -$offcanvas-bar-padding-vertical-m: $global-medium-gutter !default; -$offcanvas-bar-padding-horizontal-m: $global-medium-gutter !default; -$offcanvas-close-position: 20px !default; -$offcanvas-close-padding: 5px !default; -$offcanvas-overlay-background: rgba(0,0,0,0.1) !default; -$overlay-padding-horizontal: $global-gutter !default; -$overlay-padding-vertical: $global-gutter !default; -$overlay-default-background: rgba($global-background, 0.8) !default; -$overlay-primary-background: rgba($global-secondary-background, 0.8) !default; -$overlay-primary-color-mode: light !default; -$padding-padding: $global-gutter !default; -$padding-padding-l: $global-medium-gutter !default; -$padding-small-padding: $global-small-gutter !default; -$padding-large-padding: $global-gutter !default; -$padding-large-padding-l: $global-large-gutter !default; -$pagination-margin-horizontal: 20px !default; -$pagination-item-color: $global-muted-color !default; -$pagination-item-hover-color: $global-color !default; -$pagination-item-hover-text-decoration: none !default; -$pagination-item-active-color: $global-color !default; -$pagination-item-disabled-color: $global-muted-color !default; -$inverse-pagination-item-color: $inverse-global-muted-color !default; -$inverse-pagination-item-hover-color: $inverse-global-color !default; -$inverse-pagination-item-active-color: $inverse-global-color !default; -$inverse-pagination-item-disabled-color: $inverse-global-muted-color !default; -$placeholder-margin-vertical: $global-margin !default; -$placeholder-padding-vertical: $global-gutter !default; -$placeholder-padding-horizontal: $global-gutter !default; -$placeholder-background: transparent !default; -$position-small-margin: $global-small-gutter !default; -$position-medium-margin: $global-gutter !default; -$position-large-margin: $global-gutter !default; -$position-large-margin-l: 50px !default; -$progress-height: 15px !default; -$progress-margin-vertical: $global-margin !default; -$progress-background: $global-muted-background !default; -$progress-bar-background: $global-primary-background !default; -$search-color: $global-color !default; -$search-placeholder-color: $global-muted-color !default; -$search-icon-color: $global-muted-color !default; -$search-default-width: 180px !default; -$search-default-height: $global-control-height !default; -$search-default-padding-horizontal: 6px !default; -$search-default-background: transparent !default; -$search-default-focus-background: $search-default-background !default; -$search-default-icon-width: $global-control-height !default; -$search-navbar-width: 400px !default; -$search-navbar-height: 40px !default; -$search-navbar-background: transparent !default; -$search-navbar-font-size: $global-large-font-size !default; -$search-navbar-icon-width: 40px !default; -$search-large-width: 500px !default; -$search-large-height: 80px !default; -$search-large-background: transparent !default; -$search-large-font-size: $global-xxlarge-font-size !default; -$search-large-icon-width: 80px !default; -$search-toggle-color: $global-muted-color !default; -$search-toggle-hover-color: $global-color !default; -$inverse-search-color: $inverse-global-color !default; -$inverse-search-placeholder-color: $inverse-global-muted-color !default; -$inverse-search-icon-color: $inverse-global-muted-color !default; -$inverse-search-default-background: transparent !default; -$inverse-search-default-focus-background: $inverse-search-default-background !default; -$inverse-search-navbar-background: transparent !default; -$inverse-search-large-background: transparent !default; -$inverse-search-toggle-color: $inverse-global-muted-color !default; -$inverse-search-toggle-hover-color: $inverse-global-color !default; -$section-padding-vertical: $global-medium-margin !default; -$section-padding-vertical-m: $global-large-margin !default; -$section-xsmall-padding-vertical: $global-margin !default; -$section-small-padding-vertical: $global-medium-margin !default; -$section-large-padding-vertical: $global-large-margin !default; -$section-large-padding-vertical-m: $global-xlarge-margin !default; -$section-xlarge-padding-vertical: $global-xlarge-margin !default; -$section-xlarge-padding-vertical-m: ($global-large-margin + $global-xlarge-margin) !default; -$section-default-background: $global-background !default; -$section-muted-background: $global-muted-background !default; -$section-primary-background: $global-primary-background !default; -$section-primary-color-mode: light !default; -$section-secondary-background: $global-secondary-background !default; -$section-secondary-color-mode: light !default; -$slidenav-padding-vertical: 5px !default; -$slidenav-padding-horizontal: 10px !default; -$slidenav-color: rgba($global-color, 0.5) !default; -$slidenav-hover-color: rgba($global-color, 0.9) !default; -$slidenav-active-color: rgba($global-color, 0.5) !default; -$slidenav-large-padding-vertical: 10px !default; -$slidenav-large-padding-horizontal: $slidenav-large-padding-vertical !default; -$inverse-slidenav-color: rgba($inverse-global-color, 0.7) !default; -$inverse-slidenav-hover-color: rgba($inverse-global-color, 0.95) !default; -$inverse-slidenav-active-color: rgba($inverse-global-color, 0.7) !default; -$sortable-dragged-z-index: $global-z-index + 50 !default; -$sortable-placeholder-opacity: 0 !default; -$sortable-empty-height: 50px !default; -$spinner-size: 30px !default; -$spinner-stroke-width: 1 !default; -$spinner-radius: floor(($spinner-size - $spinner-stroke-width) / 2) !default; -$spinner-circumference: round(2 * 3.141 * $spinner-radius) !default; -$spinner-duration: 1.4s !default; -$sticky-z-index: $global-z-index - 20 !default; -$sticky-animation-duration: 0.2s !default; -$sticky-reverse-animation-duration: 0.2s !default; -$subnav-margin-horizontal: 20px !default; -$subnav-item-color: $global-muted-color !default; -$subnav-item-hover-color: $global-color !default; -$subnav-item-hover-text-decoration: none !default; -$subnav-item-active-color: $global-emphasis-color !default; -$subnav-divider-margin-horizontal: $subnav-margin-horizontal !default; -$subnav-divider-border-height: 1.5em !default; -$subnav-divider-border-width: $global-border-width !default; -$subnav-divider-border: $global-border !default; -$subnav-pill-item-padding-vertical: 5px !default; -$subnav-pill-item-padding-horizontal: 10px !default; -$subnav-pill-item-background: transparent !default; -$subnav-pill-item-color: $subnav-item-color !default; -$subnav-pill-item-hover-background: $global-muted-background !default; -$subnav-pill-item-hover-color: $global-color !default; -$subnav-pill-item-onclick-background: $subnav-pill-item-hover-background !default; -$subnav-pill-item-onclick-color: $subnav-pill-item-hover-color !default; -$subnav-pill-item-active-background: $global-primary-background !default; -$subnav-pill-item-active-color: $global-inverse-color !default; -$subnav-item-disabled-color: $global-muted-color !default; -$inverse-subnav-item-color: $inverse-global-muted-color !default; -$inverse-subnav-item-hover-color: $inverse-global-color !default; -$inverse-subnav-item-active-color: $inverse-global-emphasis-color !default; -$inverse-subnav-divider-border: $inverse-global-border !default; -$inverse-subnav-pill-item-background: transparent !default; -$inverse-subnav-pill-item-color: $inverse-global-muted-color !default; -$inverse-subnav-pill-item-hover-background: $inverse-global-muted-background !default; -$inverse-subnav-pill-item-hover-color: $inverse-global-color !default; -$inverse-subnav-pill-item-onclick-background: $inverse-subnav-pill-item-hover-background !default; -$inverse-subnav-pill-item-onclick-color: $inverse-subnav-pill-item-hover-color !default; -$inverse-subnav-pill-item-active-background: $inverse-global-primary-background !default; -$inverse-subnav-pill-item-active-color: $inverse-global-inverse-color !default; -$inverse-subnav-item-disabled-color: $inverse-global-muted-color !default; -$tab-margin-horizontal: 20px !default; -$tab-item-padding-horizontal: 10px !default; -$tab-item-padding-vertical: 5px !default; -$tab-item-color: $global-muted-color !default; -$tab-item-hover-color: $global-color !default; -$tab-item-hover-text-decoration: none !default; -$tab-item-active-color: $global-emphasis-color !default; -$tab-item-disabled-color: $global-muted-color !default; -$inverse-tab-item-color: $inverse-global-muted-color !default; -$inverse-tab-item-hover-color: $inverse-global-color !default; -$inverse-tab-item-active-color: $inverse-global-emphasis-color !default; -$inverse-tab-item-disabled-color: $inverse-global-muted-color !default; -$table-margin-vertical: $global-margin !default; -$table-cell-padding-vertical: 16px !default; -$table-cell-padding-horizontal: 12px !default; -$table-header-cell-font-size: $global-small-font-size !default; -$table-header-cell-font-weight: normal !default; -$table-header-cell-color: $global-muted-color !default; -$table-footer-font-size: $global-small-font-size !default; -$table-caption-font-size: $global-small-font-size !default; -$table-caption-color: $global-muted-color !default; -$table-row-active-background: #ffd !default; -$table-divider-border-width: $global-border-width !default; -$table-divider-border: $global-border !default; -$table-striped-row-background: $global-muted-background !default; -$table-hover-row-background: $table-row-active-background !default; -$table-small-cell-padding-vertical: 10px !default; -$table-small-cell-padding-horizontal: 12px !default; -$table-large-cell-padding-vertical: 22px !default; -$table-large-cell-padding-horizontal: 12px !default; -$table-expand-min-width: 150px !default; -$inverse-table-header-cell-color: $inverse-global-color !default; -$inverse-table-caption-color: $inverse-global-muted-color !default; -$inverse-table-row-active-background: fade-out($inverse-global-muted-background, 0.02) !default; -$inverse-table-divider-border: $inverse-global-border !default; -$inverse-table-striped-row-background: $inverse-global-muted-background !default; -$inverse-table-hover-row-background: $inverse-table-row-active-background !default; -$text-lead-font-size: $global-large-font-size !default; -$text-lead-line-height: 1.5 !default; -$text-lead-color: $global-emphasis-color !default; -$text-meta-font-size: $global-small-font-size !default; -$text-meta-line-height: 1.4 !default; -$text-meta-color: $global-muted-color !default; -$text-small-font-size: $global-small-font-size !default; -$text-small-line-height: 1.5 !default; -$text-large-font-size: $global-large-font-size !default; -$text-large-line-height: 1.5 !default; -$text-bold-font-weight: bolder !default; -$text-muted-color: $global-muted-color !default; -$text-primary-color: $global-primary-background !default; -$text-success-color: $global-success-background !default; -$text-warning-color: $global-warning-background !default; -$text-danger-color: $global-danger-background !default; -$text-background-color: $global-primary-background !default; -$inverse-text-lead-color: $inverse-global-color !default; -$inverse-text-meta-color: $inverse-global-muted-color !default; -$inverse-text-muted-color: $inverse-global-muted-color !default; -$inverse-text-primary-color: $inverse-global-color !default; -$thumbnav-margin-horizontal: 15px !default; -$thumbnav-margin-vertical: $thumbnav-margin-horizontal !default; -$tile-padding-horizontal: 15px !default; -$tile-padding-horizontal-s: $global-gutter !default; -$tile-padding-horizontal-m: $global-medium-gutter !default; -$tile-padding-vertical: $global-medium-margin !default; -$tile-padding-vertical-m: $global-large-margin !default; -$tile-xsmall-padding-vertical: $global-margin !default; -$tile-small-padding-vertical: $global-medium-margin !default; -$tile-large-padding-vertical: $global-large-margin !default; -$tile-large-padding-vertical-m: $global-xlarge-margin !default; -$tile-xlarge-padding-vertical: $global-xlarge-margin !default; -$tile-xlarge-padding-vertical-m: ($global-large-margin + $global-xlarge-margin) !default; -$tile-default-background: $global-background !default; -$tile-muted-background: $global-muted-background !default; -$tile-primary-background: $global-primary-background !default; -$tile-primary-color-mode: light !default; -$tile-secondary-background: $global-secondary-background !default; -$tile-secondary-color-mode: light !default; -$tooltip-z-index: $global-z-index + 30 !default; -$tooltip-max-width: 200px !default; -$tooltip-padding-vertical: 3px !default; -$tooltip-padding-horizontal: 6px !default; -$tooltip-background: #666 !default; -$tooltip-border-radius: 2px !default; -$tooltip-color: $global-inverse-color !default; -$tooltip-font-size: 12px !default; -$tooltip-margin: 10px !default; -$totop-padding: 5px !default; -$totop-color: $global-muted-color !default; -$totop-hover-color: $global-color !default; -$totop-active-color: $global-emphasis-color !default; -$inverse-totop-color: $inverse-global-muted-color !default; -$inverse-totop-hover-color: $inverse-global-color !default; -$inverse-totop-active-color: $inverse-global-emphasis-color !default; -$transition-duration: 0.3s !default; -$transition-scale: 1.1 !default; -$transition-slide-small-translate: 10px !default; -$transition-slide-medium-translate: 50px !default; -$transition-slow-duration: 0.7s !default; -$panel-scrollable-height: 170px !default; -$panel-scrollable-padding: 10px !default; -$panel-scrollable-border-width: $global-border-width !default; -$panel-scrollable-border: $global-border !default; -$height-small-height: 150px !default; -$height-medium-height: 300px !default; -$height-large-height: 450px !default; -$border-rounded-border-radius: 5px !default; -$box-shadow-duration: 0.1s !default; -$box-shadow-bottom-height: 30px !default; -$box-shadow-bottom-border-radius: 100% !default; -$box-shadow-bottom-background: #444 !default; -$box-shadow-bottom-blur: 20px !default; -$dropcap-margin-right: 10px !default; -$dropcap-font-size: (($global-line-height * 3) * 1em) !default; -$leader-fill-content: '.' !default; -$leader-fill-margin-left: $global-small-gutter !default; -$logo-font-size: $global-large-font-size !default; -$logo-font-family: $global-font-family !default; -$logo-color: $global-color !default; -$logo-hover-color: $global-color !default; -$dragover-box-shadow: 0 0 20px rgba(100,100,100,0.3) !default; -$inverse-logo-color: $inverse-global-color !default; -$inverse-logo-hover-color: $inverse-global-color !default; -$breakpoint-small: 640px !default; -$breakpoint-medium: 960px !default; -$breakpoint-large: 1200px !default; -$breakpoint-xlarge: 1600px !default; -$breakpoint-xsmall-max: ($breakpoint-small - 1) !default; -$breakpoint-small-max: ($breakpoint-medium - 1) !default; -$breakpoint-medium-max: ($breakpoint-large - 1) !default; -$breakpoint-large-max: ($breakpoint-xlarge - 1) !default; -$global-small-box-shadow: 0 2px 8px rgba(0,0,0,0.08) !default; -$global-medium-box-shadow: 0 5px 15px rgba(0,0,0,0.08) !default; -$global-large-box-shadow: 0 14px 25px rgba(0,0,0,0.16) !default; -$global-xlarge-box-shadow: 0 28px 50px rgba(0,0,0,0.16) !default; -$width-small-width: 150px !default; -$width-medium-width: 300px !default; -$width-large-width: 450px !default; -$width-xlarge-width: 600px !default; -$width-xxlarge-width: 750px !default; -$accordion-icon-color: $global-color !default; -$internal-accordion-open-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2213%22%20height%3D%2213%22%20viewBox%3D%220%200%2013%2013%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22#000%22%20width%3D%2213%22%20height%3D%221%22%20x%3D%220%22%20y%3D%226%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$internal-accordion-close-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2213%22%20height%3D%2213%22%20viewBox%3D%220%200%2013%2013%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22#000%22%20width%3D%2213%22%20height%3D%221%22%20x%3D%220%22%20y%3D%226%22%20%2F%3E%0A%20%20%20%20%3Crect%20fill%3D%22#000%22%20width%3D%221%22%20height%3D%2213%22%20x%3D%226%22%20y%3D%220%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$alert-close-opacity: 0.4 !default; -$alert-close-hover-opacity: 0.8 !default; -$article-meta-link-color: $article-meta-color !default; -$article-meta-link-hover-color: $global-color !default; -$base-code-padding-horizontal: 6px !default; -$base-code-padding-vertical: 2px !default; -$base-code-background: $global-muted-background !default; -$base-blockquote-color: $global-emphasis-color !default; -$base-blockquote-footer-color: $global-color !default; -$base-pre-padding: 10px !default; -$base-pre-background: $global-background !default; -$base-pre-border-width: $global-border-width !default; -$base-pre-border: $global-border !default; -$base-pre-border-radius: 3px !default; -$inverse-base-blockquote-color: $inverse-global-emphasis-color !default; -$inverse-base-blockquote-footer-color: $inverse-global-color !default; -$button-text-transform: uppercase !default; -$button-default-border: $global-border !default; -$button-default-hover-border: darken($global-border, 20%) !default; -$button-default-active-border: darken($global-border, 30%) !default; -$button-disabled-border: $global-border !default; -$button-text-border-width: $global-border-width !default; -$button-text-border: $button-text-hover-color !default; -$card-hover-box-shadow: $global-large-box-shadow !default; -$card-default-box-shadow: $global-medium-box-shadow !default; -$card-default-hover-box-shadow: $global-large-box-shadow !default; -$card-default-header-border-width: $global-border-width !default; -$card-default-header-border: $global-border !default; -$card-default-footer-border-width: $global-border-width !default; -$card-default-footer-border: $global-border !default; -$card-primary-box-shadow: $global-medium-box-shadow !default; -$card-primary-hover-box-shadow: $global-large-box-shadow !default; -$card-secondary-box-shadow: $global-medium-box-shadow !default; -$card-secondary-hover-box-shadow: $global-large-box-shadow !default; -$comment-primary-padding: $global-gutter !default; -$comment-primary-background: $global-muted-background !default; -$description-list-term-font-size: $global-small-font-size !default; -$description-list-term-font-weight: normal !default; -$description-list-term-text-transform: uppercase !default; -$dotnav-item-border-width: 1px !default; -$dotnav-item-border: rgba($global-color, 0.4) !default; -$dotnav-item-hover-border: transparent !default; -$dotnav-item-onclick-border: transparent !default; -$dotnav-item-active-border: transparent !default; -$dropdown-nav-font-size: $global-small-font-size !default; -$dropdown-box-shadow: 0 5px 12px rgba(0,0,0,0.15) !default; -$form-range-thumb-border-width: $global-border-width !default; -$form-range-thumb-border: darken($global-border, 10%) !default; -$form-range-track-border-radius: 500px !default; -$form-border: $global-border !default; -$form-focus-border: $global-primary-background !default; -$form-disabled-border: $global-border !default; -$form-danger-border: $global-danger-background !default; -$form-success-border: $global-success-background !default; -$form-blank-focus-border: $global-border !default; -$form-blank-focus-border-style: dashed !default; -$form-radio-border-width: $global-border-width !default; -$form-radio-border: darken($global-border, 10%) !default; -$form-radio-focus-border: $global-primary-background !default; -$form-radio-checked-border: transparent !default; -$form-radio-disabled-border: $global-border !default; -$form-label-color: $global-emphasis-color !default; -$form-label-font-size: $global-small-font-size !default; -$inverse-form-label-color: $inverse-global-emphasis-color !default; -$label-border-radius: 2px !default; -$label-text-transform: uppercase !default; -$list-striped-border-width: $global-border-width !default; -$list-striped-border: $global-border !default; -$modal-header-border-width: $global-border-width !default; -$modal-header-border: $global-border !default; -$modal-footer-border-width: $global-border-width !default; -$modal-footer-border: $global-border !default; -$modal-close-full-padding: $global-margin !default; -$modal-close-full-background: $modal-dialog-background !default; -$nav-default-font-size: $global-small-font-size !default; -$navbar-nav-item-text-transform: uppercase !default; -$navbar-dropdown-nav-font-size: $global-small-font-size !default; -$navbar-dropdown-box-shadow: 0 5px 12px rgba(0,0,0,0.15) !default; -$navbar-dropbar-box-shadow: 0 5px 7px rgba(0, 0, 0, 0.05) !default; -$navbar-dropdown-grid-divider-border-width: $global-border-width !default; -$navbar-dropdown-grid-divider-border: $navbar-dropdown-nav-divider-border !default; -$placeholder-border-width: $global-border-width !default; -$placeholder-border: $global-border !default; -$progress-border-radius: 500px !default; -$search-default-border-width: $global-border-width !default; -$search-default-border: $global-border !default; -$subnav-item-font-size: $global-small-font-size !default; -$subnav-item-text-transform: uppercase !default; -$tab-border-width: $global-border-width !default; -$tab-border: $global-border !default; -$tab-item-border-width: $global-border-width !default; -$tab-item-font-size: $global-small-font-size !default; -$tab-item-text-transform: uppercase !default; -$tab-item-active-border: $global-primary-background !default; -$inverse-tab-border: $inverse-global-border !default; -$table-striped-border-width: $global-border-width !default; -$table-striped-border: $global-border !default; -$text-meta-link-color: $text-meta-color !default; -$text-meta-link-hover-color: $global-color !default; -$thumbnav-item-background: rgba($global-background, 0.4) !default; -$thumbnav-item-hover-background: transparent !default; -$thumbnav-item-active-background: transparent !default; \ No newline at end of file diff --git a/_sass/uikit/variables.scss b/_sass/uikit/variables.scss deleted file mode 100644 index 90b5afbb8a..0000000000 --- a/_sass/uikit/variables.scss +++ /dev/null @@ -1,986 +0,0 @@ -$global-margin: 20px !default; -$accordion-item-margin-top: $global-margin !default; -$global-medium-font-size: 1.25rem !default; -$accordion-title-font-size: $global-medium-font-size !default; -$accordion-title-line-height: 1.4 !default; -$global-emphasis-color: #333 !default; -$accordion-title-color: $global-emphasis-color !default; -$global-color: #666 !default; -$accordion-title-hover-color: $global-color !default; -$accordion-content-margin-top: $global-margin !default; -$global-inverse-color: #fff !default; -$inverse-global-emphasis-color: $global-inverse-color !default; -$inverse-accordion-title-color: $inverse-global-emphasis-color !default; -$inverse-global-inverse-color: $global-color !default; -$inverse-accordion-title-hover-color: $inverse-global-inverse-color !default; -$global-gutter: 30px !default; -$align-margin-horizontal: $global-gutter !default; -$align-margin-vertical: $global-gutter !default; -$global-medium-gutter: 40px !default; -$align-margin-horizontal-l: $global-medium-gutter !default; -$alert-margin-vertical: $global-margin !default; -$global-small-gutter: 15px !default; -$alert-padding: $global-small-gutter !default; -$alert-padding-right: $alert-padding + 14px !default; -$global-muted-background: #f8f8f8 !default; -$alert-background: $global-muted-background !default; -$alert-color: $global-color !default; -$alert-close-top: $alert-padding + 5px !default; -$alert-close-right: $alert-padding !default; -$global-primary-background: #1e87f0 !default; -$alert-primary-background: lighten(mix(white, $global-primary-background, 40%), 20%) !default; -$alert-primary-color: $global-primary-background !default; -$global-success-background: #32d296 !default; -$alert-success-background: lighten(mix(white, $global-success-background, 40%), 25%) !default; -$alert-success-color: $global-success-background !default; -$global-warning-background: #faa05a !default; -$alert-warning-background: lighten(mix(white, $global-warning-background, 45%), 15%) !default; -$alert-warning-color: $global-warning-background !default; -$global-danger-background: #f0506e !default; -$alert-danger-background: lighten(mix(white, $global-danger-background, 40%), 20%) !default; -$alert-danger-color: $global-danger-background !default; -$global-large-margin: 70px !default; -$article-margin-top: $global-large-margin !default; -$global-xxlarge-font-size: 2.625rem !default; -$article-title-font-size: $global-xxlarge-font-size !default; -$article-title-line-height: 1.2 !default; -$global-small-font-size: 0.875rem !default; -$article-meta-font-size: $global-small-font-size !default; -$article-meta-line-height: 1.4 !default; -$global-muted-color: #999 !default; -$article-meta-color: $global-muted-color !default; -$inverse-global-muted-color: rgba($global-inverse-color, 0.5) !default; -$inverse-article-meta-color: $inverse-global-muted-color !default; -$animation-duration: 0.5s !default; -$animation-fade-duration: 0.8s !default; -$animation-kenburns-duration: 15s !default; -$animation-fast-duration: 0.1s !default; -$animation-slide-small-translate: 10px !default; -$animation-slide-medium-translate: 50px !default; -$global-background: #fff !default; -$background-default-background: $global-background !default; -$background-muted-background: $global-muted-background !default; -$background-primary-background: $global-primary-background !default; -$global-secondary-background: #222 !default; -$background-secondary-background: $global-secondary-background !default; -$badge-size: 22px !default; -$badge-padding-vertical: 0 !default; -$badge-padding-horizontal: 5px !default; -$badge-border-radius: 500px !default; -$badge-background: $global-primary-background !default; -$badge-color: $global-inverse-color !default; -$badge-font-size: $global-small-font-size !default; -$badge-hover-color: $global-inverse-color !default; -$inverse-global-primary-background: $global-inverse-color !default; -$inverse-badge-background: $inverse-global-primary-background !default; -$inverse-badge-color: $inverse-global-inverse-color !default; -$inverse-badge-hover-color: $inverse-global-inverse-color !default; -$base-body-background: $global-background !default; -$global-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !default; -$base-body-font-family: $global-font-family !default; -$base-body-font-weight: normal !default; -$global-font-size: 16px !default; -$base-body-font-size: $global-font-size !default; -$global-line-height: 1.5 !default; -$base-body-line-height: $global-line-height !default; -$base-body-color: $global-color !default; -$global-link-color: #1e87f0 !default; -$base-link-color: $global-link-color !default; -$base-link-text-decoration: none !default; -$global-link-hover-color: #0f6ecd !default; -$base-link-hover-color: $global-link-hover-color !default; -$base-link-hover-text-decoration: underline !default; -$base-strong-font-weight: bolder !default; -$base-code-font-size: $global-small-font-size !default; -$base-code-font-family: Consolas, monaco, monospace !default; -$base-code-color: $global-danger-background !default; -$base-em-color: $global-danger-background !default; -$base-ins-background: #ffd !default; -$base-ins-color: $global-color !default; -$base-mark-background: #ffd !default; -$base-mark-color: $global-color !default; -$base-quote-font-style: italic !default; -$base-small-font-size: 80% !default; -$base-margin-vertical: $global-margin !default; -$base-heading-font-family: $global-font-family !default; -$base-heading-font-weight: normal !default; -$base-heading-color: $global-emphasis-color !default; -$base-heading-text-transform: none !default; -$global-medium-margin: 40px !default; -$base-heading-margin-top: $global-medium-margin !default; -$base-h1-font-size: $global-xxlarge-font-size !default; -$base-h1-line-height: 1.2 !default; -$global-xlarge-font-size: 2rem !default; -$base-h2-font-size: $global-xlarge-font-size !default; -$base-h2-line-height: 1.3 !default; -$global-large-font-size: 1.5rem !default; -$base-h3-font-size: $global-large-font-size !default; -$base-h3-line-height: 1.4 !default; -$base-h4-font-size: $global-medium-font-size !default; -$base-h4-line-height: 1.4 !default; -$base-h5-font-size: $global-font-size !default; -$base-h5-line-height: 1.4 !default; -$base-h6-font-size: $global-small-font-size !default; -$base-h6-line-height: 1.4 !default; -$base-list-padding-left: 30px !default; -$base-hr-margin-vertical: $global-margin !default; -$global-border-width: 1px !default; -$base-hr-border-width: $global-border-width !default; -$global-border: #e5e5e5 !default; -$base-hr-border: $global-border !default; -$base-blockquote-font-size: $global-medium-font-size !default; -$base-blockquote-line-height: 1.5 !default; -$base-blockquote-font-style: italic !default; -$base-blockquote-margin-vertical: $global-margin !default; -$global-small-margin: 10px !default; -$base-blockquote-footer-margin-top: $global-small-margin !default; -$base-blockquote-footer-font-size: $global-small-font-size !default; -$base-blockquote-footer-line-height: 1.5 !default; -$base-pre-font-size: $global-small-font-size !default; -$base-pre-line-height: 1.5 !default; -$base-pre-font-family: $base-code-font-family !default; -$base-pre-color: $global-color !default; -$base-selection-background: #39f !default; -$base-selection-color: $global-inverse-color !default; -$inverse-global-color: rgba($global-inverse-color, 0.7) !default; -$inverse-base-color: $inverse-global-color !default; -$inverse-base-link-color: $inverse-global-emphasis-color !default; -$inverse-base-link-hover-color: $inverse-global-emphasis-color !default; -$inverse-base-code-color: $inverse-global-color !default; -$inverse-base-em-color: $inverse-global-emphasis-color !default; -$inverse-base-heading-color: $inverse-global-emphasis-color !default; -$inverse-global-border: rgba($global-inverse-color, 0.2) !default; -$inverse-base-hr-border: $inverse-global-border !default; -$breadcrumb-item-font-size: $global-small-font-size !default; -$breadcrumb-item-color: $global-muted-color !default; -$breadcrumb-item-hover-color: $global-color !default; -$breadcrumb-item-hover-text-decoration: none !default; -$breadcrumb-item-active-color: $global-color !default; -$breadcrumb-divider: "/" !default; -$breadcrumb-divider-margin-horizontal: 20px !default; -$breadcrumb-divider-color: $global-muted-color !default; -$inverse-breadcrumb-item-color: $inverse-global-muted-color !default; -$inverse-breadcrumb-item-hover-color: $inverse-global-color !default; -$inverse-breadcrumb-item-active-color: $inverse-global-color !default; -$inverse-breadcrumb-divider-color: $inverse-global-muted-color !default; -$global-control-height: 40px !default; -$button-line-height: $global-control-height !default; -$global-control-small-height: 30px !default; -$button-small-line-height: $global-control-small-height !default; -$global-control-large-height: 55px !default; -$button-large-line-height: $global-control-large-height !default; -$button-font-size: $global-font-size !default; -$button-small-font-size: $global-small-font-size !default; -$button-large-font-size: $global-medium-font-size !default; -$button-padding-horizontal: $global-gutter !default; -$button-small-padding-horizontal: $global-small-gutter !default; -$button-large-padding-horizontal: $global-medium-gutter !default; -$button-default-background: $global-muted-background !default; -$button-default-color: $global-emphasis-color !default; -$button-default-hover-background: darken($button-default-background, 5%) !default; -$button-default-hover-color: $global-emphasis-color !default; -$button-default-active-background: darken($button-default-background, 10%) !default; -$button-default-active-color: $global-emphasis-color !default; -$button-primary-background: $global-primary-background !default; -$button-primary-color: $global-inverse-color !default; -$button-primary-hover-background: darken($button-primary-background, 5%) !default; -$button-primary-hover-color: $global-inverse-color !default; -$button-primary-active-background: darken($button-primary-background, 10%) !default; -$button-primary-active-color: $global-inverse-color !default; -$button-secondary-background: $global-secondary-background !default; -$button-secondary-color: $global-inverse-color !default; -$button-secondary-hover-background: darken($button-secondary-background, 5%) !default; -$button-secondary-hover-color: $global-inverse-color !default; -$button-secondary-active-background: darken($button-secondary-background, 10%) !default; -$button-secondary-active-color: $global-inverse-color !default; -$button-danger-background: $global-danger-background !default; -$button-danger-color: $global-inverse-color !default; -$button-danger-hover-background: darken($button-danger-background, 5%) !default; -$button-danger-hover-color: $global-inverse-color !default; -$button-danger-active-background: darken($button-danger-background, 10%) !default; -$button-danger-active-color: $global-inverse-color !default; -$button-disabled-background: $global-muted-background !default; -$button-disabled-color: $global-muted-color !default; -$button-text-line-height: $global-line-height !default; -$button-text-color: $global-muted-color !default; -$button-text-hover-color: $global-color !default; -$button-text-disabled-color: $global-muted-color !default; -$button-link-line-height: $global-line-height !default; -$button-link-color: $global-link-color !default; -$button-link-hover-color: $global-link-hover-color !default; -$button-link-hover-text-decoration: underline !default; -$button-link-disabled-color: $global-muted-color !default; -$inverse-button-default-background: $inverse-global-primary-background !default; -$inverse-button-default-color: $inverse-global-inverse-color !default; -$inverse-button-default-hover-background: darken($inverse-button-default-background, 5%) !default; -$inverse-button-default-hover-color: $inverse-global-inverse-color !default; -$inverse-button-default-active-background: darken($inverse-button-default-background, 10%) !default; -$inverse-button-default-active-color: $inverse-global-inverse-color !default; -$inverse-button-primary-background: $inverse-global-primary-background !default; -$inverse-button-primary-color: $inverse-global-inverse-color !default; -$inverse-button-primary-hover-background: darken($inverse-button-primary-background, 5%) !default; -$inverse-button-primary-hover-color: $inverse-global-inverse-color !default; -$inverse-button-primary-active-background: darken($inverse-button-primary-background, 10%) !default; -$inverse-button-primary-active-color: $inverse-global-inverse-color !default; -$inverse-button-secondary-background: $inverse-global-primary-background !default; -$inverse-button-secondary-color: $inverse-global-inverse-color !default; -$inverse-button-secondary-hover-background: darken($inverse-button-secondary-background, 5%) !default; -$inverse-button-secondary-hover-color: $inverse-global-inverse-color !default; -$inverse-button-secondary-active-background: darken($inverse-button-secondary-background, 10%) !default; -$inverse-button-secondary-active-color: $inverse-global-inverse-color !default; -$inverse-button-text-color: $inverse-global-muted-color !default; -$inverse-button-text-hover-color: $inverse-global-color !default; -$inverse-button-text-disabled-color: $inverse-global-muted-color !default; -$inverse-button-link-color: $inverse-global-muted-color !default; -$inverse-button-link-hover-color: $inverse-global-color !default; -$card-body-padding-horizontal: $global-gutter !default; -$card-body-padding-vertical: $global-gutter !default; -$card-body-padding-horizontal-l: $global-medium-gutter !default; -$card-body-padding-vertical-l: $global-medium-gutter !default; -$card-header-padding-horizontal: $global-gutter !default; -$card-header-padding-vertical: round($global-gutter / 2) !default; -$card-header-padding-horizontal-l: $global-medium-gutter !default; -$card-header-padding-vertical-l: round($global-medium-gutter / 2) !default; -$card-footer-padding-horizontal: $global-gutter !default; -$card-footer-padding-vertical: ($global-gutter / 2) !default; -$card-footer-padding-horizontal-l: $global-medium-gutter !default; -$card-footer-padding-vertical-l: round($global-medium-gutter / 2) !default; -$card-title-font-size: $global-large-font-size !default; -$card-title-line-height: 1.4 !default; -$card-badge-top: $global-gutter !default; -$card-badge-right: $card-badge-top !default; -$card-hover-background: $global-muted-background !default; -$card-default-background: $global-muted-background !default; -$card-default-color: $global-color !default; -$card-default-title-color: $global-emphasis-color !default; -$card-default-hover-background: darken($card-default-background, 5%) !default; -$card-primary-background: $global-primary-background !default; -$card-primary-color: $global-inverse-color !default; -$card-primary-title-color: $card-primary-color !default; -$card-primary-hover-background: darken($card-primary-background, 5%) !default; -$card-primary-color-mode: light !default; -$card-secondary-background: $global-secondary-background !default; -$card-secondary-color: $global-inverse-color !default; -$card-secondary-title-color: $card-secondary-color !default; -$card-secondary-hover-background: darken($card-secondary-background, 5%) !default; -$card-secondary-color-mode: light !default; -$card-small-body-padding-horizontal: $global-margin !default; -$card-small-body-padding-vertical: $global-margin !default; -$card-small-header-padding-horizontal: $global-margin !default; -$card-small-header-padding-vertical: round($global-margin / 1.5) !default; -$card-small-footer-padding-horizontal: $global-margin !default; -$card-small-footer-padding-vertical: round($global-margin / 1.5) !default; -$global-large-gutter: 70px !default; -$card-large-body-padding-horizontal-l: $global-large-gutter !default; -$card-large-body-padding-vertical-l: $global-large-gutter !default; -$card-large-header-padding-horizontal-l: $global-large-gutter !default; -$card-large-header-padding-vertical-l: round($global-large-gutter / 2) !default; -$card-large-footer-padding-horizontal-l: $global-large-gutter !default; -$card-large-footer-padding-vertical-l: round($global-large-gutter / 2) !default; -$close-color: $global-muted-color !default; -$close-hover-color: $global-color !default; -$inverse-close-color: $inverse-global-muted-color !default; -$inverse-close-hover-color: $inverse-global-color !default; -$column-gutter: $global-gutter !default; -$column-gutter-l: $global-medium-gutter !default; -$column-divider-rule-color: $global-border !default; -$column-divider-rule-width: 1px !default; -$inverse-column-divider-rule-color: $inverse-global-border !default; -$comment-header-margin-bottom: $global-margin !default; -$comment-title-font-size: $global-medium-font-size !default; -$comment-title-line-height: 1.4 !default; -$comment-meta-font-size: $global-small-font-size !default; -$comment-meta-line-height: 1.4 !default; -$comment-meta-color: $global-muted-color !default; -$comment-list-margin-top: $global-large-margin !default; -$comment-list-padding-left: 30px !default; -$comment-list-padding-left-m: 100px !default; -$container-max-width: 1200px !default; -$container-small-max-width: 900px !default; -$container-large-max-width: 1600px !default; -$container-padding-horizontal: 15px !default; -$container-padding-horizontal-s: $global-gutter !default; -$container-padding-horizontal-m: $global-medium-gutter !default; -$countdown-item-line-height: 70px !default; -$countdown-number-font-size: 2rem !default; -$countdown-number-font-size-s: 4rem !default; -$countdown-number-font-size-m: 6rem !default; -$countdown-separator-font-size: 1rem !default; -$countdown-separator-font-size-s: 2rem !default; -$countdown-separator-font-size-m: 3rem !default; -$description-list-term-color: $global-emphasis-color !default; -$description-list-term-margin-top: $global-margin !default; -$description-list-divider-term-margin-top: $global-margin !default; -$description-list-divider-term-border-width: $global-border-width !default; -$description-list-divider-term-border: $global-border !default; -$divider-margin-vertical: $global-margin !default; -$divider-icon-width: 50px !default; -$divider-icon-height: 20px !default; -$divider-icon-color: $global-border !default; -$divider-icon-line-top: 50% !default; -$divider-icon-line-width: 100% !default; -$divider-icon-line-border-width: $global-border-width !default; -$divider-icon-line-border: $global-border !default; -$internal-divider-icon-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22none%22%20stroke%3D%22#000%22%20stroke-width%3D%222%22%20cx%3D%2210%22%20cy%3D%2210%22%20r%3D%227%22%20%2F%3E%0A%3C%2Fsvg%3E%0A" !default; -$divider-small-width: 100px !default; -$divider-small-border-width: $global-border-width !default; -$divider-small-border: $global-border !default; -$inverse-divider-icon-color: $inverse-global-border !default; -$inverse-divider-icon-line-border: $inverse-global-border !default; -$inverse-divider-small-border: $inverse-global-border !default; -$dotnav-margin-horizontal: 12px !default; -$dotnav-margin-vertical: $dotnav-margin-horizontal !default; -$dotnav-item-width: 10px !default; -$dotnav-item-height: $dotnav-item-width !default; -$dotnav-item-border-radius: 50% !default; -$dotnav-item-background: rgba($global-color, 0.2) !default; -$dotnav-item-hover-background: rgba($global-color, 0.6) !default; -$dotnav-item-onclick-background: rgba($global-color, 0.2) !default; -$dotnav-item-active-background: rgba($global-color, 0.6) !default; -$inverse-dotnav-item-background: rgba($inverse-global-color, 0.5) !default; -$inverse-dotnav-item-hover-background: rgba($inverse-global-color, 0.9) !default; -$inverse-dotnav-item-onclick-background: rgba($inverse-global-color, 0.5) !default; -$inverse-dotnav-item-active-background: rgba($inverse-global-color, 0.9) !default; -$global-z-index: 1000 !default; -$drop-z-index: $global-z-index + 20 !default; -$drop-width: 300px !default; -$drop-margin: $global-margin !default; -$dropdown-z-index: $global-z-index + 20 !default; -$dropdown-min-width: 200px !default; -$dropdown-padding: 15px !default; -$dropdown-background: $global-muted-background !default; -$dropdown-color: $global-color !default; -$dropdown-margin: $global-small-margin !default; -$dropdown-nav-item-color: $global-muted-color !default; -$dropdown-nav-item-hover-color: $global-color !default; -$dropdown-nav-header-color: $global-emphasis-color !default; -$dropdown-nav-divider-border-width: $global-border-width !default; -$dropdown-nav-divider-border: $global-border !default; -$dropdown-nav-sublist-item-color: $global-muted-color !default; -$dropdown-nav-sublist-item-hover-color: $global-color !default; -$form-range-thumb-height: 15px !default; -$form-range-thumb-border-radius: 500px !default; -$form-range-thumb-background: $global-color !default; -$form-range-track-height: 3px !default; -$form-range-track-background: darken($global-muted-background, 5%) !default; -$form-range-track-focus-background: darken($global-muted-background, 15%) !default; -$form-height: $global-control-height !default; -$form-line-height: $form-height !default; -$form-padding-horizontal: 10px !default; -$form-padding-vertical: 4px !default; -$form-background: $global-muted-background !default; -$form-color: $global-color !default; -$form-focus-background: $global-muted-background !default; -$form-focus-color: $global-color !default; -$form-disabled-background: $global-muted-background !default; -$form-disabled-color: $global-muted-color !default; -$form-placeholder-color: $global-muted-color !default; -$form-small-height: $global-control-small-height !default; -$form-small-padding-horizontal: 8px !default; -$form-small-line-height: $form-small-height !default; -$form-small-font-size: $global-small-font-size !default; -$form-large-height: $global-control-large-height !default; -$form-large-padding-horizontal: 12px !default; -$form-large-line-height: $form-large-height !default; -$form-large-font-size: $global-medium-font-size !default; -$form-danger-color: $global-danger-background !default; -$form-success-color: $global-success-background !default; -$form-width-xsmall: 50px !default; -$form-width-small: 130px !default; -$form-width-medium: 200px !default; -$form-width-large: 500px !default; -$form-select-padding-right: 20px !default; -$form-select-icon-color: $global-color !default; -$form-select-disabled-icon-color: $global-muted-color !default; -$form-radio-size: 16px !default; -$form-radio-margin-top: -4px !default; -$form-radio-background: darken($global-muted-background, 5%) !default; -$form-radio-checked-background: $global-primary-background !default; -$form-radio-checked-icon-color: $global-inverse-color !default; -$form-radio-checked-focus-background: darken($global-primary-background, 10%) !default; -$form-radio-disabled-background: $global-muted-background !default; -$form-radio-disabled-icon-color: $global-muted-color !default; -$form-legend-font-size: $global-large-font-size !default; -$form-legend-line-height: 1.4 !default; -$form-stacked-margin-bottom: $global-small-margin !default; -$form-horizontal-label-width: 200px !default; -$form-horizontal-label-margin-top: 7px !default; -$form-horizontal-controls-margin-left: 215px !default; -$form-horizontal-controls-text-padding-top: 7px !default; -$form-icon-width: $form-height !default; -$form-icon-font-size: $global-font-size !default; -$form-icon-color: $global-muted-color !default; -$form-icon-hover-color: $global-color !default; -$internal-form-select-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2224%22%20height%3D%2216%22%20viewBox%3D%220%200%2024%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22#000%22%20points%3D%2212%201%209%206%2015%206%22%20%2F%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22#000%22%20points%3D%2212%2013%209%208%2015%208%22%20%2F%3E%0A%3C%2Fsvg%3E%0A" !default; -$internal-form-radio-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22#000%22%20cx%3D%228%22%20cy%3D%228%22%20r%3D%222%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$internal-form-checkbox-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2211%22%20viewBox%3D%220%200%2014%2011%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22#000%22%20points%3D%2212%201%205%207.5%202%205%201%205.5%205%2010%2013%201.5%22%20%2F%3E%0A%3C%2Fsvg%3E%0A" !default; -$internal-form-checkbox-indeterminate-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22#000%22%20x%3D%223%22%20y%3D%228%22%20width%3D%2210%22%20height%3D%221%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$inverse-global-muted-background: rgba($global-inverse-color, 0.1) !default; -$inverse-form-background: $inverse-global-muted-background !default; -$inverse-form-color: $inverse-global-color !default; -$inverse-form-focus-background: $inverse-global-muted-background !default; -$inverse-form-focus-color: $inverse-global-color !default; -$inverse-form-placeholder-color: $inverse-global-muted-color !default; -$inverse-form-select-icon-color: $inverse-global-color !default; -$inverse-form-radio-background: darken($inverse-global-muted-background, 5%) !default; -$inverse-form-radio-checked-background: $inverse-global-primary-background !default; -$inverse-form-radio-checked-icon-color: $inverse-global-inverse-color !default; -$inverse-form-radio-checked-focus-background: darken($inverse-global-primary-background, 10%) !default; -$grid-gutter-horizontal: $global-gutter !default; -$grid-gutter-vertical: $grid-gutter-horizontal !default; -$grid-gutter-horizontal-l: $global-medium-gutter !default; -$grid-gutter-vertical-l: $grid-gutter-horizontal-l !default; -$grid-small-gutter-horizontal: $global-small-gutter !default; -$grid-small-gutter-vertical: $grid-small-gutter-horizontal !default; -$grid-medium-gutter-horizontal: $global-gutter !default; -$grid-medium-gutter-vertical: $grid-medium-gutter-horizontal !default; -$grid-large-gutter-horizontal: $global-medium-gutter !default; -$grid-large-gutter-vertical: $grid-large-gutter-horizontal !default; -$grid-large-gutter-horizontal-l: $global-large-gutter !default; -$grid-large-gutter-vertical-l: $grid-large-gutter-horizontal-l !default; -$grid-divider-border-width: $global-border-width !default; -$grid-divider-border: $global-border !default; -$inverse-grid-divider-border: $inverse-global-border !default; -$heading-primary-font-size: $global-xxlarge-font-size !default; -$heading-primary-line-height: 1.2 !default; -$heading-primary-font-size-m: 3.75rem !default; -$heading-primary-line-height-m: 1.1 !default; -$heading-hero-font-size: 4rem !default; -$heading-hero-line-height: 1.1 !default; -$heading-hero-font-size-s: 6rem !default; -$heading-hero-line-height-s: 1 !default; -$heading-hero-font-size-m: 8rem !default; -$heading-hero-line-height-m: 1 !default; -$heading-divider-padding-bottom: 10px !default; -$heading-divider-border-width: $global-border-width !default; -$heading-divider-border: $global-border !default; -$heading-bullet-top: unquote('calc(-0.1 * 1em)') !default; -$heading-bullet-height: 0.9em !default; -$heading-bullet-margin-right: 10px !default; -$heading-bullet-border-width: 5px !default; -$heading-bullet-border: $global-border !default; -$heading-line-top: 50% !default; -$heading-line-border-width: $global-border-width !default; -$heading-line-height: $heading-line-border-width !default; -$heading-line-width: 2000px !default; -$heading-line-border: $global-border !default; -$heading-line-margin-horizontal: 0.6em !default; -$inverse-heading-divider-border: $inverse-global-border !default; -$inverse-heading-bullet-border: $inverse-global-border !default; -$inverse-heading-line-border: $inverse-global-border !default; -$icon-image-size: 20px !default; -$icon-link-color: $global-muted-color !default; -$icon-link-hover-color: $global-color !default; -$icon-link-active-color: darken($global-color, 5%) !default; -$icon-button-size: 36px !default; -$icon-button-border-radius: 500px !default; -$icon-button-background: $global-muted-background !default; -$icon-button-color: $global-muted-color !default; -$icon-button-hover-background: darken($icon-button-background, 5%) !default; -$icon-button-hover-color: $global-color !default; -$icon-button-active-background: darken($icon-button-background, 10%) !default; -$icon-button-active-color: $global-color !default; -$inverse-icon-link-color: $inverse-global-muted-color !default; -$inverse-icon-link-hover-color: $inverse-global-color !default; -$inverse-icon-link-active-color: $inverse-global-color !default; -$inverse-icon-button-background: $inverse-global-muted-background !default; -$inverse-icon-button-color: $inverse-global-muted-color !default; -$inverse-icon-button-hover-background: darken($inverse-icon-button-background, 5%) !default; -$inverse-icon-button-hover-color: $inverse-global-color !default; -$inverse-icon-button-active-background: darken($inverse-icon-button-background, 10%) !default; -$inverse-icon-button-active-color: $inverse-global-color !default; -$iconnav-margin-horizontal: $global-small-margin !default; -$iconnav-margin-vertical: $iconnav-margin-horizontal !default; -$iconnav-item-color: $global-muted-color !default; -$iconnav-item-hover-color: $global-color !default; -$iconnav-item-active-color: $global-color !default; -$inverse-iconnav-item-color: $inverse-global-muted-color !default; -$inverse-iconnav-item-hover-color: $inverse-global-color !default; -$inverse-iconnav-item-active-color: $inverse-global-color !default; -$inverse-global-color-mode: light !default; -$label-padding-vertical: 0 !default; -$label-padding-horizontal: $global-small-margin !default; -$label-background: $global-primary-background !default; -$label-line-height: $global-line-height !default; -$label-font-size: $global-small-font-size !default; -$label-color: $global-inverse-color !default; -$label-success-background: $global-success-background !default; -$label-success-color: $global-inverse-color !default; -$label-warning-background: $global-warning-background !default; -$label-warning-color: $global-inverse-color !default; -$label-danger-background: $global-danger-background !default; -$label-danger-color: $global-inverse-color !default; -$inverse-label-background: $inverse-global-primary-background !default; -$inverse-label-color: $inverse-global-inverse-color !default; -$lightbox-z-index: $global-z-index + 10 !default; -$lightbox-background: #000 !default; -$lightbox-item-color: rgba(255,255,255,0.7) !default; -$lightbox-toolbar-padding-vertical: 10px !default; -$lightbox-toolbar-padding-horizontal: 10px !default; -$lightbox-toolbar-background: rgba(0,0,0,0.3) !default; -$lightbox-toolbar-color: rgba(255,255,255,0.7) !default; -$lightbox-toolbar-icon-padding: 5px !default; -$lightbox-toolbar-icon-color: rgba(255,255,255,0.7) !default; -$lightbox-toolbar-icon-hover-color: #fff !default; -$lightbox-button-size: 50px !default; -$lightbox-button-background: $lightbox-toolbar-background !default; -$lightbox-button-color: rgba(255,255,255,0.7) !default; -$lightbox-button-hover-color: #fff !default; -$link-muted-color: $global-muted-color !default; -$link-muted-hover-color: $global-color !default; -$link-text-hover-color: $global-muted-color !default; -$link-heading-hover-color: $global-primary-background !default; -$link-heading-hover-text-decoration: none !default; -$inverse-link-muted-color: $inverse-global-muted-color !default; -$inverse-link-muted-hover-color: $inverse-global-color !default; -$inverse-link-text-hover-color: $inverse-global-muted-color !default; -$inverse-link-heading-hover-color: $inverse-global-primary-background !default; -$list-margin-top: $global-small-margin !default; -$list-nested-padding-left: $global-gutter !default; -$list-divider-margin-top: $global-small-margin !default; -$list-divider-border-width: $global-border-width !default; -$list-divider-border: $global-border !default; -$list-striped-padding-vertical: $global-small-margin !default; -$list-striped-padding-horizontal: $global-small-margin !default; -$list-striped-background: $global-muted-background !default; -$list-bullet-width: ($global-line-height * 1em) !default; -$list-bullet-height: $list-bullet-width !default; -$list-bullet-margin-right: $global-small-margin !default; -$list-bullet-icon-color: $global-color !default; -$list-large-margin-top: $global-margin !default; -$list-large-divider-margin-top: $global-margin !default; -$list-large-striped-padding-vertical: $global-margin !default; -$list-large-striped-padding-horizontal: $global-small-margin !default; -$internal-list-bullet-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%226%22%20height%3D%226%22%20viewBox%3D%220%200%206%206%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22#000%22%20cx%3D%223%22%20cy%3D%223%22%20r%3D%223%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$inverse-list-divider-border: $inverse-global-border !default; -$inverse-list-striped-background: $inverse-global-muted-background !default; -$inverse-list-bullet-icon-color: $inverse-global-color !default; -$margin-margin: $global-margin !default; -$margin-small-margin: $global-small-margin !default; -$margin-medium-margin: $global-medium-margin !default; -$margin-large-margin: $global-medium-margin !default; -$margin-large-margin-l: $global-large-margin !default; -$margin-xlarge-margin: $global-large-margin !default; -$global-xlarge-margin: 140px !default; -$margin-xlarge-margin-l: $global-xlarge-margin !default; -$marker-padding: 5px !default; -$marker-background: $global-secondary-background !default; -$marker-color: $global-inverse-color !default; -$marker-hover-color: $global-inverse-color !default; -$inverse-marker-background: $global-muted-background !default; -$inverse-marker-color: $global-color !default; -$inverse-marker-hover-color: $global-color !default; -$modal-z-index: $global-z-index + 10 !default; -$modal-background: rgba(0,0,0,0.6) !default; -$modal-padding-horizontal: 15px !default; -$modal-padding-horizontal-s: $global-gutter !default; -$modal-padding-horizontal-m: $global-medium-gutter !default; -$modal-padding-vertical: $modal-padding-horizontal !default; -$modal-padding-vertical-s: 50px !default; -$modal-dialog-width: 600px !default; -$modal-dialog-background: $global-background !default; -$modal-container-width: 1200px !default; -$modal-body-padding-horizontal: $global-gutter !default; -$modal-body-padding-vertical: $global-gutter !default; -$modal-header-padding-horizontal: $global-gutter !default; -$modal-header-padding-vertical: ($modal-header-padding-horizontal / 2) !default; -$modal-header-background: $global-muted-background !default; -$modal-footer-padding-horizontal: $global-gutter !default; -$modal-footer-padding-vertical: ($modal-footer-padding-horizontal / 2) !default; -$modal-footer-background: $global-muted-background !default; -$modal-title-font-size: $global-xlarge-font-size !default; -$modal-title-line-height: 1.3 !default; -$modal-close-position: $global-small-margin !default; -$modal-close-padding: 5px !default; -$modal-close-outside-position: 0 !default; -$modal-close-outside-translate: 100% !default; -$modal-close-outside-color: lighten($global-inverse-color, 20%) !default; -$modal-close-outside-hover-color: $global-inverse-color !default; -$nav-item-padding-vertical: 5px !default; -$nav-item-padding-horizontal: 0 !default; -$nav-sublist-padding-vertical: 5px !default; -$nav-sublist-padding-left: 15px !default; -$nav-sublist-deeper-padding-left: 15px !default; -$nav-sublist-item-padding-vertical: 2px !default; -$nav-parent-icon-width: ($global-line-height * 1em) !default; -$nav-parent-icon-height: $nav-parent-icon-width !default; -$nav-parent-icon-color: $global-color !default; -$nav-header-padding-vertical: $nav-item-padding-vertical !default; -$nav-header-padding-horizontal: $nav-item-padding-horizontal !default; -$nav-header-font-size: $global-small-font-size !default; -$nav-header-text-transform: uppercase !default; -$nav-header-margin-top: $global-margin !default; -$nav-divider-margin-vertical: 5px !default; -$nav-divider-margin-horizontal: 0 !default; -$nav-default-item-color: $global-muted-color !default; -$nav-default-item-hover-color: $global-color !default; -$nav-default-item-active-color: $global-emphasis-color !default; -$nav-default-header-color: $global-emphasis-color !default; -$nav-default-divider-border-width: $global-border-width !default; -$nav-default-divider-border: $global-border !default; -$nav-default-sublist-item-color: $global-muted-color !default; -$nav-default-sublist-item-hover-color: $global-color !default; -$nav-primary-item-font-size: $global-large-font-size !default; -$nav-primary-item-line-height: $global-line-height !default; -$nav-primary-item-color: $global-muted-color !default; -$nav-primary-item-hover-color: $global-color !default; -$nav-primary-item-active-color: $global-emphasis-color !default; -$nav-primary-header-color: $global-emphasis-color !default; -$nav-primary-divider-border-width: $global-border-width !default; -$nav-primary-divider-border: $global-border !default; -$nav-primary-sublist-item-color: $global-muted-color !default; -$nav-primary-sublist-item-hover-color: $global-color !default; -$internal-nav-parent-close-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolyline%20fill%3D%22none%22%20stroke%3D%22#000%22%20stroke-width%3D%221.1%22%20points%3D%2210%201%204%207%2010%2013%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$internal-nav-parent-open-image: "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolyline%20fill%3D%22none%22%20stroke%3D%22#000%22%20stroke-width%3D%221.1%22%20points%3D%221%204%207%2010%2013%204%22%20%2F%3E%0A%3C%2Fsvg%3E" !default; -$inverse-nav-parent-icon-color: $inverse-global-color !default; -$inverse-nav-default-item-color: $inverse-global-muted-color !default; -$inverse-nav-default-item-hover-color: $inverse-global-color !default; -$inverse-nav-default-item-active-color: $inverse-global-emphasis-color !default; -$inverse-nav-default-header-color: $inverse-global-emphasis-color !default; -$inverse-nav-default-divider-border: $inverse-global-border !default; -$inverse-nav-default-sublist-item-color: $inverse-global-muted-color !default; -$inverse-nav-default-sublist-item-hover-color: $inverse-global-color !default; -$inverse-nav-primary-item-color: $inverse-global-muted-color !default; -$inverse-nav-primary-item-hover-color: $inverse-global-color !default; -$inverse-nav-primary-item-active-color: $inverse-global-emphasis-color !default; -$inverse-nav-primary-header-color: $inverse-global-emphasis-color !default; -$inverse-nav-primary-divider-border: $inverse-global-border !default; -$inverse-nav-primary-sublist-item-color: $inverse-global-muted-color !default; -$inverse-nav-primary-sublist-item-hover-color: $inverse-global-color !default; -$navbar-background: $global-muted-background !default; -$navbar-color-mode: none !default; -$navbar-nav-item-height: 80px !default; -$navbar-nav-item-padding-horizontal: 15px !default; -$navbar-nav-item-color: $global-muted-color !default; -$navbar-nav-item-font-size: $global-font-size !default; -$navbar-nav-item-font-family: $global-font-family !default; -$navbar-nav-item-hover-color: $global-color !default; -$navbar-nav-item-onclick-color: $global-emphasis-color !default; -$navbar-nav-item-active-color: $global-emphasis-color !default; -$navbar-item-color: $global-color !default; -$navbar-toggle-color: $global-muted-color !default; -$navbar-toggle-hover-color: $global-color !default; -$navbar-subtitle-font-size: $global-small-font-size !default; -$navbar-dropdown-z-index: $global-z-index + 20 !default; -$navbar-dropdown-width: 200px !default; -$navbar-dropdown-margin: 0 !default; -$navbar-dropdown-padding: 15px !default; -$navbar-dropdown-background: $global-muted-background !default; -$navbar-dropdown-color: $global-color !default; -$navbar-dropdown-grid-gutter-horizontal: $global-gutter !default; -$navbar-dropdown-grid-gutter-vertical: $navbar-dropdown-grid-gutter-horizontal !default; -$navbar-dropdown-dropbar-margin-top: 0 !default; -$navbar-dropdown-dropbar-margin-bottom: $navbar-dropdown-dropbar-margin-top !default; -$navbar-dropdown-nav-item-color: $global-muted-color !default; -$navbar-dropdown-nav-item-hover-color: $global-color !default; -$navbar-dropdown-nav-item-active-color: $global-emphasis-color !default; -$navbar-dropdown-nav-header-color: $global-emphasis-color !default; -$navbar-dropdown-nav-divider-border-width: $global-border-width !default; -$navbar-dropdown-nav-divider-border: $global-border !default; -$navbar-dropdown-nav-sublist-item-color: $global-muted-color !default; -$navbar-dropdown-nav-sublist-item-hover-color: $global-color !default; -$navbar-dropbar-background: $navbar-dropdown-background !default; -$navbar-dropbar-z-index: $global-z-index - 20 !default; -$inverse-navbar-nav-item-color: $inverse-global-muted-color !default; -$inverse-navbar-nav-item-hover-color: $inverse-global-color !default; -$inverse-navbar-nav-item-onclick-color: $inverse-global-emphasis-color !default; -$inverse-navbar-nav-item-active-color: $inverse-global-emphasis-color !default; -$inverse-navbar-item-color: $inverse-global-color !default; -$inverse-navbar-toggle-color: $inverse-global-muted-color !default; -$inverse-navbar-toggle-hover-color: $inverse-global-color !default; -$notification-position: 10px !default; -$notification-z-index: $global-z-index + 40 !default; -$notification-width: 350px !default; -$notification-message-margin-bottom: 10px !default; -$notification-message-padding: $global-small-gutter !default; -$notification-message-background: $global-muted-background !default; -$notification-message-color: $global-color !default; -$notification-message-font-size: $global-medium-font-size !default; -$notification-message-line-height: 1.4 !default; -$notification-close-top: $notification-message-padding + 5px !default; -$notification-close-right: $notification-message-padding !default; -$notification-message-primary-color: $global-primary-background !default; -$notification-message-success-color: $global-success-background !default; -$notification-message-warning-color: $global-warning-background !default; -$notification-message-danger-color: $global-danger-background !default; -$offcanvas-z-index: $global-z-index !default; -$offcanvas-bar-width: 270px !default; -$offcanvas-bar-padding-vertical: $global-margin !default; -$offcanvas-bar-padding-horizontal: $global-margin !default; -$offcanvas-bar-background: $global-secondary-background !default; -$offcanvas-bar-color-mode: light !default; -$offcanvas-bar-width-m: 350px !default; -$offcanvas-bar-padding-vertical-m: $global-medium-gutter !default; -$offcanvas-bar-padding-horizontal-m: $global-medium-gutter !default; -$offcanvas-close-position: 20px !default; -$offcanvas-close-padding: 5px !default; -$offcanvas-overlay-background: rgba(0,0,0,0.1) !default; -$overlay-padding-horizontal: $global-gutter !default; -$overlay-padding-vertical: $global-gutter !default; -$overlay-default-background: rgba($global-background, 0.8) !default; -$overlay-primary-background: rgba($global-secondary-background, 0.8) !default; -$overlay-primary-color-mode: light !default; -$padding-padding: $global-gutter !default; -$padding-padding-l: $global-medium-gutter !default; -$padding-small-padding: $global-small-gutter !default; -$padding-large-padding: $global-gutter !default; -$padding-large-padding-l: $global-large-gutter !default; -$pagination-margin-horizontal: 20px !default; -$pagination-item-color: $global-muted-color !default; -$pagination-item-hover-color: $global-color !default; -$pagination-item-hover-text-decoration: none !default; -$pagination-item-active-color: $global-color !default; -$pagination-item-disabled-color: $global-muted-color !default; -$inverse-pagination-item-color: $inverse-global-muted-color !default; -$inverse-pagination-item-hover-color: $inverse-global-color !default; -$inverse-pagination-item-active-color: $inverse-global-color !default; -$inverse-pagination-item-disabled-color: $inverse-global-muted-color !default; -$placeholder-margin-vertical: $global-margin !default; -$placeholder-padding-vertical: $global-gutter !default; -$placeholder-padding-horizontal: $global-gutter !default; -$placeholder-background: $global-muted-background !default; -$position-small-margin: $global-small-gutter !default; -$position-medium-margin: $global-gutter !default; -$position-large-margin: $global-gutter !default; -$position-large-margin-l: 50px !default; -$progress-height: 15px !default; -$progress-margin-vertical: $global-margin !default; -$progress-background: $global-muted-background !default; -$progress-bar-background: $global-primary-background !default; -$search-color: $global-color !default; -$search-placeholder-color: $global-muted-color !default; -$search-icon-color: $global-muted-color !default; -$search-default-width: 180px !default; -$search-default-height: $global-control-height !default; -$search-default-padding-horizontal: 6px !default; -$search-default-background: $global-muted-background !default; -$search-default-focus-background: $search-default-background !default; -$search-default-icon-width: $global-control-height !default; -$search-navbar-width: 400px !default; -$search-navbar-height: 40px !default; -$search-navbar-background: transparent !default; -$search-navbar-font-size: $global-large-font-size !default; -$search-navbar-icon-width: 40px !default; -$search-large-width: 500px !default; -$search-large-height: 80px !default; -$search-large-background: transparent !default; -$search-large-font-size: $global-xxlarge-font-size !default; -$search-large-icon-width: 80px !default; -$search-toggle-color: $global-muted-color !default; -$search-toggle-hover-color: $global-color !default; -$inverse-search-color: $inverse-global-color !default; -$inverse-search-placeholder-color: $inverse-global-muted-color !default; -$inverse-search-icon-color: $inverse-global-muted-color !default; -$inverse-search-default-background: $inverse-global-muted-background !default; -$inverse-search-default-focus-background: $inverse-search-default-background !default; -$inverse-search-navbar-background: transparent !default; -$inverse-search-large-background: transparent !default; -$inverse-search-toggle-color: $inverse-global-muted-color !default; -$inverse-search-toggle-hover-color: $inverse-global-color !default; -$section-padding-vertical: $global-medium-margin !default; -$section-padding-vertical-m: $global-large-margin !default; -$section-xsmall-padding-vertical: $global-margin !default; -$section-small-padding-vertical: $global-medium-margin !default; -$section-large-padding-vertical: $global-large-margin !default; -$section-large-padding-vertical-m: $global-xlarge-margin !default; -$section-xlarge-padding-vertical: $global-xlarge-margin !default; -$section-xlarge-padding-vertical-m: ($global-large-margin + $global-xlarge-margin) !default; -$section-default-background: $global-background !default; -$section-muted-background: $global-muted-background !default; -$section-primary-background: $global-primary-background !default; -$section-primary-color-mode: light !default; -$section-secondary-background: $global-secondary-background !default; -$section-secondary-color-mode: light !default; -$slidenav-padding-vertical: 5px !default; -$slidenav-padding-horizontal: 10px !default; -$slidenav-color: rgba($global-color, 0.5) !default; -$slidenav-hover-color: rgba($global-color, 0.9) !default; -$slidenav-active-color: rgba($global-color, 0.5) !default; -$slidenav-large-padding-vertical: 10px !default; -$slidenav-large-padding-horizontal: $slidenav-large-padding-vertical !default; -$inverse-slidenav-color: rgba($inverse-global-color, 0.7) !default; -$inverse-slidenav-hover-color: rgba($inverse-global-color, 0.95) !default; -$inverse-slidenav-active-color: rgba($inverse-global-color, 0.7) !default; -$sortable-dragged-z-index: $global-z-index + 50 !default; -$sortable-placeholder-opacity: 0 !default; -$sortable-empty-height: 50px !default; -$spinner-size: 30px !default; -$spinner-stroke-width: 1 !default; -$spinner-radius: floor(($spinner-size - $spinner-stroke-width) / 2) !default; -$spinner-circumference: round(2 * 3.141 * $spinner-radius) !default; -$spinner-duration: 1.4s !default; -$sticky-z-index: $global-z-index - 20 !default; -$sticky-animation-duration: 0.2s !default; -$sticky-reverse-animation-duration: 0.2s !default; -$subnav-margin-horizontal: 20px !default; -$subnav-item-color: $global-muted-color !default; -$subnav-item-hover-color: $global-color !default; -$subnav-item-hover-text-decoration: none !default; -$subnav-item-active-color: $global-emphasis-color !default; -$subnav-divider-margin-horizontal: $subnav-margin-horizontal !default; -$subnav-divider-border-height: 1.5em !default; -$subnav-divider-border-width: $global-border-width !default; -$subnav-divider-border: $global-border !default; -$subnav-pill-item-padding-vertical: 5px !default; -$subnav-pill-item-padding-horizontal: 10px !default; -$subnav-pill-item-background: transparent !default; -$subnav-pill-item-color: $subnav-item-color !default; -$subnav-pill-item-hover-background: $global-muted-background !default; -$subnav-pill-item-hover-color: $global-color !default; -$subnav-pill-item-onclick-background: $subnav-pill-item-hover-background !default; -$subnav-pill-item-onclick-color: $subnav-pill-item-hover-color !default; -$subnav-pill-item-active-background: $global-primary-background !default; -$subnav-pill-item-active-color: $global-inverse-color !default; -$subnav-item-disabled-color: $global-muted-color !default; -$inverse-subnav-item-color: $inverse-global-muted-color !default; -$inverse-subnav-item-hover-color: $inverse-global-color !default; -$inverse-subnav-item-active-color: $inverse-global-emphasis-color !default; -$inverse-subnav-divider-border: $inverse-global-border !default; -$inverse-subnav-pill-item-background: transparent !default; -$inverse-subnav-pill-item-color: $inverse-global-muted-color !default; -$inverse-subnav-pill-item-hover-background: $inverse-global-muted-background !default; -$inverse-subnav-pill-item-hover-color: $inverse-global-color !default; -$inverse-subnav-pill-item-onclick-background: $inverse-subnav-pill-item-hover-background !default; -$inverse-subnav-pill-item-onclick-color: $inverse-subnav-pill-item-hover-color !default; -$inverse-subnav-pill-item-active-background: $inverse-global-primary-background !default; -$inverse-subnav-pill-item-active-color: $inverse-global-inverse-color !default; -$inverse-subnav-item-disabled-color: $inverse-global-muted-color !default; -$tab-margin-horizontal: 20px !default; -$tab-item-padding-horizontal: 10px !default; -$tab-item-padding-vertical: 5px !default; -$tab-item-color: $global-muted-color !default; -$tab-item-hover-color: $global-color !default; -$tab-item-hover-text-decoration: none !default; -$tab-item-active-color: $global-emphasis-color !default; -$tab-item-disabled-color: $global-muted-color !default; -$inverse-tab-item-color: $inverse-global-muted-color !default; -$inverse-tab-item-hover-color: $inverse-global-color !default; -$inverse-tab-item-active-color: $inverse-global-emphasis-color !default; -$inverse-tab-item-disabled-color: $inverse-global-muted-color !default; -$table-margin-vertical: $global-margin !default; -$table-cell-padding-vertical: 16px !default; -$table-cell-padding-horizontal: 12px !default; -$table-header-cell-font-size: $global-font-size !default; -$table-header-cell-font-weight: bold !default; -$table-header-cell-color: $global-color !default; -$table-footer-font-size: $global-small-font-size !default; -$table-caption-font-size: $global-small-font-size !default; -$table-caption-color: $global-muted-color !default; -$table-row-active-background: #ffd !default; -$table-divider-border-width: $global-border-width !default; -$table-divider-border: $global-border !default; -$table-striped-row-background: $global-muted-background !default; -$table-hover-row-background: $table-row-active-background !default; -$table-small-cell-padding-vertical: 10px !default; -$table-small-cell-padding-horizontal: 12px !default; -$table-large-cell-padding-vertical: 22px !default; -$table-large-cell-padding-horizontal: 12px !default; -$table-expand-min-width: 150px !default; -$inverse-table-header-cell-color: $inverse-global-color !default; -$inverse-table-caption-color: $inverse-global-muted-color !default; -$inverse-table-row-active-background: fade-out($inverse-global-muted-background, 0.02) !default; -$inverse-table-divider-border: $inverse-global-border !default; -$inverse-table-striped-row-background: $inverse-global-muted-background !default; -$inverse-table-hover-row-background: $inverse-table-row-active-background !default; -$text-lead-font-size: $global-large-font-size !default; -$text-lead-line-height: 1.5 !default; -$text-lead-color: $global-emphasis-color !default; -$text-meta-font-size: $global-small-font-size !default; -$text-meta-line-height: 1.4 !default; -$text-meta-color: $global-muted-color !default; -$text-small-font-size: $global-small-font-size !default; -$text-small-line-height: 1.5 !default; -$text-large-font-size: $global-large-font-size !default; -$text-large-line-height: 1.5 !default; -$text-bold-font-weight: bolder !default; -$text-muted-color: $global-muted-color !default; -$text-primary-color: $global-primary-background !default; -$text-success-color: $global-success-background !default; -$text-warning-color: $global-warning-background !default; -$text-danger-color: $global-danger-background !default; -$text-background-color: $global-primary-background !default; -$inverse-text-lead-color: $inverse-global-color !default; -$inverse-text-meta-color: $inverse-global-muted-color !default; -$inverse-text-muted-color: $inverse-global-muted-color !default; -$inverse-text-primary-color: $inverse-global-color !default; -$thumbnav-margin-horizontal: 15px !default; -$thumbnav-margin-vertical: $thumbnav-margin-horizontal !default; -$tile-padding-horizontal: 15px !default; -$tile-padding-horizontal-s: $global-gutter !default; -$tile-padding-horizontal-m: $global-medium-gutter !default; -$tile-padding-vertical: $global-medium-margin !default; -$tile-padding-vertical-m: $global-large-margin !default; -$tile-xsmall-padding-vertical: $global-margin !default; -$tile-small-padding-vertical: $global-medium-margin !default; -$tile-large-padding-vertical: $global-large-margin !default; -$tile-large-padding-vertical-m: $global-xlarge-margin !default; -$tile-xlarge-padding-vertical: $global-xlarge-margin !default; -$tile-xlarge-padding-vertical-m: ($global-large-margin + $global-xlarge-margin) !default; -$tile-default-background: $global-background !default; -$tile-muted-background: $global-muted-background !default; -$tile-primary-background: $global-primary-background !default; -$tile-primary-color-mode: light !default; -$tile-secondary-background: $global-secondary-background !default; -$tile-secondary-color-mode: light !default; -$tooltip-z-index: $global-z-index + 30 !default; -$tooltip-max-width: 200px !default; -$tooltip-padding-vertical: 3px !default; -$tooltip-padding-horizontal: 6px !default; -$tooltip-background: #666 !default; -$tooltip-border-radius: 2px !default; -$tooltip-color: $global-inverse-color !default; -$tooltip-font-size: 12px !default; -$tooltip-margin: 10px !default; -$totop-padding: 5px !default; -$totop-color: $global-muted-color !default; -$totop-hover-color: $global-color !default; -$totop-active-color: $global-emphasis-color !default; -$inverse-totop-color: $inverse-global-muted-color !default; -$inverse-totop-hover-color: $inverse-global-color !default; -$inverse-totop-active-color: $inverse-global-emphasis-color !default; -$transition-duration: 0.3s !default; -$transition-scale: 1.1 !default; -$transition-slide-small-translate: 10px !default; -$transition-slide-medium-translate: 50px !default; -$transition-slow-duration: 0.7s !default; -$panel-scrollable-height: 170px !default; -$panel-scrollable-padding: 10px !default; -$panel-scrollable-border-width: $global-border-width !default; -$panel-scrollable-border: $global-border !default; -$height-small-height: 150px !default; -$height-medium-height: 300px !default; -$height-large-height: 450px !default; -$border-rounded-border-radius: 5px !default; -$box-shadow-duration: 0.1s !default; -$box-shadow-bottom-height: 30px !default; -$box-shadow-bottom-border-radius: 100% !default; -$box-shadow-bottom-background: #444 !default; -$box-shadow-bottom-blur: 20px !default; -$dropcap-margin-right: 10px !default; -$dropcap-font-size: (($global-line-height * 3) * 1em) !default; -$leader-fill-content: '.' !default; -$leader-fill-margin-left: $global-small-gutter !default; -$logo-font-size: $global-large-font-size !default; -$logo-font-family: $global-font-family !default; -$logo-color: $global-color !default; -$logo-hover-color: $global-color !default; -$dragover-box-shadow: 0 0 20px rgba(100,100,100,0.3) !default; -$inverse-logo-color: $inverse-global-color !default; -$inverse-logo-hover-color: $inverse-global-color !default; -$breakpoint-small: 640px !default; -$breakpoint-medium: 960px !default; -$breakpoint-large: 1200px !default; -$breakpoint-xlarge: 1600px !default; -$breakpoint-xsmall-max: ($breakpoint-small - 1) !default; -$breakpoint-small-max: ($breakpoint-medium - 1) !default; -$breakpoint-medium-max: ($breakpoint-large - 1) !default; -$breakpoint-large-max: ($breakpoint-xlarge - 1) !default; -$global-small-box-shadow: 0 2px 8px rgba(0,0,0,0.08) !default; -$global-medium-box-shadow: 0 5px 15px rgba(0,0,0,0.08) !default; -$global-large-box-shadow: 0 14px 25px rgba(0,0,0,0.16) !default; -$global-xlarge-box-shadow: 0 28px 50px rgba(0,0,0,0.16) !default; -$width-small-width: 150px !default; -$width-medium-width: 300px !default; -$width-large-width: 450px !default; -$width-xlarge-width: 600px !default; -$width-xxlarge-width: 750px !default; \ No newline at end of file diff --git a/_site/2017/05/25/post63.html b/_site/2017/05/25/post63.html deleted file mode 100644 index edf224762c..0000000000 --- a/_site/2017/05/25/post63.html +++ /dev/null @@ -1,356 +0,0 @@ - - - - - - - - -Site tags | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    - - - - -
    -
    -
    - -

    Site tags

    - - - -
    - -

    https://zbabystack.netlify.com/assets/posts/

    - -

    /2017/05/25/post63.html

    - -

    Musce libero nunc, dignissim quis turpis quis, semper vehicula dolor. Suspendisse tincidunt consequat quam, ac posuere leo dapibus id. Cras fringilla convallis elit, at eleifend mi interam.

    - -

    Nulla non sollicitudin. Morbi sit amet laoreet ipsum, vel pretium mi. Morbi varius, tellus in accumsan blandit, elit ligula eleifend velit, luctus mattis ante nulla condimentum nulla. Etiam vestibulum risus vel arcu elementum eleifend. Cras at dolor eget urna varius faucibus tempus in elit.

    - -

    Image Lightbox Example

    -

    Nunc porta malesuada porta. Etiam tristique vestibulum dolor at ultricies. Proin hendrerit sapien sed erat fermentum, at commodo velit consectetur.

    - -
    - - - - Alt for image -
    - -
    -
    -
    Image in lightbox
    -
    - -

    Etiam vestibulum risus vel arcu elementum eleifend. Cras at dolor eget urna varius faucibus tempus in elit. Cras a dui imperdiet, tempus metus quis, pharetra turpis. Phasellus at massa sit amet ante semper fermentum sed eget lectus. Quisque id dictum magna, et dapibus turpis.

    - -

    Example Of Code Block

    -

    In accumsan lacus ac neque maximus dictum. Phasellus eleifend leo id mattis bibendum. Curabitur et purus turpis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;

    - -
    <head>
    -  <meta charset="utf-8">
    -  <meta http-equiv="X-UA-Compatible" content="IE=edge">
    -  <meta name="viewport" content="width=device-width, initial-scale=1">
    -  <link rel="stylesheet" href="/assets/css/main.css">
    -  <link rel="shortcut icon" type="image/png" href="/assets/img/favicon.png" >
    -  <script src="/assets/js/main.js"></script>
    -</head>
    -
    -
    - -

    Text and Quote

    -

    Cras at dolor eget urna varius faucibus tempus in elit. Cras a dui imperdiet, tempus metus quis, pharetra turpis. Phasellus at massa sit amet ante semper fermentum sed eget lectus. Quisque id dictum magna turpis.

    - -
    -

    Etiam vestibulum risus vel arcu elementum eleifend. Cras at dolor eget urna varius faucibus tempus in elit. Cras a dui imperdiet

    -
    - -

    In accumsan lacus ac neque maximus dictum. Phasellus eleifend leo id mattis bibendum. Curabitur et purus turpis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;

    - -

    Etiam in fermentum mi. Sed et tempor felis, eu aliquet nisi. Nam eget ullamcorper arcu. Nunc porttitor nisl a dolor blandit, eget consequat sem maximus. Phasellus lacinia quam porta orci malesuada, vel tincidunt.

    - - - -
    - -
    - -
    -
    -
    - -
    -
    - -
    -
    -
    - - - -
    - - - -
    -
    - - -
    -
    - - - - - - - -
    -
    - - - -
    -
    - -
    - -
    -
    -
    - -
    - -
    - - -
    - -
    - - - - -
    - -
    - - - - - -
    - -
    - - - - - - -
    -
    - - -
    -
    - - - - - - - - - - - diff --git a/_site/404.html b/_site/404.html deleted file mode 100644 index 61521fc718..0000000000 --- a/_site/404.html +++ /dev/null @@ -1,263 +0,0 @@ - - - - - - - - -Feeling Lost | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    - - - - -
    -
    - -
    - -

    Feeling Lost

    - -
    -

    The page you are looking for cannot be found. Please navigate to homepage.

    - -
    - -
    - -
    -
    - - -
    -
    - - - - - - - -
    -
    - - - -
    -
    - -
    - -
    -
    -
    - -
    - -
    - - -
    - -
    - - - - -
    - -
    - - - - - -
    - -
    - - - - - - -
    -
    - - -
    -
    - - - - - - - - - - - diff --git a/_site/android/images/app-flow.png b/_site/android/images/app-flow.png deleted file mode 100644 index fa8b85a3bd..0000000000 Binary files a/_site/android/images/app-flow.png and /dev/null differ diff --git a/_site/android/images/blockstack-install.png b/_site/android/images/blockstack-install.png deleted file mode 100644 index 5e4d6256ad..0000000000 Binary files a/_site/android/images/blockstack-install.png and /dev/null differ diff --git a/_site/android/images/blockstack-signin.png b/_site/android/images/blockstack-signin.png deleted file mode 100644 index 081d505034..0000000000 Binary files a/_site/android/images/blockstack-signin.png and /dev/null differ diff --git a/_site/android/images/chrome-prompt.png b/_site/android/images/chrome-prompt.png deleted file mode 100644 index f60822ab55..0000000000 Binary files a/_site/android/images/chrome-prompt.png and /dev/null differ diff --git a/_site/android/images/configure-activity.png b/_site/android/images/configure-activity.png deleted file mode 100644 index 1be7edf580..0000000000 Binary files a/_site/android/images/configure-activity.png and /dev/null differ diff --git a/_site/android/images/create-restore.png b/_site/android/images/create-restore.png deleted file mode 100644 index 914b26e5b3..0000000000 Binary files a/_site/android/images/create-restore.png and /dev/null differ diff --git a/_site/android/images/final-app.png b/_site/android/images/final-app.png deleted file mode 100644 index 01f62b839a..0000000000 Binary files a/_site/android/images/final-app.png and /dev/null differ diff --git a/_site/android/images/hello-andriod-1.png b/_site/android/images/hello-andriod-1.png deleted file mode 100644 index c3680a2df8..0000000000 Binary files a/_site/android/images/hello-andriod-1.png and /dev/null differ diff --git a/_site/android/images/initial-build.png b/_site/android/images/initial-build.png deleted file mode 100644 index 601f3e8a18..0000000000 Binary files a/_site/android/images/initial-build.png and /dev/null differ diff --git a/_site/android/images/new-interface.png b/_site/android/images/new-interface.png deleted file mode 100644 index d434f9bba3..0000000000 Binary files a/_site/android/images/new-interface.png and /dev/null differ diff --git a/_site/android/images/oreo-api.png b/_site/android/images/oreo-api.png deleted file mode 100644 index 1de356914c..0000000000 Binary files a/_site/android/images/oreo-api.png and /dev/null differ diff --git a/_site/android/images/running-app.png b/_site/android/images/running-app.png deleted file mode 100644 index 9838c05f8c..0000000000 Binary files a/_site/android/images/running-app.png and /dev/null differ diff --git a/_site/android/images/select-hdw.png b/_site/android/images/select-hdw.png deleted file mode 100644 index 2eaccb4fcb..0000000000 Binary files a/_site/android/images/select-hdw.png and /dev/null differ diff --git a/_site/android/images/studio-download.png b/_site/android/images/studio-download.png deleted file mode 100644 index 8dc7314278..0000000000 Binary files a/_site/android/images/studio-download.png and /dev/null differ diff --git a/_site/android/images/sync-project.png b/_site/android/images/sync-project.png deleted file mode 100644 index b5e5c1412f..0000000000 Binary files a/_site/android/images/sync-project.png and /dev/null differ diff --git a/_site/android/images/sync-success.png b/_site/android/images/sync-success.png deleted file mode 100644 index 62ae3f5d21..0000000000 Binary files a/_site/android/images/sync-success.png and /dev/null differ diff --git a/_site/android/tutorial.html b/_site/android/tutorial.html deleted file mode 100644 index 576b1246f8..0000000000 --- a/_site/android/tutorial.html +++ /dev/null @@ -1,1236 +0,0 @@ - - - - - - - - -Android SDK Tutorial (Pre-release) | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    - - - - -
    -
    -
    - - - -
    - -
    - -

    Android SDK Tutorial (Pre-release)

    - - - - - -
    - -

    This tutorial is written for readers who are new to either or both Blockstack -and Android to create a decentralized application. It contains the following -content:

    - - - -

    This tutorial was extensively tested using Android Studio 3.1 on a MacBook Air -running High Sierra 10.13.4. If your environment is different, you may encounter -slight or even major discrepancies when performing the procedures in this -tutorial. Please join the Blockstack community -Slack and post questions or comments to -the #support channel.

    - -

    Finally, this tutorial is written for all levels from the beginner to the most -experienced. For best results, beginners should follow the guide as written. It -is expected that the fast or furiously brilliant will skip ahead and improvise -on this material at will. Fair journey one and all.

    - -

    If you prefer, you can skip working through the tutorial all together. Instead, -you can download the final project code and import it -into Android Studio to review it.

    - -

    Understand the sample application flow

    - -

    When complete, the sample application is a simple hello-world display. It is -intended for user on an Android phone.

    - -

    - -

    Only users with an existing blockstack.id can run your -final sample application. When complete, users interact with the sample -application by doing the following:

    - -

    - -

    Set up your environment

    - -

    This sample application has two code bases, a BlockStack hello-blockstack -application and a hello-andriod Android application. Before you start -developing the sample, there are a few elements you need in your environment.

    - -

    Install Android Studio

    - -

    If you are an experienced Android developer and already have an Android -development environment on your workstation, you can use that and skip this -step. However, you will need to adjust the remaining instructions for your -environment.

    - -

    Follow the installation instructions to download and Android Studio -3.1 for your operating system. -Depending on your network connection, this can take between 15-30 minutes.

    - -

    - -

    Do you have npm?

    - -

    The Blockstack code in this tutorial relies on the npm dependency manager. -Before you begin, verify you have installed npm using the which command to -verify.

    - -
    $ which npm
    -/usr/local/bin/npm
    -
    -
    - -

    If you don’t find npm in your system, install -it.

    - -

    Install the Blockstack test rig

    - -

    Users interact with Blockstack-enabled applications through a web browser. You -can BlockStack in test mode, on localhost or you can interact with completed -apps through the Blockstack webapp which is available at -[https://browser.blockstack.org/].

    - -

    If you have already installed Blockstack for testing locally and have an -existing Blockstack ID, skip this section. Otherwise, continue onto install -BlockStack.

    - -
      -
    1. -

      Go to Blockstack

      - -

      -
    2. -
    3. -

      Install the version appropriate for your operating system.

      -
    4. -
    - -

    Use npm to install Yeoman and the Blockstack App Generator

    - -

    You use npm to install Yeoman. Yeoman is a generic scaffolding system that -helps users rapidly start new projects and streamline the maintenance of -existing projects.

    - -
      -
    1. -

      Install Yeoman.

      - -
       npm install -g yo
      -
      -
      -
    2. -
    3. -

      Install the Blockstack application generator.

      - -
       npm install -g generator-blockstack
      -
      -
      -
    4. -
    - -

    Build the Blockstack hello-world

    - -

    In this section, you build a Blockstack hello-world application. Then, you -modify the hello-world to interact with the Android app via a redirect.

    - -

    Generate and launch your hello-blockstack application

    - -

    In this section, you build an initial React.js application called -hello-blockstack.

    - -
      -
    1. -

      Create a hello-blockstack directory.

      - -
       mkdir hello-blockstack
      -
      -
      -
    2. -
    3. -

      Change into your new directory.

      - -
       cd hello-blockstack
      -
      -
      -
    4. -
    5. -

      Use Yeoman and the Blockstack application generator to create your initial hello-blockstack application.

      - -
       yo blockstack:react
      -
      -
      - -

      You should see several interactive prompts.

      - -
       $ yo blockstack:react
      - ==========================================================================
      - We are constantly looking for ways to make yo better!
      - May we anonymously report usage statistics to improve the tool over time?
      - More info: https://github.com/yeoman/insight & http://yeoman.io
      - ========================================================================== No
      -
      -      _-----_     ╭──────────────────────────╮
      -     |       |    │      Welcome to the      │
      -     |--(o)--|    │      Blockstack app      │
      -     ---------    │        generator!        │
      -     ( _U_ )      ╰──────────────────────────╯
      -     /___A___\   /
      -      |  ~  |
      -    __'.___.'__
      -         |°  Y
      -
      - ? Are you ready to build a Blockstack app in React? (Y/n)
      -
      -
      -
    6. -
    7. -

      Respond to the prompts to populate the initial app.

      - -

      After the process completes successfully, you see a prompt similar to the following:

      - -
       [fsevents] Success:
      - "/Users/theuser/repos/hello-blockstack/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node"
      - is installed via remote npm notice created a lockfile as package-lock.json.
      - You should commit this file. added 1060 packages in 26.901s
      -
      -
      -
    8. -
    9. -

      Run the initial application.

      - -
       $ npm start
      -
      - > hello-blockstack@0.0.0 start /Users/moxiegirl/repos/hello-blockstack
      - > webpack-dev-server
      -
      - Project is running at http://localhost:8080/
      - webpack output is served from /
      - 404s will fallback to /index.html
      - Hash: 4d2312ba236a4b95dc3a
      - Version: webpack 2.7.0
      - Time: 2969ms
      -                                  Asset       Size  Chunks                    Chunk Names
      - ....
      -   Child html-webpack-plugin for "index.html":
      -      chunk    {0} index.html 541 kB [entry] [rendered]
      -          [0] ./~/lodash/lodash.js 540 kB {0} [built]
      -          [1] ./~/html-webpack-plugin/lib/loader.js!./src/index.html 533 bytes {0} [built]
      -          [2] (webpack)/buildin/global.js 509 bytes {0} [built]
      -          [3] (webpack)/buildin/module.js 517 bytes {0} [built]
      -  webpack: Compiled successfully.
      -
      -
      - -

      The system opens a browser displaying your running application.

      - -

      - -

      At this point, the browser is running a Blockstack server on your local host. -This is for testing your applications only.

      -
    10. -
    11. -

      Choose Sign in with Blockstack

      - -

      The system displays a prompt allowing you to create a new Blockstack ID or restore an existing one.

      - -

      -
    12. -
    13. -

      Follow the prompts appropriate to your situation.

      - -

      If you are restoring an existing ID, you may see a prompt about your user - being nameless, ignore it. At this point you have only a single application - on your test server. So, you should see this single application, with your - own blockstack.id display name, once you are signed in:

      - -

      -
    14. -
    - -

    Add a redirect end point to your application

    - -

    When a user opens the webapp from the Blockstack browser on an Android phone, -you want the web app to redirect the user to your Android application. The work -you do here will allow it.

    - -
      -
    1. -

      From the terminal command line, change directory to the root of your sample -application directory.

      -
    2. -
    3. -

      Use the touch command to add a redirect endpoint to your application.

      - -

      This endpoint on the web version of your app will redirect Android users back - to your mobile app.

      - -
       $ touch public/redirect.html
      -
      -
      -
    4. -
    5. -

      Open redirect.html and add code to the endpoint.

      - -
       <!DOCTYPE html>
      - <html>
      - <head>
      -   <title>Hello, Blockstack!</title>
      -   <script>
      -   function getParameterByName(name) {
      -     var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
      -     return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
      -   }
      -
      -   var authResponse = getParameterByName('authResponse')
      -   window.location="myblockstackapp:" + authResponse
      -   </script>
      - <body>
      - </body>
      - </html>
      -
      -
      - -

      Blockstack apps are identified by their domain names. The endpoint will - receive a get request with the query parameter authResponse=XXXX and - should redirect the browser to myblockstackapp:XXXX.

      - -

      myblockstackapp: is custom protocol handler. The handler should be unique - to your application. Your app’s web-based authentication uses this handler - to redirect the user back to your Android app. Later, you’ll add a reference - to this handler in your Android application.

      -
    6. -
    7. Close and save the redirect.html file.
    8. -
    9. Ensure your Blockstack compiles successfully.
    10. -
    - -

    Create the hello-android project

    - -

    In this section, you’ll create an Android application in Android Studio. You’ll -run the application in the emulator to test it.

    - -

    Create a simple project

    - -

    In this section, you create an inital project. You’ll validate the application’s -iniatial state by creating an emulator to run it in. Open Android Studio and do the following:

    - -
      -
    1. -

      Open Android Studio and choose Start a new Andriod Studio project.

      - -

      If studio is already started, choose File > New > New Project.

      -
    2. -
    3. -

      Enter these fields in the Create Android Project page.

      - - - - - - - - - - - - - - - - - - -
      Application Namehello-android
      Company domainUSERNAME.example.com
      Project location/Users/USERNAME/AndroidStudioProjects/helloandroid
      Include Kotlin supportSet (checked)
      -
    4. -
    5. Press Next to display Target Android Devices.
    6. -
    7. Check Phone and Tablet.
    8. -
    9. Choose API 27: Andriod 8.1 (Oreo) for the target version.
    10. -
    11. Press Next.
    12. -
    13. Choose Empty Activity and press Next.
    14. -
    15. -

      Leave the Configure Activity dialog with its defaults.

      - -

      -
    16. -
    17. -

      Press Finish.

      - -

      Android studio builds your initial project. This can take a bit the first time you do it.

      - -

      -
    18. -
    - -

    Run the app in an emulator

    - -

    In this section, you run the appliation and create an emulator when prompted.

    - -
      -
    1. -

      Once the project is imported into studio, click the app module in the Project window.

      -
    2. -
    3. -

      Then, select Run > Run (or click the green arrow in the toolbar).

      - -

      Studio prompts you to Select Deployment Target.

      -
    4. -
    5. -

      Choose Create New Virtual Device and press OK.

      - -

      Studio prompts you to Select Hardware.

      -
    6. -
    7. -

      Choose a Phone running Pixel XL.

      - -

      - -

      Studio prompts you for a system image.

      -
    8. -
    9. -

      Choose Oreo which is API level 27 and press Next.

      - -

      - -

      Studio asks you to verify your new emulator configuration.

      -
    10. -
    11. -

      Press Finish.

      - -

      The emulation takes a moment to build. Then, studio launches the emulation and opens your application.

      - -

      -
    12. -
    - -

    Configure your application with the Blockstack SDK

    - -

    Now that you have created your initial project and verified it running in an emulator, you are ready to begin configuring the application for use with Blockstack.

    - -
      -
    1. In studio, open the AndroidManifest.xml file.
    2. -
    3. -

      Add an <intent-filter> with the custom handler for Blockstack.

      - -
       <intent-filter>
      -   <action android:name="android.intent.action.VIEW" />
      -   <category android:name="android.intent.category.DEFAULT" />
      -   <category android:name="android.intent.category.BROWSABLE" />
      -   <data android:scheme="myblockstackapp" />
      -  </intent-filter>
      -
      -
    4. -
    5. Open the Project’s build.gradle file.
    6. -
    7. -

      Add the Jitpack repository maven { url 'https://jitpack.io' } to the repositories section.

      - -

      When you finish, that section looks like this:

      - -
       allprojects {
      -   repositories {
      -       google()
      -       jcenter()
      -       maven { url 'https://jitpack.io' }
      -   }
      - }
      -
      -
    8. -
    9. Open the Module build.gradle file.
    10. -
    11. -

      Set the defaultConfig minSdkVersion to 19.

      - -

      When you are done, you should see (within your own username not moxiegirl):

      - -
      android {
      -    compileSdkVersion 27
      -        defaultConfig {
      -            applicationId "com.example.moxiegirl.hello_android"
      -            minSdkVersion 19
      -            targetSdkVersion 27
      -            versionCode 1
      -            versionName "1.0"
      -            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
      -        }
      -   ...
      -}
      -
      -
    12. -
    13. -

      Below this, add the Blockstack Android SDK dependency to your project’s dependencies list:

      - -

      When you are done you should see:

      - -
       dependencies {
      -     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
      -     implementation 'com.android.support:appcompat-v7:27.1.0'
      -     implementation 'com.android.support.constraint:constraint-layout:1.1.2'
      -     testImplementation 'junit:junit:4.12'
      -     androidTestImplementation 'com.android.support.test:runner:1.0.2'
      -     androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
      -     implementation 'com.github.blockstack:blockstack-android:0.3.0'
      - }
      -
      -
      - -

      NOTE: Ignore the warning on the appcompat` dependencies.

      -
    14. -
    15. -

      Sync your project.

      - -

      - -

      Be sure to check the sync completed successfully.

      - -

      -
    16. -
    17. -

      Run your app in the emulator.

      - -

      You’ve made a lot of changes, make sure the emulator is still running -correctly.

      -
    18. -
    - -

    Add a simple interface

    - -
      -
    1. -

      Open the app/res/layout/activity_main.xml file.

      - -

      The activity_main.xml file defines the graphical elements. Some elements are required before you can functionality to your MainActivity.kt code.

      -
    2. -
    3. -

      Replace the entire content of the file with the following code:

      - -

      The new interface includes a BlockstackSignInButton which is provided by -the SDK. This SDK includes a themed “Sign in with Blockstack” button -(BlockstackSignInButton). You use this button in your here with the -org.blockstack.android.sdk.ui.BlockstackSignInButton class.

      - -
       <?xml version="1.0" encoding="utf-8"?>
      - <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
      - xmlns:app="http://schemas.android.com/apk/res-auto"
      - xmlns:tools="http://schemas.android.com/tools"
      - android:layout_width="match_parent"
      - android:layout_height="match_parent"
      - tools:context=".MainActivity">
      -
      - <org.blockstack.android.sdk.ui.BlockstackSignInButton
      -     android:id="@+id/signInButton"
      -     android:layout_width="307dp"
      -     android:layout_height="45dp"
      -     android:layout_margin="4dp"
      -     android:layout_marginEnd="185dp"
      -     android:layout_marginStart="39dp"
      -     app:layout_constraintEnd_toEndOf="parent"
      -     app:layout_constraintStart_toStartOf="parent"
      -     tools:layout_editor_absoluteY="16dp" />
      -
      - <TextView
      -     android:id="@+id/userDataTextView"
      -     android:layout_width="370dp"
      -     android:layout_height="27dp"
      -     tools:layout_editor_absoluteX="6dp"
      -     tools:layout_editor_absoluteY="70dp" />
      -
      - </android
      -
      - -

      This codes adds a button and some text to your application.

      -
    4. -
    5. -

      Choose Run > Apply changes.

      -
    6. -
    7. -

      Choose Run > Run app in the emulator.

      - -

      The emulator now contains a new interface with a button:

      - -

      -
    8. -
    - -

    Add session & authentication code

    - -
      -
    1. Open the MainActivity.kt file.
    2. -
    3. -

      Add some additional imports to the top below the android.os.Bundle import.

      - -

      When you are done, your imports should appear as follows:

      - -
      
      - import android.support.v7.app.AppCompatActivity
      - import android.os.Bundle
      -
      - import android.support.v7.app.AppCompatActivity
      - import android.view.View
      - import kotlinx.android.synthetic.main.activity_main.*
      - import org.blockstack.android.sdk.BlockstackSession
      - import org.blockstack.android.sdk.Scope
      - import org.blockstack.android.sdk.UserData
      - import java.net.URI
      -
      -
      -
    4. -
    5. -

      Add a variable for the Blockstack session before onCreate.

      - -
      class MainActivity : AppCompatActivity() {
      -
      - private var _blockstackSession: BlockstackSession? = null
      -
      -
      - override fun onCreate(savedInstanceState: Bundle?) {
      -     super.onCreate(savedInstanceState)
      -     setContentView(R.layout.activity_main)
      -   }
      - }
      -
      -
      -
    6. -
    7. -

      Replae the existing the onCreate function with the following:

      - -
       override fun onCreate(savedInstanceState: Bundle?) {
      -     super.onCreate(savedInstanceState)
      -     setContentView(R.layout.activity_main)
      -
      -     signInButton.isEnabled = false
      -
      -     val appDomain = URI("https://flamboyant-darwin-d11c17.netlify.com")
      -     val redirectURI = URI("${appDomain}/redirect")
      -     val manifestURI = URI("${appDomain}/manifest.json")
      -     val scopes = arrayOf(Scope.StoreWrite)
      -
      - val config = java.net.URI("https://flamboyant-darwin-d11c17.netlify.com").run {
      -         org.blockstack.android.sdk.BlockstackConfig(
      -                 this,
      -                 redirectURI,
      -                 manifestURI,
      -                 scopes
      -     }
      -
      -     _blockstackSession = BlockstackSession(this, config,
      -             onLoadedCallback = {
      -                 signInButton.isEnabled = true
      -             })
      -
      -
      -     signInButton.setOnClickListener { view: View ->
      -         blockstackSession().redirectUserToSignIn { userData ->
      -             if (userData.hasValue) {
      -                 runOnUiThread {
      -                     onSignIn(userData.value!!)
      -                 }
      -             }
      -         }
      -     }
      -     if (intent?.action == Intent.ACTION_VIEW) {
      -         // handle the redirect from sign in
      -         handleAuthResponse(intent)
      -     }
      - }
      -
      -
      - -

      This new onCreate does several things:

      - -
        -
      • Define the initial state for the signInButton.
      • -
      • Supply authentication information for connecting to your Blockstack app: appDomain, redirectURI, manifestURI and scopes
      • -
      • Add a listener for the button click.
      • -
      - -

      Notice that the application in this example is a URI you have not set up. - Registering and application name takes time, so in time’s interest you’ll - use an existing app that is identical to the hello-world you created - earlier. For a produciton verison, you’ll need to replace appDomain, - redirectURI, manifestURI and scopes with values appropriate for your - app.

      -
    8. -
    9. -

      Add a private function to reflect when a user successfully signs in.

      - -
       private fun onSignIn(userData: UserData) {
      - 		userDataTextView.text = "Signed in as ${userData.decentralizedID}"
      -
      - 		signInButton.isEnabled = false
      -
      - }
      -
      -
      -
    10. -
    11. -

      Handle sign in requests with an onNewIntent function if the activity was already opened when signing in

      - -

      Retrieve the authentication token from the custom protocol handler call and - send it to the Blockstack session.

      - -
       override fun onNewIntent(intent: Intent?) {
      -   super.onNewIntent(intent)
      -
      -   if (intent?.action == Intent.ACTION_VIEW) {
      -       handleAuthResponse(intent)
      -   }
      - }
      -
      -
      -
    12. -
    13. -

      Create a handler for the authentication response.

      - -
       private fun handleAuthResponse(intent: Intent) {
      -    val response = intent.dataString
      -    if (response != null) {
      -        val authResponseTokens = response.split(':')
      -
      -        if (authResponseTokens.size > 1) {
      -            val authResponse = authResponseTokens[1]
      -
      -            blockstackSession().handlePendingSignIn(authResponse, { userData ->
      -                if (userData.hasValue) {
      -                   // The user is now signed in!
      -                   runOnUiThread {
      -                      onSignIn(userData.value!!)
      -                   }
      -                }
      -            })
      -        }
      -    }
      - }
      -
      -
      -
    14. -
    15. -

      Add the convenience method to access the blockstack session.

      - -
       fun blockstackSession() : BlockstackSession {
      -   val session = _blockstackSession
      -    if(session != null) {
      -       return session
      -    } else {
      -     throw IllegalStateException("No session.")
      -   }
      - }
      -
      -
      -
    16. -
    17. -

      Verify your final MainActivity.kt code looks like this:

      - -
       class MainActivity : AppCompatActivity() {
      -
      -     private var _blockstackSession: BlockstackSession? = null
      -
      -
      -     override fun onCreate(savedInstanceState: Bundle?) {
      -         super.onCreate(savedInstanceState)
      -         setContentView(R.layout.activity_main)
      -
      -         signInButton.isEnabled = false
      -
      -         val appDomain = URI("https://flamboyant-darwin-d11c17.netlify.com")
      -         val redirectURI = URI("${appDomain}/redirect")
      -         val manifestURI = URI("${appDomain}/manifest.json")
      -         val scopes = arrayOf(Scope.StoreWrite)
      -
      -         _blockstackSession = BlockstackSession(this, appDomain, redirectURI, manifestURI, scopes,
      -                 onLoadedCallback = {signInButton.isEnabled = true
      -                 })
      -
      -
      -         signInButton.setOnClickListener { view: View ->
      -             blockstackSession().redirectUserToSignIn { userData ->
      -                 if (userData.hasValue) {
      -                     runOnUiThread {
      -                         onSignIn(userData)
      -                     }
      -                 }
      -             }
      -         }
      -         if (intent?.action == Intent.ACTION_VIEW) {
      -             handleAuthResponse(intent)
      -         }
      -     }
      -
      -     private fun onSignIn(userData: UserData) {
      -         userDataTextView.text = "Signed in as ${userData.decentralizedID}"
      -
      -         signInButton.isEnabled = false
      -
      -     }
      -
      -     override fun onNewIntent(intent: Intent?) {
      -         super.onNewIntent(intent)
      -
      -         if (intent?.action == Intent.ACTION_VIEW) {
      -             handleAuthResponse(intent)
      -         }
      -     }
      -
      -     private fun handleAuthResponse(intent: Intent) {
      -         val response = intent.dataString
      -         if (response != null) {
      -             val authResponseTokens = response.split(':')
      -
      -             if (authResponseTokens.size > 1) {
      -                 val authResponse = authResponseTokens[1]
      -
      -                 blockstackSession().handlePendingSignIn(authResponse, { userData ->
      -                     if (userData.hasValue) {
      -                         // The user is now signed in!
      -                         runOnUiThread {
      -                             onSignIn(userData.value!!)
      -                         }
      -                     }
      -                 })
      -             }
      -         }
      -     }
      -
      -     fun blockstackSession() : BlockstackSession {
      -         val session = _blockstackSession
      -         if(session != null) {
      -             return session
      -         } else {
      -             throw IllegalStateException("No session.")
      -         }
      -     }
      -
      -
      - }
      -
      -
      -
    18. -
    - -

    Run the final app in the emulator

    - -
      -
    1. Choose Run > Apply changes.
    2. -
    3. Choose Run > Run app in the emulator.
    4. -
    5. -

      When you see the application open, choose Sign in with Blockstack.

      - -

      The system prompts you how to open.

      - -

      -
    6. -
    7. Choose Chrome and click ALWAYS.
    8. -
    9. -

      Move through the rest of the Chrome prompts.

      - -

      The system presents you with your final application.

      - -

      -
    10. -
    11. Work through the Blockstack prompts to login.
    12. -
    - -

    Where to go next

    - -

    Congratulations, you’ve completed your Android app using the new, pre-release -Blockstack Android SDK. Thank you for trying this pre-release. Please let us -know about your experience by tweeting to -@blockstack.

    - -

    Learn more about Blockstack by trying another tutorial.

    - - - -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -

    Related Articles

    - - - - - -
      - -
    -
    - - - -
    - - - -
    - -
    -
    - - -
    -
    - - - - - - - -
    -
    - - - -
    -
    - -
    - -
    -
    -
    - -
    - -
    - - -
    - -
    - - - - -
    - -
    - - - - - -
    - -
    - - - - - - -
    -
    - - -
    -
    - - - - - - - - - - - diff --git a/_site/assets/css/main.css b/_site/assets/css/main.css deleted file mode 100644 index a7790286bd..0000000000 --- a/_site/assets/css/main.css +++ /dev/null @@ -1 +0,0 @@ -/*! system-font.css v2.0.2 | CC0-1.0 License | github.com/jonathantneal/system-font-css */@font-face{font-family:system-ui;font-style:normal;font-weight:300;src:local(".SFNSText-Light"),local(".HelveticaNeueDeskInterface-Light"),local(".LucidaGrandeUI"),local("Segoe UI Light"),local("Ubuntu Light"),local("Roboto-Light"),local("DroidSans"),local("Tahoma")}@font-face{font-family:system-ui;font-style:italic;font-weight:300;src:local(".SFNSText-LightItalic"),local(".HelveticaNeueDeskInterface-Italic"),local(".LucidaGrandeUI"),local("Segoe UI Light Italic"),local("Ubuntu Light Italic"),local("Roboto-LightItalic"),local("DroidSans"),local("Tahoma")}@font-face{font-family:system-ui;font-style:normal;font-weight:400;src:local(".SFNSText-Regular"),local(".HelveticaNeueDeskInterface-Regular"),local(".LucidaGrandeUI"),local("Segoe UI"),local("Ubuntu"),local("Roboto-Regular"),local("DroidSans"),local("Tahoma")}@font-face{font-family:system-ui;font-style:italic;font-weight:400;src:local(".SFNSText-Italic"),local(".HelveticaNeueDeskInterface-Italic"),local(".LucidaGrandeUI"),local("Segoe UI Italic"),local("Ubuntu Italic"),local("Roboto-Italic"),local("DroidSans"),local("Tahoma")}@font-face{font-family:system-ui;font-style:normal;font-weight:500;src:local(".SFNSText-Medium"),local(".HelveticaNeueDeskInterface-MediumP4"),local(".LucidaGrandeUI"),local("Segoe UI Semibold"),local("Ubuntu Medium"),local("Roboto-Medium"),local("DroidSans-Bold"),local("Tahoma Bold")}@font-face{font-family:system-ui;font-style:italic;font-weight:500;src:local(".SFNSText-MediumItalic"),local(".HelveticaNeueDeskInterface-MediumItalicP4"),local(".LucidaGrandeUI"),local("Segoe UI Semibold Italic"),local("Ubuntu Medium Italic"),local("Roboto-MediumItalic"),local("DroidSans-Bold"),local("Tahoma Bold")}@font-face{font-family:system-ui;font-style:normal;font-weight:700;src:local(".SFNSText-Bold"),local(".HelveticaNeueDeskInterface-Bold"),local(".LucidaGrandeUI"),local("Segoe UI Bold"),local("Ubuntu Bold"),local("Roboto-Bold"),local("DroidSans-Bold"),local("Tahoma Bold")}@font-face{font-family:system-ui;font-style:italic;font-weight:700;src:local(".SFNSText-BoldItalic"),local(".HelveticaNeueDeskInterface-BoldItalic"),local(".LucidaGrandeUI"),local("Segoe UI Bold Italic"),local("Ubuntu Bold Italic"),local("Roboto-BoldItalic"),local("DroidSans-Bold"),local("Tahoma Bold")}.sidebar-fixed-width{width:260px}.sidebar-docs{width:220px;padding-right:40px;top:112px;bottom:70px;overflow-y:scroll;overflow-x:hidden}.sidebar-docs>h5{margin:15px 0 0}.sidebar-docs>h5:first-child{margin-top:17px}@media (min-width: 1200px){.sidebar-fixed-width{width:360px}.sidebar-docs{width:290px;padding-right:70px}}ul.doc-nav{padding-left:14px;margin-top:5px}.doc-nav>li.uk-active>a{position:relative}.doc-nav>li.uk-active>a:before{content:"";position:absolute;top:15px;left:-14px;width:7px;border-top:1px solid #0F1214}.hero-image img{max-width:200px;max-height:75px}.button-cta:nth-child(2n),.button-cta:nth-child(3n){margin-top:20px}.heading-hero-2{font-size:1.875rem}@media (min-width: 640px){.heading-hero-2{font-size:1.375rem}}@media (min-width: 960px){.heading-hero-2{font-size:1.75rem}}.list-featured>li:first-child{margin-top:20px}@media (min-width: 640px){.tm-timeline{box-sizing:border-box;position:relative}.tm-timeline *{box-sizing:border-box}.tm-timeline:before{content:'';position:absolute;top:0;left:calc(30% - 2px);bottom:0;width:4px;background:#0F1214}.tm-timeline:after{content:"";display:table;clear:both}.tm-timeline-entry{clear:both;text-align:left;position:relative}.tm-timeline-entry+.tm-timeline-entry{margin-top:70px}.tm-timeline-entry:after{display:block;content:"";clear:both}.tm-timeline-entry .tm-timeline-time{float:left;width:30%;padding-right:70px;text-align:right;position:relative}.tm-timeline-entry .tm-timeline-time:before{content:'';position:absolute;width:20px;height:20px;border:4px solid #0F1214;background-color:#fff;border-radius:100%;top:0;right:-14px;z-index:99}.tm-timeline-entry .tm-timeline-time h5{margin:3px 0 0}.tm-timeline-entry .tm-timeline-body{float:right;width:70%;padding-left:70px;margin-top:-2px}.tm-timeline-entry .tm-timeline-body h3{margin:0 0 15px}.tm-timeline-entry .tm-timeline-body h3 span{font-size:.7rem;margin-bottom:4px;padding:0 5px}}html{font-family:system-ui;font-size:16px;font-weight:normal;line-height:1.5;-webkit-text-size-adjust:100%;background:#fff;color:#0F1214;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}body{margin:0}a{-webkit-text-decoration-skip:objects}a:active,a:hover{outline:none}a,.uk-link{color:#7a838a;text-decoration:none;cursor:pointer}a:hover,.uk-link:hover{color:#000;text-decoration:underline}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}:not(pre)>code,:not(pre)>kbd,:not(pre)>samp{font-family:Consolas,monaco,monospace;font-size:.875rem;color:#f0506e;white-space:nowrap;padding:2px 6px;background:#f8f8f8}em{color:#f0506e}ins{background:#ffd;color:#666;text-decoration:none}mark{background:#ffd;color:#666}q{font-style:italic}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}audio,canvas,iframe,img,svg,video{vertical-align:middle}audio,canvas,img,video{max-width:100%;height:auto;box-sizing:border-box}svg:not(:root){overflow:hidden}p,ul,ol,dl,pre,address,fieldset,figure{margin:0 0 20px 0}*+p,*+ul,*+ol,*+dl,*+pre,*+address,*+fieldset,*+figure{margin-top:20px}h1,.uk-h1,h2,.uk-h2,h3,.uk-h3,h4,.uk-h4,h5,.uk-h5,h6,.uk-h6{margin:0 0 20px 0;font-family:system-ui;font-weight:bold;color:#0F1214;text-transform:none}*+h1,*+.uk-h1,*+h2,*+.uk-h2,*+h3,*+.uk-h3,*+h4,*+.uk-h4,*+h5,*+.uk-h5,*+h6,*+.uk-h6{margin-top:50px}h1,.uk-h1{font-size:1.875rem;line-height:1.2}h2,.uk-h2{font-size:1.625rem;line-height:1.4}h3,.uk-h3{font-size:1.375rem;line-height:1.4}h4,.uk-h4{font-size:1.125rem;line-height:1.4}h5,.uk-h5{font-size:16px;line-height:1.4}h6,.uk-h6{font-size:.875rem;line-height:1.4}ul,ol{padding-left:30px}ul>li>ul,ul>li>ol,ol>li>ol,ol>li>ul{margin:0}dt{font-weight:bold}dd{margin-left:0}hr,.uk-hr{box-sizing:content-box;height:0;overflow:visible;text-align:inherit;margin:0 0 20px 0;border:0;border-top:1px solid #e7e8e9}*+hr,*+.uk-hr{margin-top:20px}address{font-style:normal}blockquote{margin:0 0 20px 0;font-size:1.125rem;line-height:1.5;font-style:italic;color:#333}*+blockquote{margin-top:20px}blockquote p:last-of-type{margin-bottom:0}blockquote footer{margin-top:10px;font-size:.875rem;line-height:1.5;color:#666}blockquote footer::before{content:"— "}pre{font:.875rem / 1.5 Consolas,monaco,monospace;color:#666;-moz-tab-size:4;tab-size:4;overflow:auto;padding:10px;border:1px solid #c4c7ca;border-radius:3px;background:#fff}pre code{font-family:Consolas,monaco,monospace}::-moz-selection{background:#39f;color:#fff;text-shadow:none}::selection{background:#39f;color:#fff;text-shadow:none}details,main{display:block}summary{display:list-item}template{display:none}iframe{border:0}a,area,button,input,label,select,summary,textarea{touch-action:manipulation}.var-media-s::before{content:"640px"}.var-media-m::before{content:"960px"}.var-media-l::before{content:"1200px"}.var-media-xl::before{content:"1600px"}input[type="submit" i]{-webkit-appearance:none}a.uk-link-muted,.uk-link-muted a{color:#7a838a}a.uk-link-muted:hover,.uk-link-muted a:hover{color:#0F1214}a.uk-link-text:not(:hover),.uk-link-text a:not(:hover){color:inherit}a.uk-link-text:hover,.uk-link-text a:hover{color:#7a838a}a.uk-link-heading:not(:hover),.uk-link-heading a:not(:hover){color:inherit}a.uk-link-heading:hover,.uk-link-heading a:hover{color:#0F1214;text-decoration:none}a.uk-link-reset,a.uk-link-reset:hover,.uk-link-reset a,.uk-link-reset a:hover{color:inherit !important;text-decoration:none !important}.uk-heading-primary{font-size:1.875rem;line-height:1.2}@media (min-width: 960px){.uk-heading-primary{font-size:3.75rem;line-height:1.1}}.uk-heading-hero{font-size:2.375rem;line-height:1.3;font-weight:bold}@media (min-width: 640px){.uk-heading-hero{font-size:2.9375rem;line-height:1.3}}@media (min-width: 960px){.uk-heading-hero{font-size:3.625rem;line-height:1.4}}.uk-heading-divider{padding-bottom:10px;border-bottom:1px solid #c4c7ca}.uk-heading-bullet{position:relative}.uk-heading-bullet::before{content:"";display:inline-block;position:relative;top:calc(-0.1 * 1em);vertical-align:middle;height:.9em;margin-right:10px;border-left:5px solid #c4c7ca}.uk-heading-line{overflow:hidden}.uk-heading-line>*{display:inline-block;position:relative}.uk-heading-line>::before,.uk-heading-line>::after{content:"";position:absolute;top:calc(50% - (1px / 2));width:2000px;border-bottom:1px solid #c4c7ca}.uk-heading-line>::before{right:100%;margin-right:.6em}.uk-heading-line>::after{left:100%;margin-left:.6em}[class*='uk-divider']{border:none;margin-bottom:20px}*+[class*='uk-divider']{margin-top:20px}.uk-divider-icon{position:relative;height:20px;background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22none%22%20stroke%3D%22%23c4c7ca%22%20stroke-width%3D%222%22%20cx%3D%2210%22%20cy%3D%2210%22%20r%3D%227%22%20%2F%3E%0A%3C%2Fsvg%3E%0A");background-repeat:no-repeat;background-position:50% 50%}.uk-divider-icon::before,.uk-divider-icon::after{content:"";position:absolute;top:50%;max-width:calc(50% - (50px / 2));border-bottom:1px solid #c4c7ca}.uk-divider-icon::before{right:calc(50% + (50px / 2));width:100%}.uk-divider-icon::after{left:calc(50% + (50px / 2));width:100%}.uk-divider-small{line-height:0}.uk-divider-small::after{content:"";display:inline-block;width:100px;max-width:100%;border-top:1px solid #c4c7ca;vertical-align:top}.uk-list{padding:0;list-style:none}.uk-list>li::before,.uk-list>li::after{content:"";display:table}.uk-list>li::after{clear:both}.uk-list>li>:last-child{margin-bottom:0}.uk-list ul{margin:0;padding-left:30px;list-style:none}.uk-list>li:nth-child(n+2),.uk-list>li>ul{margin-top:10px}.uk-list-divider>li:nth-child(n+2){margin-top:10px;padding-top:10px;border-top:1px solid #c4c7ca}.uk-list-striped>li{padding:10px 10px}.uk-list-striped>li:nth-of-type(odd){border-top:1px solid #c4c7ca;border-bottom:1px solid #c4c7ca}.uk-list-striped>li:nth-of-type(odd){background:#f8f8f8}.uk-list-striped>li:nth-child(n+2){margin-top:0}.uk-list-bullet>li{position:relative;padding-left:calc(1.5em + 10px)}.uk-list-bullet>li::before{content:"";position:absolute;top:0;left:0;width:1.5em;height:1.5em;background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%226%22%20height%3D%226%22%20viewBox%3D%220%200%206%206%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22%23666%22%20cx%3D%223%22%20cy%3D%223%22%20r%3D%223%22%20%2F%3E%0A%3C%2Fsvg%3E");background-repeat:no-repeat;background-position:50% 50%;float:left}.uk-list-large>li:nth-child(n+2),.uk-list-large>li>ul{margin-top:20px}.uk-list-large.uk-list-divider>li:nth-child(n+2){margin-top:20px;padding-top:20px}.uk-list-large.uk-list-striped>li{padding:20px 10px}.uk-list-large.uk-list-striped>li:nth-of-type(odd){border-top:1px solid #c4c7ca;border-bottom:1px solid #c4c7ca}.uk-list-large.uk-list-striped>li:nth-child(n+2){margin-top:0}.uk-list{margin:0}.uk-icon{margin:0;border:none;border-radius:0;overflow:visible;font:inherit;color:inherit;text-transform:none;padding:0;background-color:transparent;display:inline-block;fill:currentcolor;line-height:0}button.uk-icon:not(:disabled){cursor:pointer}.uk-icon::-moz-focus-inner{border:0;padding:0}.uk-icon [fill*='#']:not(.uk-preserve),.uk-icon [FILL*='#']:not(.uk-preserve){fill:currentcolor}.uk-icon [stroke*='#']:not(.uk-preserve),.uk-icon [STROKE*='#']:not(.uk-preserve){stroke:currentcolor}.uk-icon>*{transform:translate(0, 0)}.uk-icon-image{width:20px;height:20px;background-position:50% 50%;background-repeat:no-repeat;background-size:contain;vertical-align:middle}.uk-icon-link{color:#7a838a}.uk-icon-link:hover,.uk-icon-link:focus{color:#666;outline:none}.uk-icon-link:active,.uk-active>.uk-icon-link{color:#595959}.uk-icon-button{box-sizing:border-box;width:36px;height:36px;border-radius:500px;background:#f8f8f8;color:#7a838a;vertical-align:middle;display:inline-flex;justify-content:center;align-items:center;transition:0.1s ease-in-out;transition-property:color,background-color}.uk-icon-button:hover,.uk-icon-button:focus{background-color:#ebebeb;color:#666;outline:none}.uk-icon-button:active,.uk-active>.uk-icon-button{background-color:#dfdfdf;color:#666}.uk-input,.uk-select,.uk-textarea,.uk-radio,.uk-checkbox{box-sizing:border-box;margin:0;border-radius:0;font:inherit}.uk-input{overflow:visible}.uk-select{text-transform:none}.uk-select optgroup{font:inherit;font-weight:bold}.uk-textarea{overflow:auto}.uk-input[type="search"]::-webkit-search-cancel-button,.uk-input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}.uk-input[type="number"]::-webkit-inner-spin-button,.uk-input[type="number"]::-webkit-outer-spin-button{height:auto}.uk-input::-moz-placeholder,.uk-textarea::-moz-placeholder{opacity:1}.uk-radio:not(:disabled),.uk-checkbox:not(:disabled){cursor:pointer}.uk-fieldset{border:none;margin:0;padding:0}.uk-input,.uk-textarea{-webkit-appearance:none}.uk-input,.uk-select,.uk-textarea{max-width:100%;width:100%;border:0 none;padding:0 10px;background:#fff;color:#666;border:solid 1px #c4c7ca}.uk-input,.uk-select:not([multiple]):not([size]){height:40px;vertical-align:middle;display:inline-block}.uk-input:not(input),.uk-select:not(select){line-height:38px}.uk-select[multiple],.uk-select[size],.uk-textarea{padding-top:4px;padding-bottom:4px;vertical-align:top}.uk-input:focus,.uk-select:focus,.uk-textarea:focus{outline:none;background-color:#fff;color:#666;border-color:#0F1214}.uk-input:disabled,.uk-select:disabled,.uk-textarea:disabled{background-color:#f8f8f8;color:#7a838a;border-color:#c4c7ca}.uk-input:-ms-input-placeholder{color:#7a838a !important}.uk-input::placeholder{color:#7a838a}.uk-textarea:-ms-input-placeholder{color:#7a838a !important}.uk-textarea::placeholder{color:#7a838a}.uk-form-small{font-size:.875rem}.uk-form-small:not(textarea):not([multiple]):not([size]){height:30px;padding-left:8px;padding-right:8px}.uk-form-small:not(select):not(input):not(textarea){line-height:28px}.uk-form-large{font-size:1.125rem}.uk-form-large:not(textarea):not([multiple]):not([size]){height:55px;padding-left:12px;padding-right:12px}.uk-form-large:not(select):not(input):not(textarea){line-height:53px}.uk-form-danger,.uk-form-danger:focus{color:#f0506e;border-color:#f0506e}.uk-form-success,.uk-form-success:focus{color:#32d296;border-color:#32d296}.uk-form-blank{background:none;border-color:transparent}.uk-form-blank:focus{border-color:#c4c7ca;border-style:dashed}input.uk-form-width-xsmall{width:50px}select.uk-form-width-xsmall{width:75px}.uk-form-width-small{width:130px}.uk-form-width-medium{width:200px}.uk-form-width-large{width:500px}.uk-select:not([multiple]):not([size]){-webkit-appearance:none;-moz-appearance:none;padding-right:20px;background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2224%22%20height%3D%2216%22%20viewBox%3D%220%200%2024%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%23666%22%20points%3D%2212%201%209%206%2015%206%22%20%2F%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%23666%22%20points%3D%2212%2013%209%208%2015%208%22%20%2F%3E%0A%3C%2Fsvg%3E%0A");background-repeat:no-repeat;background-position:100% 50%}.uk-select:not([multiple]):not([size])::-ms-expand{display:none}.uk-select:not([multiple]):not([size]):disabled{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2224%22%20height%3D%2216%22%20viewBox%3D%220%200%2024%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%237a838a%22%20points%3D%2212%201%209%206%2015%206%22%20%2F%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%237a838a%22%20points%3D%2212%2013%209%208%2015%208%22%20%2F%3E%0A%3C%2Fsvg%3E%0A")}.uk-radio,.uk-checkbox{display:inline-block;height:16px;width:16px;overflow:hidden;margin-top:-4px;vertical-align:middle;-webkit-appearance:none;-moz-appearance:none;background-color:transparent;background-repeat:no-repeat;background-position:50% 50%;border:1px solid #a9aeb2;transition:0.2s ease-in-out;transition-property:background-color, border}.uk-radio{border-radius:50%}.uk-radio:focus,.uk-checkbox:focus{outline:none;border-color:#0F1214}.uk-radio:checked,.uk-checkbox:checked,.uk-checkbox:indeterminate{background-color:#0F1214;border-color:transparent}.uk-radio:checked:focus,.uk-checkbox:checked:focus,.uk-checkbox:indeterminate:focus{background-color:#000}.uk-radio:checked{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22%23fff%22%20cx%3D%228%22%20cy%3D%228%22%20r%3D%222%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-checkbox:checked{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2211%22%20viewBox%3D%220%200%2014%2011%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%23fff%22%20points%3D%2212%201%205%207.5%202%205%201%205.5%205%2010%2013%201.5%22%20%2F%3E%0A%3C%2Fsvg%3E%0A")}.uk-checkbox:indeterminate{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22%23fff%22%20x%3D%223%22%20y%3D%228%22%20width%3D%2210%22%20height%3D%221%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-radio:disabled,.uk-checkbox:disabled{background-color:#f8f8f8;border-color:#c4c7ca}.uk-radio:disabled:checked{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22%237a838a%22%20cx%3D%228%22%20cy%3D%228%22%20r%3D%222%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-checkbox:disabled:checked{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2211%22%20viewBox%3D%220%200%2014%2011%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%237a838a%22%20points%3D%2212%201%205%207.5%202%205%201%205.5%205%2010%2013%201.5%22%20%2F%3E%0A%3C%2Fsvg%3E%0A")}.uk-checkbox:disabled:indeterminate{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22%237a838a%22%20x%3D%223%22%20y%3D%228%22%20width%3D%2210%22%20height%3D%221%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-legend{width:100%;color:inherit;padding:0;font-size:1.375rem;line-height:1.4}.uk-form-custom{display:inline-block;position:relative;max-width:100%;vertical-align:middle}.uk-form-custom select,.uk-form-custom input[type="file"]{position:absolute;top:0;z-index:1;width:100%;height:100%;left:0;-webkit-appearance:none;opacity:0;cursor:pointer}.uk-form-custom input[type="file"]{font-size:500px;overflow:hidden}.uk-form-label{color:#333;font-size:.875rem}.uk-form-stacked .uk-form-label{display:block;margin-bottom:5px}@media (max-width: 959px){.uk-form-horizontal .uk-form-label{display:block;margin-bottom:5px}}@media (min-width: 960px){.uk-form-horizontal .uk-form-label{width:200px;margin-top:7px;float:left}.uk-form-horizontal .uk-form-controls{margin-left:215px}.uk-form-horizontal .uk-form-controls-text{padding-top:7px}}.uk-form-icon{position:absolute;top:0;bottom:0;left:0;width:40px;display:inline-flex;justify-content:center;align-items:center;color:#7a838a}.uk-form-icon:hover{color:#666}.uk-form-icon:not(a):not(button):not(input){pointer-events:none}.uk-form-icon:not(.uk-form-icon-flip)+.uk-input{padding-left:40px !important}.uk-form-icon-flip{right:0;left:auto}.uk-form-icon-flip+.uk-input{padding-right:40px !important}.uk-button{margin:0;border:none;border-radius:0;overflow:visible;font:inherit;color:inherit;text-transform:none;display:inline-block;box-sizing:border-box;padding:0 30px;vertical-align:middle;font-size:.875rem;line-height:38px;text-align:center;text-decoration:none;border-radius:5px}.uk-button:not(:disabled){cursor:pointer}.uk-button::-moz-focus-inner{border:0;padding:0}.uk-button:hover{text-decoration:none}.uk-button:focus{outline:none}.uk-button-default{background-color:transparent;color:#333;border:1px solid #c4c7ca}.uk-button-default:hover,.uk-button-default:focus{background-color:transparent;color:#333;border-color:#8e949a}.uk-button-default:active,.uk-button-default.uk-active{background-color:transparent;color:#333;border-color:#747b81}.uk-button-primary{background-color:#0F1214;color:#fff;border:1px solid transparent}.uk-button-primary:hover,.uk-button-primary:focus{background-color:#000;color:#fff}.uk-button-primary:active,.uk-button-primary.uk-active{background-color:#000;color:#fff}.uk-button-secondary{background-color:#222;color:#fff;border:1px solid transparent}.uk-button-secondary:hover,.uk-button-secondary:focus{background-color:#151515;color:#fff}.uk-button-secondary:active,.uk-button-secondary.uk-active{background-color:#090909;color:#fff}.uk-button-danger{background-color:#f0506e;color:#fff;border:1px solid transparent}.uk-button-danger:hover,.uk-button-danger:focus{background-color:#ee395b;color:#fff}.uk-button-danger:active,.uk-button-danger.uk-active{background-color:#ec2147;color:#fff}.uk-button-default:disabled,.uk-button-primary:disabled,.uk-button-secondary:disabled,.uk-button-danger:disabled{background-color:transparent;color:#7a838a;border-color:#c4c7ca}.uk-button-small{padding:0 15px;line-height:28px;font-size:.875rem}.uk-button-large{padding:0 40px;line-height:53px;font-size:.875rem}.uk-button-text{padding:0;line-height:1.5;background:none;color:#333;position:relative}.uk-button-text::before{content:"";position:absolute;bottom:0;left:0;right:100%;border-bottom:1px solid #333;transition:right 0.3s ease-out}.uk-button-text:hover,.uk-button-text:focus{color:#333}.uk-button-text:hover::before,.uk-button-text:focus::before{right:0}.uk-button-text:disabled{color:#7a838a}.uk-button-text:disabled::before{display:none}.uk-button-link{padding:0;line-height:1.5;background:none;color:#7a838a}.uk-button-link:hover,.uk-button-link:focus{color:#000;text-decoration:underline}.uk-button-link:disabled{color:#7a838a;text-decoration:none}.uk-button-group{display:inline-flex;vertical-align:middle;position:relative}.uk-button-group>.uk-button:nth-child(n+2),.uk-button-group>div:nth-child(n+2) .uk-button{margin-left:-1px}.uk-button-group .uk-button:hover,.uk-button-group .uk-button:focus,.uk-button-group .uk-button:active,.uk-button-group .uk-button.uk-active{position:relative;z-index:1}.uk-section{box-sizing:border-box;padding-top:40px;padding-bottom:40px}@media (min-width: 960px){.uk-section{padding-top:15px;padding-bottom:15px}}.uk-section::before,.uk-section::after{content:"";display:table}.uk-section::after{clear:both}.uk-section>:last-child{margin-bottom:0}.uk-section-xsmall{padding-top:20px;padding-bottom:20px}.uk-section-small{padding-top:40px;padding-bottom:40px}.uk-section-large{padding-top:70px;padding-bottom:70px}@media (min-width: 960px){.uk-section-large{padding-top:140px;padding-bottom:140px}}.uk-section-xlarge{padding-top:140px;padding-bottom:140px}@media (min-width: 960px){.uk-section-xlarge{padding-top:210px;padding-bottom:210px}}.uk-section-default{background:#fff}.uk-section-muted{background:#f8f8f8}.uk-section-primary{background:#0F1214}.uk-section-secondary{background:#222}.uk-container{box-sizing:content-box;max-width:1100px;margin-left:auto;margin-right:auto;padding-left:15px;padding-right:15px}@media (min-width: 640px){.uk-container{padding-left:30px;padding-right:30px}}@media (min-width: 960px){.uk-container{padding-left:40px;padding-right:40px}}.uk-container::before,.uk-container::after{content:"";display:table}.uk-container::after{clear:both}.uk-container>:last-child{margin-bottom:0}.uk-container .uk-container{padding-left:0;padding-right:0}.uk-container-small{max-width:800px}.uk-container-large{max-width:1600px}.uk-container-expand{max-width:none}.uk-grid{display:flex;flex-wrap:wrap;margin:0;padding:0;list-style:none}.uk-grid>*{margin:0}.uk-grid>*>:last-child{margin-bottom:0}.uk-grid{margin-left:-30px}.uk-grid>*{padding-left:30px}.uk-grid+.uk-grid,.uk-grid>.uk-grid-margin,*+.uk-grid-margin{margin-top:30px}@media (min-width: 1200px){.uk-grid{margin-left:-40px}.uk-grid>*{padding-left:40px}.uk-grid+.uk-grid,.uk-grid>.uk-grid-margin,*+.uk-grid-margin{margin-top:40px}}.uk-grid-small{margin-left:-15px}.uk-grid-small>*{padding-left:15px}.uk-grid+.uk-grid-small,.uk-grid-small>.uk-grid-margin,*+.uk-grid-margin-small{margin-top:15px}.uk-grid-medium{margin-left:-30px}.uk-grid-medium>*{padding-left:30px}.uk-grid+.uk-grid-medium,.uk-grid-medium>.uk-grid-margin,*+.uk-grid-margin-medium{margin-top:30px}.uk-grid-large{margin-left:-40px}.uk-grid-large>*{padding-left:40px}.uk-grid+.uk-grid-large,.uk-grid-large>.uk-grid-margin,*+.uk-grid-margin-large{margin-top:40px}@media (min-width: 1200px){.uk-grid-large{margin-left:-70px}.uk-grid-large>*{padding-left:70px}.uk-grid+.uk-grid-large,.uk-grid-large>.uk-grid-margin,*+.uk-grid-margin-large{margin-top:70px}}.uk-grid-collapse{margin-left:0}.uk-grid-collapse>*{padding-left:0}.uk-grid+.uk-grid-collapse,.uk-grid-collapse>.uk-grid-margin{margin-top:0}.uk-grid-divider>*{position:relative}.uk-grid-divider>:not(.uk-first-column)::before{content:"";position:absolute;top:0;bottom:0;border-left:1px solid #c4c7ca}.uk-grid-divider.uk-grid-stack>.uk-grid-margin::before{content:"";position:absolute;left:0;right:0;border-top:1px solid #c4c7ca}.uk-grid-divider{margin-left:-60px}.uk-grid-divider>*{padding-left:60px}.uk-grid-divider>:not(.uk-first-column)::before{left:30px}.uk-grid-divider.uk-grid-stack>.uk-grid-margin{margin-top:60px}.uk-grid-divider.uk-grid-stack>.uk-grid-margin::before{top:-30px;left:60px}@media (min-width: 1200px){.uk-grid-divider{margin-left:-80px}.uk-grid-divider>*{padding-left:80px}.uk-grid-divider>:not(.uk-first-column)::before{left:40px}.uk-grid-divider.uk-grid-stack>.uk-grid-margin{margin-top:80px}.uk-grid-divider.uk-grid-stack>.uk-grid-margin::before{top:-40px;left:80px}}.uk-grid-divider.uk-grid-small{margin-left:-30px}.uk-grid-divider.uk-grid-small>*{padding-left:30px}.uk-grid-divider.uk-grid-small>:not(.uk-first-column)::before{left:15px}.uk-grid-divider.uk-grid-small.uk-grid-stack>.uk-grid-margin{margin-top:30px}.uk-grid-divider.uk-grid-small.uk-grid-stack>.uk-grid-margin::before{top:-15px;left:30px}.uk-grid-divider.uk-grid-medium{margin-left:-60px}.uk-grid-divider.uk-grid-medium>*{padding-left:60px}.uk-grid-divider.uk-grid-medium>:not(.uk-first-column)::before{left:30px}.uk-grid-divider.uk-grid-medium.uk-grid-stack>.uk-grid-margin{margin-top:60px}.uk-grid-divider.uk-grid-medium.uk-grid-stack>.uk-grid-margin::before{top:-30px;left:60px}.uk-grid-divider.uk-grid-large{margin-left:-80px}.uk-grid-divider.uk-grid-large>*{padding-left:80px}.uk-grid-divider.uk-grid-large>:not(.uk-first-column)::before{left:40px}.uk-grid-divider.uk-grid-large.uk-grid-stack>.uk-grid-margin{margin-top:80px}.uk-grid-divider.uk-grid-large.uk-grid-stack>.uk-grid-margin::before{top:-40px;left:80px}@media (min-width: 1200px){.uk-grid-divider.uk-grid-large{margin-left:-140px}.uk-grid-divider.uk-grid-large>*{padding-left:140px}.uk-grid-divider.uk-grid-large>:not(.uk-first-column)::before{left:70px}.uk-grid-divider.uk-grid-large.uk-grid-stack>.uk-grid-margin{margin-top:140px}.uk-grid-divider.uk-grid-large.uk-grid-stack>.uk-grid-margin::before{top:-70px;left:140px}}.uk-grid-match>*,.uk-grid-item-match{display:flex;flex-wrap:wrap}.uk-grid-match>*>:not([class*='uk-width']),.uk-grid-item-match>:not([class*='uk-width']){box-sizing:border-box;width:100%;flex:auto}.uk-tile{position:relative;box-sizing:border-box;padding-left:15px;padding-right:15px;padding-top:40px;padding-bottom:40px}@media (min-width: 640px){.uk-tile{padding-left:30px;padding-right:30px}}@media (min-width: 960px){.uk-tile{padding-left:40px;padding-right:40px;padding-top:70px;padding-bottom:70px}}.uk-tile::before,.uk-tile::after{content:"";display:table}.uk-tile::after{clear:both}.uk-tile>:last-child{margin-bottom:0}.uk-tile-xsmall{padding-top:20px;padding-bottom:20px}.uk-tile-small{padding-top:40px;padding-bottom:40px}.uk-tile-large{padding-top:70px;padding-bottom:70px}@media (min-width: 960px){.uk-tile-large{padding-top:140px;padding-bottom:140px}}.uk-tile-xlarge{padding-top:140px;padding-bottom:140px}@media (min-width: 960px){.uk-tile-xlarge{padding-top:210px;padding-bottom:210px}}.uk-tile-default{background:#fff}.uk-tile-muted{background:#f8f8f8}.uk-tile-primary{background:#0F1214}.uk-tile-secondary{background:#222}.uk-card{position:relative;box-sizing:border-box;border:solid 1px #dfe1e2}.uk-card:hover{border-color:#a9aeb2}.uk-card-body{padding:30px 30px}.uk-card-body>p{font-size:.875rem}.uk-card-body>span{color:#0F1214}.uk-card-header{padding:15px 30px}.uk-card-footer{padding:15px 30px}@media (min-width: 1200px){.uk-card-body{padding:40px 40px}.uk-card-header{padding:20px 40px}.uk-card-footer{padding:20px 40px}}.uk-card-body::before,.uk-card-body::after,.uk-card-header::before,.uk-card-header::after,.uk-card-footer::before,.uk-card-footer::after{content:"";display:table}.uk-card-body::after,.uk-card-header::after,.uk-card-footer::after{clear:both}.uk-card-body>:last-child,.uk-card-header>:last-child,.uk-card-footer>:last-child{margin-bottom:0}.uk-card-title{font-size:1.375rem;line-height:1.4;font-size:1.125rem}.uk-card-badge{position:absolute;top:30px;right:30px;z-index:1}.uk-card-badge:first-child+*{margin-top:0}.uk-card-hover:not(.uk-card-default):not(.uk-card-primary):not(.uk-card-secondary):hover{background:#fff;box-shadow:0 14px 25px rgba(0,0,0,0.16)}.uk-card-default{background:#fff;color:#666;box-shadow:0 5px 15px rgba(0,0,0,0.08)}.uk-card-default .uk-card-title{color:#333}.uk-card-default.uk-card-hover:hover{background-color:#fff;box-shadow:0 14px 25px rgba(0,0,0,0.16)}.uk-card-default .uk-card-header{border-bottom:1px solid #c4c7ca}.uk-card-default .uk-card-footer{border-top:1px solid #c4c7ca}.uk-card-primary{background:#0F1214;color:#fff;box-shadow:0 5px 15px rgba(0,0,0,0.08)}.uk-card-primary .uk-card-title{color:#fff}.uk-card-primary.uk-card-hover:hover{background-color:#0F1214;box-shadow:0 14px 25px rgba(0,0,0,0.16)}.uk-card-secondary{background:#222;color:#fff;box-shadow:0 5px 15px rgba(0,0,0,0.08)}.uk-card-secondary .uk-card-title{color:#fff}.uk-card-secondary.uk-card-hover:hover{background-color:#222;box-shadow:0 14px 25px rgba(0,0,0,0.16)}.uk-card-small.uk-card-body,.uk-card-small .uk-card-body{padding:25px 25px}.uk-card-small .uk-card-header{padding:13px 20px}.uk-card-small .uk-card-footer{padding:13px 20px}@media (min-width: 1200px){.uk-card-large.uk-card-body,.uk-card-large .uk-card-body{padding:70px 70px}.uk-card-large .uk-card-header{padding:35px 70px}.uk-card-large .uk-card-footer{padding:35px 70px}}.uk-position-cover{z-index:1}.card-category h3:nth-child(2n){margin-top:0 !important}.card-post .uk-card-header{padding-top:40px;padding-bottom:0}.card-post .uk-card-body{padding-top:20px;padding-bottom:20px}.card-post .uk-card-footer{padding-bottom:32px;padding-top:0}.uk-close{color:#7a838a;transition:0.1s ease-in-out;transition-property:color, opacity}.uk-close:hover,.uk-close:focus{color:#666;outline:none}.uk-totop{padding:5px;color:#7a838a;transition:color 0.1s ease-in-out}.uk-totop:hover,.uk-totop:focus{color:#666;outline:none}.uk-totop:active{color:#333}.uk-label{display:inline-block;padding:0 10px;background:#0F1214;line-height:1.5;font-size:.875rem;color:#fff;vertical-align:middle;white-space:nowrap;border-radius:2px;text-transform:uppercase}.uk-label-success{background-color:#32d296;color:#fff}.uk-label-warning{background-color:#faa05a;color:#fff}.uk-label-danger{background-color:#f0506e;color:#fff}.uk-overlay{padding:30px 30px}.uk-overlay>:last-child{margin-bottom:0}.uk-overlay-default{background:rgba(255,255,255,0.8)}.uk-overlay-primary{background:rgba(34,34,34,0.8)}.uk-article figure,.uk-article .uk-slideshow{margin-top:44px;margin-bottom:60px}.uk-article figure img+div .uk-overlay-icon{color:rgba(255,255,255,0)}.uk-article figure img:hover+div .uk-overlay-icon{color:#fff}.uk-article figure figcaption{margin-left:0}.uk-article figure figcaption span{padding-right:20px;margin-bottom:-43px;margin-top:20px;border-right:solid 2px #7a838a;font-style:italic;font-size:0.8rem;line-height:1.8}.uk-article blockquote{border-left:solid 2px #7a838a;padding-left:20px;line-height:1.7;margin-top:40px;margin-bottom:40px}.uk-article .highlight,.uk-article .highlighter-rouge{margin-top:40px;margin-bottom:40px}.uk-article::before,.uk-article::after{content:"";display:table}.uk-article::after{clear:both}.uk-article>:last-child{margin-bottom:0}.uk-article+.uk-article{margin-top:70px}.uk-article-title{font-size:1.875rem;line-height:1.4;margin-bottom:20px}.uk-article-meta{font-size:.8125rem;line-height:1.3;color:#7a838a}.uk-article-meta a{color:#7a838a}.uk-article-meta a:hover{color:#0F1214}.uk-article-meta .avatar{margin-right:10px;float:left}.article-content{line-height:1.8}.avatar{border-radius:50%}.paginate-post .uk-text-small{line-height:1.75}.uk-search{display:inline-block;position:relative;max-width:100%;margin:0}.uk-search-input::-webkit-search-cancel-button,.uk-search-input::-webkit-search-decoration{-webkit-appearance:none}.uk-search-input::-moz-placeholder{opacity:1}.uk-search-input{box-sizing:border-box;margin:0;border-radius:0;font:inherit;overflow:visible;-webkit-appearance:none;vertical-align:middle;width:100%;border:none;color:#666}.uk-search-input:focus{outline:none}.uk-search-input:-ms-input-placeholder{color:#7a838a !important}.uk-search-input::placeholder{color:#7a838a}.uk-search-icon:focus{outline:none}.uk-search .uk-search-icon{position:absolute;top:0;bottom:0;left:0;display:inline-flex;justify-content:center;align-items:center;color:#7a838a}.uk-search .uk-search-icon:hover{color:#7a838a}.uk-search .uk-search-icon:not(a):not(button):not(input){pointer-events:none}.uk-search .uk-search-icon-flip{right:0;left:auto}.uk-search-default{width:180px}.uk-search-default .uk-search-input{height:40px;padding-left:6px;padding-right:6px;background:transparent;border:1px solid #9ca1a6}.uk-search-default .uk-search-input:focus{background-color:transparent}.uk-search-default .uk-search-icon{width:40px}.uk-search-default .uk-search-icon:not(.uk-search-icon-flip)+.uk-search-input{padding-left:40px}.uk-search-default .uk-search-icon-flip+.uk-search-input{padding-right:40px}.uk-search-navbar{width:400px}.uk-search-navbar .uk-search-input{height:40px;background:transparent;font-size:1.375rem}.uk-search-navbar .uk-search-icon{width:40px}.uk-search-navbar .uk-search-icon:not(.uk-search-icon-flip)+.uk-search-input{padding-left:40px}.uk-search-navbar .uk-search-icon-flip+.uk-search-input{padding-right:40px}.uk-search-large{width:500px}.uk-search-large .uk-search-input{height:80px;background:transparent;font-size:1.875rem}.uk-search-large .uk-search-icon{width:80px}.uk-search-large .uk-search-icon:not(.uk-search-icon-flip)+.uk-search-input{padding-left:80px}.uk-search-large .uk-search-icon-flip+.uk-search-input{padding-right:80px}.uk-search-toggle{color:#7a838a}.uk-search-toggle:hover,.uk-search-toggle:focus{color:#666}.uk-nav,.uk-nav ul{margin:0;padding:0;list-style:none}.uk-nav li>a{display:block;text-decoration:none}.uk-nav li>a:focus{outline:none}.uk-nav>li>a{padding:5px 0}ul.uk-nav-sub{padding:5px 0 5px 15px}.uk-nav-sub ul{padding-left:15px}.uk-nav-sub a{padding:2px 0}.uk-nav-parent-icon>.uk-parent>a::after{content:"";width:1.5em;height:1.5em;float:right;background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolyline%20fill%3D%22none%22%20stroke%3D%22%23666%22%20stroke-width%3D%221.1%22%20points%3D%2210%201%204%207%2010%2013%22%20%2F%3E%0A%3C%2Fsvg%3E");background-repeat:no-repeat;background-position:50% 50%}.uk-nav-parent-icon>.uk-parent.uk-open>a::after{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolyline%20fill%3D%22none%22%20stroke%3D%22%23666%22%20stroke-width%3D%221.1%22%20points%3D%221%204%207%2010%2013%204%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-nav-header{padding:5px 0;text-transform:uppercase;font-size:.875rem}.uk-nav-header:not(:first-child){margin-top:20px}.uk-nav-divider{margin:5px 0}.uk-nav-default{font-size:.875rem}.uk-nav-default>li>a{color:#7a838a}.uk-nav-default>li>a:hover,.uk-nav-default>li>a:focus{color:#666}.uk-nav-default>li.uk-active>a{color:#333}.uk-nav-default .uk-nav-header{color:#333}.uk-nav-default .uk-nav-divider{border-top:1px solid #c4c7ca}.uk-nav-default .uk-nav-sub a{color:#7a838a}.uk-nav-default .uk-nav-sub a:hover,.uk-nav-default .uk-nav-sub a:focus{color:#666}.uk-nav-primary>li>a{font-size:1.375rem;line-height:1.5;color:#7a838a}.uk-nav-primary>li>a:hover,.uk-nav-primary>li>a:focus{color:#666}.uk-nav-primary>li.uk-active>a{color:#333}.uk-nav-primary .uk-nav-header{color:#333}.uk-nav-primary .uk-nav-divider{border-top:1px solid #c4c7ca}.uk-nav-primary .uk-nav-sub a{color:#7a838a}.uk-nav-primary .uk-nav-sub a:hover,.uk-nav-primary .uk-nav-sub a:focus{color:#666}.uk-nav-center{text-align:center}.uk-nav-center .uk-nav-sub,.uk-nav-center .uk-nav-sub ul{padding-left:0}.uk-nav-center.uk-nav-parent-icon>.uk-parent>a::after{position:absolute}.uk-navbar{display:flex;position:relative;font-weight:500}.uk-navbar .uk-drop{width:360px}.uk-navbar .uk-search-navbar .uk-search-input{height:50px;font-size:1.0625rem;padding-left:1.1875rem;background:#fff}.uk-navbar-container:not(.uk-navbar-transparent){background:#fff}.uk-navbar-container>::before,.uk-navbar-container>::after{display:none !important}.uk-navbar-left,.uk-navbar-right,.uk-navbar-center,.uk-navbar-center-left>*,.uk-navbar-center-right>*{display:flex;align-items:center}.uk-navbar-right{margin-left:auto}.uk-navbar-center:only-child{margin-left:auto;margin-right:auto;position:relative}.uk-navbar-center:not(:only-child){position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:990}.uk-navbar-center:not(:only-child) .uk-navbar-nav>li>a,.uk-navbar-center:not(:only-child) .uk-navbar-item,.uk-navbar-center:not(:only-child) .uk-navbar-toggle{white-space:nowrap}.uk-navbar-center-left,.uk-navbar-center-right{position:absolute;top:0}.uk-navbar-center-left{right:100%}.uk-navbar-center-right{left:100%}[class*='uk-navbar-center-'] .uk-navbar-nav>li>a,[class*='uk-navbar-center-'] .uk-navbar-item,[class*='uk-navbar-center-'] .uk-navbar-toggle{white-space:nowrap}.uk-navbar-nav{display:flex;margin:0;padding:0;list-style:none}.uk-navbar-left,.uk-navbar-right,.uk-navbar-center:only-child{flex-wrap:wrap}.uk-navbar-nav>li>a,.uk-navbar-item,.uk-navbar-toggle{display:flex;justify-content:center;align-items:center;box-sizing:border-box;height:80px;padding:0 15px;font-size:.875rem;font-family:system-ui;text-decoration:none}.uk-navbar-nav>li>a{color:#7a838a;text-transform:none;transition:0.1s ease-in-out;transition-property:color, background-color}.uk-navbar-nav>li:hover>a,.uk-navbar-nav>li>a:focus,.uk-navbar-nav>li>a.uk-open{color:#0F1214;outline:none}.uk-navbar-nav>li>a:active{color:#333}.uk-navbar-nav>li.uk-active>a{color:#0F1214}.uk-navbar-item{color:#666}.uk-navbar-toggle{color:#7a838a}.uk-navbar-toggle:hover,.uk-navbar-toggle:focus,.uk-navbar-toggle.uk-open{color:#0F1214;outline:none;text-decoration:none}.uk-navbar-subtitle{font-size:.875rem}.uk-navbar-dropdown{display:none;position:absolute;z-index:1020;box-sizing:border-box;width:200px;padding:25px;background:#fff;color:#666;box-shadow:0 5px 12px rgba(0,0,0,0.15)}.uk-navbar-dropdown.uk-open{display:block}[class*='uk-navbar-dropdown-top']{margin-top:-15px}[class*='uk-navbar-dropdown-bottom']{margin-top:15px}[class*='uk-navbar-dropdown-left']{margin-left:-15px}[class*='uk-navbar-dropdown-right']{margin-left:15px}.uk-navbar-dropdown-grid{margin-left:-50px}.uk-navbar-dropdown-grid>*{padding-left:50px}.uk-navbar-dropdown-grid>.uk-grid-margin{margin-top:50px}.uk-navbar-dropdown-stack .uk-navbar-dropdown-grid>*{width:100% !important}.uk-navbar-dropdown-width-2:not(.uk-navbar-dropdown-stack){width:400px}.uk-navbar-dropdown-width-3:not(.uk-navbar-dropdown-stack){width:600px}.uk-navbar-dropdown-width-4:not(.uk-navbar-dropdown-stack){width:800px}.uk-navbar-dropdown-width-5:not(.uk-navbar-dropdown-stack){width:1000px}.uk-navbar-dropdown-dropbar{margin-top:0;margin-bottom:0;box-shadow:none}.uk-navbar-dropdown-nav{font-size:.875rem}.uk-navbar-dropdown-nav>li>a{color:#7a838a}.uk-navbar-dropdown-nav>li>a:hover,.uk-navbar-dropdown-nav>li>a:focus{color:#666}.uk-navbar-dropdown-nav>li.uk-active>a{color:#333}.uk-navbar-dropdown-nav .uk-nav-header{color:#333}.uk-navbar-dropdown-nav .uk-nav-divider{border-top:1px solid #c4c7ca}.uk-navbar-dropdown-nav .uk-nav-sub a{color:#7a838a}.uk-navbar-dropdown-nav .uk-nav-sub a:hover,.uk-navbar-dropdown-nav .uk-nav-sub a:focus{color:#666}.uk-navbar-dropbar{background:#fff}.uk-navbar-dropbar-slide{position:absolute;z-index:980;left:0;right:0;box-shadow:0 5px 7px rgba(0,0,0,0.05)}.uk-navbar-container>.uk-container .uk-navbar-left{margin-left:-15px;margin-right:-15px}.uk-navbar-container>.uk-container .uk-navbar-right{margin-right:-15px}.uk-navbar-dropdown-grid>*{position:relative}.uk-navbar-dropdown-grid>:not(.uk-first-column)::before{content:"";position:absolute;top:0;bottom:0;left:25px;border-left:1px solid #c4c7ca}.uk-navbar-dropdown-grid.uk-grid-stack>.uk-grid-margin::before{content:"";position:absolute;top:-25px;left:50px;right:0;border-top:1px solid #c4c7ca}.uk-subnav{display:flex;flex-wrap:wrap;margin-left:-20px;padding:0;list-style:none}.uk-subnav>*{flex:none;padding-left:20px;position:relative}.uk-subnav>*>:first-child{display:block;color:#7a838a;font-size:.875rem;text-transform:none;transition:0.1s ease-in-out;transition-property:color, background-color}.uk-subnav>*>a:hover,.uk-subnav>*>a:focus{color:#0F1214;text-decoration:none;outline:none}.uk-subnav>.uk-active>a{color:#333}.uk-subnav-divider>*{display:flex;align-items:center}.uk-subnav-divider>:nth-child(n+2):not(.uk-first-column)::before{content:"";height:1.5em;margin-left:0px;margin-right:20px;border-left:1px solid #c4c7ca}.uk-subnav-pill>*>:first-child{padding:5px 10px;background:transparent;color:#7a838a}.uk-subnav-pill>*>a:hover,.uk-subnav-pill>*>a:focus{background-color:#f8f8f8;color:#666}.uk-subnav-pill>*>a:active{background-color:#f8f8f8;color:#666}.uk-subnav-pill>.uk-active>a{background-color:#0F1214;color:#fff}.uk-subnav>.uk-disabled>a{color:#7a838a}.uk-pagination{display:flex;flex-wrap:wrap;margin-left:-20px;padding:0;list-style:none}.uk-pagination>*{flex:none;padding-left:20px;position:relative}.uk-pagination>*>*{display:block;color:#0F1214;transition:color 0.1s ease-in-out}.uk-pagination>*>:hover,.uk-pagination>*>:focus{color:#0F1214;text-decoration:none}.uk-pagination>.uk-active>*{color:#666}.uk-pagination>.uk-disabled>*{color:#7a838a}.uk-drop{display:none;position:absolute;z-index:1020;box-sizing:border-box;width:300px}.uk-drop.uk-open{display:block}[class*='uk-drop-top']{margin-top:-20px}[class*='uk-drop-bottom']{margin-top:20px}[class*='uk-drop-left']{margin-left:-20px}[class*='uk-drop-right']{margin-left:20px}.uk-drop-stack .uk-drop-grid>*{width:100% !important}.uk-dropdown{display:none;position:absolute;z-index:1020;box-sizing:border-box;min-width:200px;padding:25px;background:#fff;color:#666;box-shadow:0 5px 12px rgba(0,0,0,0.15)}.uk-dropdown.uk-open{display:block}.uk-dropdown-nav{white-space:nowrap;font-size:.875rem}.uk-dropdown-nav>li>a{color:#7a838a}.uk-dropdown-nav>li>a:hover,.uk-dropdown-nav>li>a:focus,.uk-dropdown-nav>li.uk-active>a{color:#666}.uk-dropdown-nav .uk-nav-header{color:#333}.uk-dropdown-nav .uk-nav-divider{border-top:1px solid #c4c7ca}.uk-dropdown-nav .uk-nav-sub a{color:#7a838a}.uk-dropdown-nav .uk-nav-sub a:hover,.uk-dropdown-nav .uk-nav-sub a:focus{color:#666}[class*='uk-dropdown-top']{margin-top:-10px}[class*='uk-dropdown-bottom']{margin-top:10px}[class*='uk-dropdown-left']{margin-left:-10px}[class*='uk-dropdown-right']{margin-left:10px}.uk-dropdown-stack .uk-dropdown-grid>*{width:100% !important}.uk-lightbox{display:none;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1010;background:#000;opacity:0;transition:opacity 0.15s linear}.uk-lightbox.uk-open{display:block;opacity:1}.uk-lightbox-page{overflow:hidden}.uk-lightbox-items>*{position:absolute;top:0;right:0;bottom:0;left:0;display:none;justify-content:center;align-items:center;color:rgba(255,255,255,0.7);will-change:transform, opacity}.uk-lightbox-items>*>*{max-width:100vw;max-height:100vh}.uk-lightbox-items>*>:not(iframe){width:auto;height:auto}.uk-lightbox-items>.uk-active{display:flex}.uk-lightbox-toolbar{padding:10px 10px;background:rgba(0,0,0,0.3);color:rgba(255,255,255,0.7)}.uk-lightbox-toolbar *{color:rgba(255,255,255,0.7)}.uk-lightbox-toolbar-icon{padding:5px;color:rgba(255,255,255,0.7)}.uk-lightbox-toolbar-icon:hover{color:#fff}.uk-lightbox-button{box-sizing:border-box;width:50px;height:50px;background:rgba(0,0,0,0.3);color:rgba(255,255,255,0.7);display:inline-flex;justify-content:center;align-items:center}.uk-lightbox-button:hover{color:#fff}.uk-lightbox-iframe{width:80%;height:80%}.uk-slideshow{-webkit-tap-highlight-color:transparent}.uk-slideshow-items{position:relative;z-index:0;margin:0;padding:0;list-style:none;overflow:hidden;-webkit-touch-callout:none}.uk-slideshow-items>*{position:absolute;top:0;left:0;right:0;bottom:0;overflow:hidden;will-change:transform, opacity;touch-action:pan-y}.uk-slideshow-items>:not(.uk-active){display:none}.uk-sticky-fixed{z-index:980;box-sizing:border-box;margin:0 !important;-webkit-backface-visibility:hidden;backface-visibility:hidden}.uk-sticky[class*='uk-animation-']{animation-duration:.2s}.uk-sticky.uk-animation-reverse{animation-duration:.2s}.uk-offcanvas{display:none;position:fixed;top:0;bottom:0;left:0;z-index:1000}.uk-offcanvas-flip .uk-offcanvas{right:0;left:auto}.uk-offcanvas-bar{position:absolute;top:0;bottom:0;left:0;box-sizing:border-box;width:270px;padding:20px 20px;background:#fff;overflow-y:auto;-webkit-overflow-scrolling:touch;transform:translateX(-100%);font-weight:500}@media (min-width: 960px){.uk-offcanvas-bar{width:350px;padding:40px 40px}}.uk-offcanvas-flip .uk-offcanvas-bar{left:auto;right:0;transform:translateX(100%)}.uk-open>.uk-offcanvas-bar{transform:translateX(0)}.uk-offcanvas-bar-animation{transition:transform 0.3s ease-out}.uk-offcanvas-reveal{position:absolute;top:0;bottom:0;left:0;width:0;overflow:hidden;transition:width 0.3s ease-out}.uk-offcanvas-reveal .uk-offcanvas-bar{transform:translateX(0)}.uk-open>.uk-offcanvas-reveal{width:270px}@media (min-width: 960px){.uk-open>.uk-offcanvas-reveal{width:350px}}.uk-offcanvas-flip .uk-offcanvas-reveal{right:0;left:auto}.uk-offcanvas-close{position:absolute;z-index:1000;top:20px;right:20px;padding:5px}.uk-offcanvas-overlay{width:100vw;touch-action:none}.uk-offcanvas-overlay::before{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background:rgba(0,0,0,0.1);opacity:0;transition:opacity 0.15s linear}.uk-offcanvas-overlay.uk-open::before{opacity:1}.uk-offcanvas-page,.uk-offcanvas-container{overflow-x:hidden}.uk-offcanvas-container-overlay{overflow:hidden}.uk-offcanvas-container .uk-offcanvas-content{position:relative;left:0;transition:left 0.3s ease-out;-webkit-overflow-scrolling:touch}.uk-offcanvas-overlay .uk-offcanvas-content{overflow-y:hidden}:not(.uk-offcanvas-flip)>.uk-offcanvas-content-animation{left:270px}.uk-offcanvas-flip>.uk-offcanvas-content-animation{left:-270px}@media (min-width: 960px){:not(.uk-offcanvas-flip)>.uk-offcanvas-content-animation{left:350px}.uk-offcanvas-flip>.uk-offcanvas-content-animation{left:-350px}}[class*='uk-animation-']{animation-duration:.5s;animation-timing-function:ease-out;animation-fill-mode:both}.uk-animation-reverse{animation-direction:reverse;animation-timing-function:ease-in}.uk-animation-fade{animation-name:uk-fade;animation-duration:.8s;animation-timing-function:linear}.uk-animation-scale-up{animation-name:uk-fade-scale-02}.uk-animation-scale-down{animation-name:uk-fade-scale-18}.uk-animation-slide-top{animation-name:uk-fade-top}.uk-animation-slide-bottom{animation-name:uk-fade-bottom}.uk-animation-slide-left{animation-name:uk-fade-left}.uk-animation-slide-right{animation-name:uk-fade-right}.uk-animation-slide-top-small{animation-name:uk-fade-top-small}.uk-animation-slide-bottom-small{animation-name:uk-fade-bottom-small}.uk-animation-slide-left-small{animation-name:uk-fade-left-small}.uk-animation-slide-right-small{animation-name:uk-fade-right-small}.uk-animation-slide-top-medium{animation-name:uk-fade-top-medium}.uk-animation-slide-bottom-medium{animation-name:uk-fade-bottom-medium}.uk-animation-slide-left-medium{animation-name:uk-fade-left-medium}.uk-animation-slide-right-medium{animation-name:uk-fade-right-medium}.uk-animation-kenburns{animation-name:uk-scale-kenburns;animation-duration:15s}.uk-animation-shake{animation-name:uk-shake}.uk-animation-fast{animation-duration:.1s}.uk-animation-toggle:not(:hover):not(.uk-hover) [class*='uk-animation-']{animation-name:none}@keyframes uk-fade{0%{opacity:0}100%{opacity:1}}@keyframes uk-fade-top{0%{opacity:0;transform:translateY(-100%)}100%{opacity:1;transform:translateY(0)}}@keyframes uk-fade-bottom{0%{opacity:0;transform:translateY(100%)}100%{opacity:1;transform:translateY(0)}}@keyframes uk-fade-left{0%{opacity:0;transform:translateX(-100%)}100%{opacity:1;transform:translateX(0)}}@keyframes uk-fade-right{0%{opacity:0;transform:translateX(100%)}100%{opacity:1;transform:translateX(0)}}@keyframes uk-fade-top-small{0%{opacity:0;transform:translateY(-10px)}100%{opacity:1;transform:translateY(0)}}@keyframes uk-fade-bottom-small{0%{opacity:0;transform:translateY(10px)}100%{opacity:1;transform:translateY(0)}}@keyframes uk-fade-left-small{0%{opacity:0;transform:translateX(-10px)}100%{opacity:1;transform:translateX(0)}}@keyframes uk-fade-right-small{0%{opacity:0;transform:translateX(10px)}100%{opacity:1;transform:translateX(0)}}@keyframes uk-fade-top-medium{0%{opacity:0;transform:translateY(-50px)}100%{opacity:1;transform:translateY(0)}}@keyframes uk-fade-bottom-medium{0%{opacity:0;transform:translateY(50px)}100%{opacity:1;transform:translateY(0)}}@keyframes uk-fade-left-medium{0%{opacity:0;transform:translateX(-50px)}100%{opacity:1;transform:translateX(0)}}@keyframes uk-fade-right-medium{0%{opacity:0;transform:translateX(50px)}100%{opacity:1;transform:translateX(0)}}@keyframes uk-fade-scale-02{0%{opacity:0;transform:scale(0.2)}100%{opacity:1;transform:scale(1)}}@keyframes uk-fade-scale-18{0%{opacity:0;transform:scale(1.8)}100%{opacity:1;transform:scale(1)}}@keyframes uk-scale-kenburns{0%{transform:scale(1)}100%{transform:scale(1.2)}}@keyframes uk-shake{0%, 100%{transform:translateX(0)}10%{transform:translateX(-9px)}20%{transform:translateX(8px)}30%{transform:translateX(-7px)}40%{transform:translateX(6px)}50%{transform:translateX(-5px)}60%{transform:translateX(4px)}70%{transform:translateX(-3px)}80%{transform:translateX(2px)}90%{transform:translateX(-1px)}}[class*='uk-child-width']>*{box-sizing:border-box;width:100%}.uk-child-width-1-2>*{width:50%}.uk-child-width-1-3>*{width:calc(100% * 1 / 3.001)}.uk-child-width-1-4>*{width:25%}.uk-child-width-1-5>*{width:20%}.uk-child-width-1-6>*{width:calc(100% * 1 / 6.001)}.uk-child-width-auto>*{width:auto}.uk-child-width-expand>*{width:1px}.uk-child-width-expand>:not([class*='uk-width']){flex:1;min-width:0;flex-basis:1px}@media (min-width: 640px){.uk-child-width-1-1\@s>*{width:100%}.uk-child-width-1-2\@s>*{width:50%}.uk-child-width-1-3\@s>*{width:calc(100% * 1 / 3.001)}.uk-child-width-1-4\@s>*{width:25%}.uk-child-width-1-5\@s>*{width:20%}.uk-child-width-1-6\@s>*{width:calc(100% * 1 / 6.001)}.uk-child-width-auto\@s>*{width:auto}.uk-child-width-expand\@s>*{width:1px}.uk-child-width-expand\@s>:not([class*='uk-width']){flex:1;min-width:0;flex-basis:1px}}@media (min-width: 960px){.uk-child-width-1-1\@m>*{width:100%}.uk-child-width-1-2\@m>*{width:50%}.uk-child-width-1-3\@m>*{width:calc(100% * 1 / 3.001)}.uk-child-width-1-4\@m>*{width:25%}.uk-child-width-1-5\@m>*{width:20%}.uk-child-width-1-6\@m>*{width:calc(100% * 1 / 6.001)}.uk-child-width-auto\@m>*{width:auto}.uk-child-width-expand\@m>*{width:1px}.uk-child-width-expand\@m>:not([class*='uk-width']){flex:1;min-width:0;flex-basis:1px}}@media (min-width: 1200px){.uk-child-width-1-1\@l>*{width:100%}.uk-child-width-1-2\@l>*{width:50%}.uk-child-width-1-3\@l>*{width:calc(100% * 1 / 3.001)}.uk-child-width-1-4\@l>*{width:25%}.uk-child-width-1-5\@l>*{width:20%}.uk-child-width-1-6\@l>*{width:calc(100% * 1 / 6.001)}.uk-child-width-auto\@l>*{width:auto}.uk-child-width-expand\@l>*{width:1px}.uk-child-width-expand\@l>:not([class*='uk-width']){flex:1;min-width:0;flex-basis:1px}}@media (min-width: 1600px){.uk-child-width-1-1\@xl>*{width:100%}.uk-child-width-1-2\@xl>*{width:50%}.uk-child-width-1-3\@xl>*{width:calc(100% * 1 / 3.001)}.uk-child-width-1-4\@xl>*{width:25%}.uk-child-width-1-5\@xl>*{width:20%}.uk-child-width-1-6\@xl>*{width:calc(100% * 1 / 6.001)}.uk-child-width-auto\@xl>*{width:auto}.uk-child-width-expand\@xl>*{width:1px}.uk-child-width-expand\@xl>:not([class*='uk-width']){flex:1;min-width:0;flex-basis:1px}}[class*='uk-width']{box-sizing:border-box;width:100%;max-width:100%}.uk-width-1-2{width:50%}.uk-width-1-3{width:calc(100% * 1 / 3.001)}.uk-width-2-3{width:calc(100% * 2 / 3.001)}.uk-width-1-4{width:25%}.uk-width-3-4{width:75%}.uk-width-1-5{width:20%}.uk-width-2-5{width:40%}.uk-width-3-5{width:60%}.uk-width-4-5{width:80%}.uk-width-1-6{width:calc(100% * 1 / 6.001)}.uk-width-5-6{width:calc(100% * 5 / 6.001)}.uk-width-small{width:150px}.uk-width-medium{width:300px}.uk-width-large{width:450px}.uk-width-xlarge{width:600px}.uk-width-xxlarge{width:750px}.uk-width-auto{width:auto}.uk-width-expand{width:1px;flex:1;min-width:0;flex-basis:1px}@media (min-width: 640px){.uk-width-1-1\@s{width:100%}.uk-width-1-2\@s{width:50%}.uk-width-1-3\@s{width:calc(100% * 1 / 3.001)}.uk-width-2-3\@s{width:calc(100% * 2 / 3.001)}.uk-width-1-4\@s{width:25%}.uk-width-3-4\@s{width:75%}.uk-width-1-5\@s{width:20%}.uk-width-2-5\@s{width:40%}.uk-width-3-5\@s{width:60%}.uk-width-4-5\@s{width:80%}.uk-width-1-6\@s{width:calc(100% * 1 / 6.001)}.uk-width-5-6\@s{width:calc(100% * 5 / 6.001)}.uk-width-small\@s{width:150px}.uk-width-medium\@s{width:300px}.uk-width-large\@s{width:450px}.uk-width-xlarge\@s{width:600px}.uk-width-xxlarge\@s{width:750px}.uk-width-auto\@s{width:auto}.uk-width-expand\@s{width:1px;flex:1;min-width:0;flex-basis:1px}}@media (min-width: 960px){.uk-width-1-1\@m{width:100%}.uk-width-1-2\@m{width:50%}.uk-width-1-3\@m{width:calc(100% * 1 / 3.001)}.uk-width-2-3\@m{width:calc(100% * 2 / 3.001)}.uk-width-1-4\@m{width:25%}.uk-width-3-4\@m{width:75%}.uk-width-1-5\@m{width:20%}.uk-width-2-5\@m{width:40%}.uk-width-3-5\@m{width:60%}.uk-width-4-5\@m{width:80%}.uk-width-1-6\@m{width:calc(100% * 1 / 6.001)}.uk-width-5-6\@m{width:calc(100% * 5 / 6.001)}.uk-width-small\@m{width:150px}.uk-width-medium\@m{width:300px}.uk-width-large\@m{width:450px}.uk-width-xlarge\@m{width:600px}.uk-width-xxlarge\@m{width:750px}.uk-width-auto\@m{width:auto}.uk-width-expand\@m{width:1px;flex:1;min-width:0;flex-basis:1px}}@media (min-width: 1200px){.uk-width-1-1\@l{width:100%}.uk-width-1-2\@l{width:50%}.uk-width-1-3\@l{width:calc(100% * 1 / 3.001)}.uk-width-2-3\@l{width:calc(100% * 2 / 3.001)}.uk-width-1-4\@l{width:25%}.uk-width-3-4\@l{width:75%}.uk-width-1-5\@l{width:20%}.uk-width-2-5\@l{width:40%}.uk-width-3-5\@l{width:60%}.uk-width-4-5\@l{width:80%}.uk-width-1-6\@l{width:calc(100% * 1 / 6.001)}.uk-width-5-6\@l{width:calc(100% * 5 / 6.001)}.uk-width-small\@l{width:150px}.uk-width-medium\@l{width:300px}.uk-width-large\@l{width:450px}.uk-width-xlarge\@l{width:600px}.uk-width-xxlarge\@l{width:750px}.uk-width-auto\@l{width:auto}.uk-width-expand\@l{width:1px;flex:1;min-width:0;flex-basis:1px}}@media (min-width: 1600px){.uk-width-1-1\@xl{width:100%}.uk-width-1-2\@xl{width:50%}.uk-width-1-3\@xl{width:calc(100% * 1 / 3.001)}.uk-width-2-3\@xl{width:calc(100% * 2 / 3.001)}.uk-width-1-4\@xl{width:25%}.uk-width-3-4\@xl{width:75%}.uk-width-1-5\@xl{width:20%}.uk-width-2-5\@xl{width:40%}.uk-width-3-5\@xl{width:60%}.uk-width-4-5\@xl{width:80%}.uk-width-1-6\@xl{width:calc(100% * 1 / 6.001)}.uk-width-5-6\@xl{width:calc(100% * 5 / 6.001)}.uk-width-small\@xl{width:150px}.uk-width-medium\@xl{width:300px}.uk-width-large\@xl{width:450px}.uk-width-xlarge\@xl{width:600px}.uk-width-xxlarge\@xl{width:750px}.uk-width-auto\@xl{width:auto}.uk-width-expand\@xl{width:1px;flex:1;min-width:0;flex-basis:1px}}.uk-text-lead{font-size:1.125rem;line-height:1.5;color:#333}.uk-text-meta{font-size:.875rem;line-height:1.4;color:#7a838a}.uk-text-meta a{color:#7a838a}.uk-text-meta a:hover{color:#666;text-decoration:none}.uk-text-small{font-size:.875rem;line-height:1.5}.uk-text-large{font-size:1.375rem;line-height:1.5}.uk-text-bold{font-weight:bolder}.uk-text-uppercase{text-transform:uppercase !important}.uk-text-capitalize{text-transform:capitalize !important}.uk-text-lowercase{text-transform:lowercase !important}.uk-text-muted{color:#7a838a !important}.uk-text-primary{color:#0F1214 !important}.uk-text-success{color:#32d296 !important}.uk-text-warning{color:#faa05a !important}.uk-text-danger{color:#f0506e !important}.uk-text-background{-webkit-background-clip:text;-webkit-text-fill-color:transparent;display:inline-block;color:#0F1214 !important}@supports (-webkit-background-clip: text){.uk-text-background{background-color:#0F1214}}.uk-text-left{text-align:left !important}.uk-text-right{text-align:right !important}.uk-text-center{text-align:center !important}.uk-text-justify{text-align:justify !important}@media (min-width: 640px){.uk-text-left\@s{text-align:left !important}.uk-text-right\@s{text-align:right !important}.uk-text-center\@s{text-align:center !important}}@media (min-width: 960px){.uk-text-left\@m{text-align:left !important}.uk-text-right\@m{text-align:right !important}.uk-text-center\@m{text-align:center !important}}@media (min-width: 1200px){.uk-text-left\@l{text-align:left !important}.uk-text-right\@l{text-align:right !important}.uk-text-center\@l{text-align:center !important}}@media (min-width: 1600px){.uk-text-left\@xl{text-align:left !important}.uk-text-right\@xl{text-align:right !important}.uk-text-center\@xl{text-align:center !important}}.uk-text-top{vertical-align:top !important}.uk-text-middle{vertical-align:middle !important}.uk-text-bottom{vertical-align:bottom !important}.uk-text-baseline{vertical-align:baseline !important}.uk-text-nowrap{white-space:nowrap}.uk-text-truncate{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}th.uk-text-truncate,td.uk-text-truncate{max-width:0}.uk-text-break{overflow-wrap:break-word;word-wrap:break-word;-ms-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}th.uk-text-break,td.uk-text-break{word-break:break-all}[class*='uk-column-']{column-gap:30px}@media (min-width: 1200px){[class*='uk-column-']{column-gap:40px}}[class*='uk-column-'] img{transform:translate3d(0, 0, 0)}.uk-column-divider{column-rule:1px solid #c4c7ca;column-gap:60px}@media (min-width: 1200px){.uk-column-divider{column-gap:80px}}.uk-column-1-2{column-count:2}.uk-column-1-3{column-count:3}.uk-column-1-4{column-count:4}.uk-column-1-5{column-count:5}.uk-column-1-6{column-count:6}@media (min-width: 640px){.uk-column-1-2\@s{column-count:2}.uk-column-1-3\@s{column-count:3}.uk-column-1-4\@s{column-count:4}.uk-column-1-5\@s{column-count:5}.uk-column-1-6\@s{column-count:6}}@media (min-width: 960px){.uk-column-1-2\@m{column-count:2}.uk-column-1-3\@m{column-count:3}.uk-column-1-4\@m{column-count:4}.uk-column-1-5\@m{column-count:5}.uk-column-1-6\@m{column-count:6}}@media (min-width: 1200px){.uk-column-1-2\@l{column-count:2}.uk-column-1-3\@l{column-count:3}.uk-column-1-4\@l{column-count:4}.uk-column-1-5\@l{column-count:5}.uk-column-1-6\@l{column-count:6}}@media (min-width: 1600px){.uk-column-1-2\@xl{column-count:2}.uk-column-1-3\@xl{column-count:3}.uk-column-1-4\@xl{column-count:4}.uk-column-1-5\@xl{column-count:5}.uk-column-1-6\@xl{column-count:6}}.uk-column-span{column-span:all}.uk-cover{max-width:none;position:absolute;left:50%;top:50%;transform:translate(-50%, -50%)}iframe.uk-cover{pointer-events:none}.uk-cover-container{overflow:hidden;position:relative}[class*='uk-align']{display:block;margin-bottom:30px}*+[class*='uk-align']{margin-top:30px}.uk-align-center{margin-left:auto;margin-right:auto}.uk-align-left{margin-top:0;margin-right:30px;float:left}.uk-align-right{margin-top:0;margin-left:30px;float:right}@media (min-width: 640px){.uk-align-left\@s{margin-top:0;margin-right:30px;float:left}.uk-align-right\@s{margin-top:0;margin-left:30px;float:right}}@media (min-width: 960px){.uk-align-left\@m{margin-top:0;margin-right:30px;float:left}.uk-align-right\@m{margin-top:0;margin-left:30px;float:right}}@media (min-width: 1200px){.uk-align-left\@l{margin-top:0;float:left}.uk-align-right\@l{margin-top:0;float:right}.uk-align-left,.uk-align-left\@s,.uk-align-left\@m,.uk-align-left\@l{margin-right:40px}.uk-align-right,.uk-align-right\@s,.uk-align-right\@m,.uk-align-right\@l{margin-left:40px}}@media (min-width: 1600px){.uk-align-left\@xl{margin-top:0;margin-right:40px;float:left}.uk-align-right\@xl{margin-top:0;margin-left:40px;float:right}}.uk-panel{position:relative;box-sizing:border-box}.uk-panel::before,.uk-panel::after{content:"";display:table}.uk-panel::after{clear:both}.uk-panel>:last-child{margin-bottom:0}.uk-panel-scrollable{height:170px;padding:10px;border:1px solid #c4c7ca;overflow:auto;-webkit-overflow-scrolling:touch;resize:both}.uk-clearfix::before{content:"";display:table-cell}.uk-clearfix::after{content:"";display:table;clear:both}.uk-float-left{float:left}.uk-float-right{float:right}[class*='uk-float-']{max-width:100%}.uk-overflow-hidden{overflow:hidden}.uk-overflow-auto{overflow:auto;-webkit-overflow-scrolling:touch}.uk-overflow-auto>:last-child{margin-bottom:0}.uk-resize{resize:both}.uk-resize-vertical{resize:vertical}.uk-display-block{display:block !important}.uk-display-inline{display:inline !important}.uk-display-inline-block{display:inline-block !important}[class*='uk-inline']{display:inline-block;position:relative;max-width:100%;vertical-align:middle;-webkit-backface-visibility:hidden}.uk-inline-clip{overflow:hidden}[class*='uk-height']{box-sizing:border-box}.uk-height-1-1{height:100%}.uk-height-viewport{min-height:100vh}.uk-height-small{height:150px}.uk-height-medium{height:300px}.uk-height-large{height:450px}.uk-height-max-small{max-height:150px}.uk-height-max-medium{max-height:300px}.uk-height-max-large{max-height:450px}.uk-preserve-width,.uk-preserve-width audio,.uk-preserve-width canvas,.uk-preserve-width img,.uk-preserve-width svg,.uk-preserve-width video{max-width:none}.uk-responsive-width,.uk-responsive-height{box-sizing:border-box}.uk-responsive-width{max-width:100% !important;height:auto}.uk-responsive-height{max-height:100%;width:auto;max-width:none}.uk-border-circle{border-radius:50%}.uk-border-rounded{border-radius:5px}.uk-inline-clip[class*='uk-border-']{-webkit-transform:translateZ(0)}.uk-box-shadow-small{box-shadow:0 2px 8px rgba(0,0,0,0.08)}.uk-box-shadow-medium{box-shadow:0 5px 15px rgba(0,0,0,0.08)}.uk-box-shadow-large{box-shadow:0 14px 25px rgba(0,0,0,0.16)}.uk-box-shadow-xlarge{box-shadow:0 28px 50px rgba(0,0,0,0.16)}[class*='uk-box-shadow-hover']{transition:box-shadow .1s ease-in-out}.uk-box-shadow-hover-small:hover{box-shadow:0 2px 8px rgba(0,0,0,0.08)}.uk-box-shadow-hover-medium:hover{box-shadow:0 5px 15px rgba(0,0,0,0.08)}.uk-box-shadow-hover-large:hover{box-shadow:0 14px 25px rgba(0,0,0,0.16)}.uk-box-shadow-hover-xlarge:hover{box-shadow:0 28px 50px rgba(0,0,0,0.16)}@supports (filter: blur(0)){.uk-box-shadow-bottom{display:inline-block;position:relative;max-width:100%;vertical-align:middle}.uk-box-shadow-bottom::before{content:'';position:absolute;bottom:-30px;left:0;right:0;height:30px;border-radius:100%;background:#444;filter:blur(20px)}.uk-box-shadow-bottom>*{position:relative}}.uk-dropcap::first-letter,.uk-dropcap>p:first-of-type::first-letter{display:block;margin-right:10px;float:left;font-size:4.5em;line-height:1;margin-bottom:-2px}.uk-leader{overflow:hidden}.uk-leader-fill::after{display:inline-block;margin-left:15px;width:0;content:attr(data-fill);white-space:nowrap}.uk-leader-fill.uk-leader-hide::after{display:none}.var-leader-fill:before{content:"."}.uk-logo{font-size:1.375rem;font-family:system-ui;color:#666;text-decoration:none}.uk-logo:hover,.uk-logo:focus{color:#666;outline:none;text-decoration:none}.uk-logo-inverse{display:none}.uk-svg,.uk-svg:not(.uk-preserve) [fill*='#']:not(.uk-preserve),.uk-svg:not(.uk-preserve) [FILL*='#']:not(.uk-preserve){fill:currentcolor}.uk-svg:not(.uk-preserve) [stroke*='#']:not(.uk-preserve),.uk-svg:not(.uk-preserve) [STROKE*='#']:not(.uk-preserve){stroke:currentcolor}.uk-svg{transform:translate(0, 0)}.uk-disabled{pointer-events:none}.uk-drag,.uk-drag *{cursor:move}.uk-drag iframe{pointer-events:none}.uk-dragover{box-shadow:0 0 20px rgba(100,100,100,0.3)}.uk-blend-multiply{mix-blend-mode:multiply}.uk-blend-screen{mix-blend-mode:screen}.uk-blend-overlay{mix-blend-mode:overlay}.uk-blend-darken{mix-blend-mode:darken}.uk-blend-lighten{mix-blend-mode:lighten}.uk-blend-color-dodge{mix-blend-mode:color-dodge}.uk-blend-color-burn{mix-blend-mode:color-burn}.uk-blend-hard-light{mix-blend-mode:hard-light}.uk-blend-soft-light{mix-blend-mode:soft-light}.uk-blend-difference{mix-blend-mode:difference}.uk-blend-exclusion{mix-blend-mode:exclusion}.uk-blend-hue{mix-blend-mode:hue}.uk-blend-saturation{mix-blend-mode:saturation}.uk-blend-color{mix-blend-mode:color}.uk-blend-luminosity{mix-blend-mode:luminosity}.uk-transform-center{transform:translate(-50%, -50%)}.uk-transform-origin-top-left{transform-origin:0 0}.uk-transform-origin-top-center{transform-origin:50% 0}.uk-transform-origin-top-right{transform-origin:100% 0}.uk-transform-origin-center-left{transform-origin:0 50%}.uk-transform-origin-center-right{transform-origin:100% 50%}.uk-transform-origin-bottom-left{transform-origin:0 100%}.uk-transform-origin-bottom-center{transform-origin:50% 100%}.uk-transform-origin-bottom-right{transform-origin:100% 100%}.remove-underline,.remove-underline:hover{text-decoration:none}.link-dark{color:#0F1214 !important}.uk-container.uk-container-xsmall{max-width:700px}.hvr-forward{display:inline-block;vertical-align:middle;-webkit-transform:perspective(1px) translateZ(0);transform:perspective(1px) translateZ(0);box-shadow:0 0 1px transparent;-webkit-transition-duration:0.3s;transition-duration:0.3s;-webkit-transition-property:transform;transition-property:transform}.hvr-forward:active,.hvr-forward:focus,.hvr-forward:hover{-webkit-transform:translateX(6px);transform:translateX(6px)}.hvr-back{display:inline-block;vertical-align:middle;-webkit-transform:perspective(1px) translateZ(0);transform:perspective(1px) translateZ(0);box-shadow:0 0 1px transparent;-webkit-transition-duration:0.3s;transition-duration:0.3s;-webkit-transition-property:transform;transition-property:transform}.hvr-back:hover,.hvr-back:focus,.hvr-back:active{-webkit-transform:translateX(-6px);transform:translateX(-6px)}.social-networks{margin-top:70px}header .uk-logo{color:#0F1214}header .uk-logo:hover{color:#0F1214}.section-title{margin-bottom:40px}.section-title ~ .section-title{margin-top:40px}@media (min-width: 960px){.section-title ~ .section-title{margin-top:70px}}.section-hero .searchBox{max-width:550px;margin:60px auto 0 auto}.section-hero .searchBox .uk-search-input{height:50px;border-radius:50px;color:#7a838a;font-style:normal}.section-hero .searchBox .uk-search-input:focus{background:#ffffff}.section-hero .searchBox .uk-search-icon{width:50px;color:#7a838a}footer .uk-subnav>.uk-active>a{color:#7a838a}#markdown-toc{padding:0 0 0 20px;border-left:solid 2px #7a838a;list-style:none;margin-bottom:40px}#markdown-toc>li>:last-child{margin-bottom:0}#markdown-toc ul{margin:0;padding-left:20px;list-style:none}#markdown-toc>li:nth-child(n+2),#markdown-toc>li>ul{margin-top:5px}#markdown-toc a{color:#7a838a}.uk-article-content .no_toc{margin-top:40px;margin-bottom:40px}#searchBox-results,#searchBox-results{margin:10px 0 0 0;z-index:1}#searchBox-results li,#searchBox-results li{margin:0;padding:20px 25px 0;background:#fff;line-height:1.4;border-left:solid 1px #c4c7ca;border-right:solid 1px #c4c7ca}#searchBox-results li:first-child,#searchBox-results li:first-child{border-top-left-radius:5px;border-top-right-radius:5px;border-top:solid 1px #c4c7ca}#searchBox-results li:last-child,#searchBox-results li:last-child{border-bottom-left-radius:5px;border-bottom-right-radius:5px;padding-bottom:25px;border-bottom:solid 1px #c4c7ca}#searchBox-results li a:hover,#searchBox-results li a:hover{text-decoration:none}.uk-flex{display:flex}.uk-flex-inline{display:inline-flex}.uk-flex::before,.uk-flex::after,.uk-flex-inline::before,.uk-flex-inline::after{display:none}.uk-flex-left{justify-content:flex-start}.uk-flex-center{justify-content:center}.uk-flex-right{justify-content:flex-end}.uk-flex-between{justify-content:space-between}.uk-flex-around{justify-content:space-around}@media (min-width: 640px){.uk-flex-left\@s{justify-content:flex-start}.uk-flex-center\@s{justify-content:center}.uk-flex-right\@s{justify-content:flex-end}.uk-flex-between\@s{justify-content:space-between}.uk-flex-around\@s{justify-content:space-around}}@media (min-width: 960px){.uk-flex-left\@m{justify-content:flex-start}.uk-flex-center\@m{justify-content:center}.uk-flex-right\@m{justify-content:flex-end}.uk-flex-between\@m{justify-content:space-between}.uk-flex-around\@m{justify-content:space-around}}@media (min-width: 1200px){.uk-flex-left\@l{justify-content:flex-start}.uk-flex-center\@l{justify-content:center}.uk-flex-right\@l{justify-content:flex-end}.uk-flex-between\@l{justify-content:space-between}.uk-flex-around\@l{justify-content:space-around}}@media (min-width: 1600px){.uk-flex-left\@xl{justify-content:flex-start}.uk-flex-center\@xl{justify-content:center}.uk-flex-right\@xl{justify-content:flex-end}.uk-flex-between\@xl{justify-content:space-between}.uk-flex-around\@xl{justify-content:space-around}}.uk-flex-stretch{align-items:stretch}.uk-flex-top{align-items:flex-start}.uk-flex-middle{align-items:center}.uk-flex-bottom{align-items:flex-end}.uk-flex-row{flex-direction:row}.uk-flex-row-reverse{flex-direction:row-reverse}.uk-flex-column{flex-direction:column}.uk-flex-column-reverse{flex-direction:column-reverse}.uk-flex-nowrap{flex-wrap:nowrap}.uk-flex-wrap{flex-wrap:wrap}.uk-flex-wrap-reverse{flex-wrap:wrap-reverse}.uk-flex-wrap-stretch{align-content:stretch}.uk-flex-wrap-top{align-content:flex-start}.uk-flex-wrap-middle{align-content:center}.uk-flex-wrap-bottom{align-content:flex-end}.uk-flex-wrap-between{align-content:space-between}.uk-flex-wrap-around{align-content:space-around}.uk-flex-first{order:-1}.uk-flex-last{order:99}@media (min-width: 640px){.uk-flex-first\@s{order:-1}.uk-flex-last\@s{order:99}}@media (min-width: 960px){.uk-flex-first\@m{order:-1}.uk-flex-last\@m{order:99}}@media (min-width: 1200px){.uk-flex-first\@l{order:-1}.uk-flex-last\@l{order:99}}@media (min-width: 1600px){.uk-flex-first\@xl{order:-1}.uk-flex-last\@xl{order:99}}.uk-flex-none{flex:none}.uk-flex-auto{flex:auto}.uk-flex-1{flex:1}.uk-margin{margin-bottom:20px}*+.uk-margin{margin-top:20px !important}.uk-margin-top{margin-top:20px !important}.uk-margin-bottom{margin-bottom:20px !important}.uk-margin-left{margin-left:20px !important}.uk-margin-right{margin-right:20px !important}.uk-margin-small{margin-bottom:10px}*+.uk-margin-small{margin-top:10px !important}.uk-margin-small-top{margin-top:10px !important}.uk-margin-small-bottom{margin-bottom:10px !important}.uk-margin-small-left{margin-left:10px !important}.uk-margin-small-right{margin-right:10px !important}.uk-margin-medium{margin-bottom:40px}*+.uk-margin-medium{margin-top:40px !important}.uk-margin-medium-top{margin-top:40px !important}.uk-margin-medium-bottom{margin-bottom:40px !important}.uk-margin-medium-left{margin-left:40px !important}.uk-margin-medium-right{margin-right:40px !important}.uk-margin-large{margin-bottom:40px}*+.uk-margin-large{margin-top:40px !important}.uk-margin-large-top{margin-top:40px !important}.uk-margin-large-bottom{margin-bottom:40px !important}.uk-margin-large-left{margin-left:40px !important}.uk-margin-large-right{margin-right:40px !important}@media (min-width: 1200px){.uk-margin-large{margin-bottom:70px}*+.uk-margin-large{margin-top:70px !important}.uk-margin-large-top{margin-top:70px !important}.uk-margin-large-bottom{margin-bottom:70px !important}.uk-margin-large-left{margin-left:70px !important}.uk-margin-large-right{margin-right:70px !important}}.uk-margin-xlarge{margin-bottom:70px}*+.uk-margin-xlarge{margin-top:70px !important}.uk-margin-xlarge-top{margin-top:70px !important}.uk-margin-xlarge-bottom{margin-bottom:70px !important}.uk-margin-xlarge-left{margin-left:70px !important}.uk-margin-xlarge-right{margin-right:70px !important}@media (min-width: 1200px){.uk-margin-xlarge{margin-bottom:140px}*+.uk-margin-xlarge{margin-top:140px !important}.uk-margin-xlarge-top{margin-top:140px !important}.uk-margin-xlarge-bottom{margin-bottom:140px !important}.uk-margin-xlarge-left{margin-left:140px !important}.uk-margin-xlarge-right{margin-right:140px !important}}.uk-margin-remove{margin:0 !important}.uk-margin-remove-top{margin-top:0 !important}.uk-margin-remove-bottom{margin-bottom:0 !important}.uk-margin-remove-left{margin-left:0 !important}.uk-margin-remove-right{margin-right:0 !important}.uk-margin-remove-vertical{margin-top:0 !important;margin-bottom:0 !important}.uk-margin-remove-adjacent+*{margin-top:0 !important}.uk-margin-auto{margin-left:auto !important;margin-right:auto !important}.uk-margin-auto-top{margin-top:auto !important}.uk-margin-auto-bottom{margin-bottom:auto !important}.uk-margin-auto-left{margin-left:auto !important}.uk-margin-auto-right{margin-right:auto !important}.uk-margin-auto-vertical{margin-top:auto !important;margin-bottom:auto !important}.uk-padding{padding:30px}@media (min-width: 1200px){.uk-padding{padding:40px}}.uk-padding-small{padding:15px}.uk-padding-large{padding:30px}@media (min-width: 1200px){.uk-padding-large{padding:70px}}.uk-padding-remove{padding:0 !important}.uk-padding-remove-top{padding-top:0 !important}.uk-padding-remove-bottom{padding-bottom:0 !important}.uk-padding-remove-left{padding-left:0 !important}.uk-padding-remove-right{padding-right:0 !important}.uk-padding-remove-vertical{padding-top:0 !important;padding-bottom:0 !important}.uk-padding-remove-horizontal{padding-left:0 !important;padding-right:0 !important}[class*='uk-position-top'],[class*='uk-position-bottom'],[class*='uk-position-left'],[class*='uk-position-right'],[class*='uk-position-center']{position:absolute !important}.uk-position-top{top:0;left:0;right:0}.uk-position-bottom{bottom:0;left:0;right:0}.uk-position-left{top:0;bottom:0;left:0}.uk-position-right{top:0;bottom:0;right:0}.uk-position-top-left{top:0;left:0}.uk-position-top-right{top:0;right:0}.uk-position-bottom-left{bottom:0;left:0}.uk-position-bottom-right{bottom:0;right:0}.uk-position-center{top:50%;left:50%;transform:translate(-50%, -50%);display:table;width:-moz-max-content;max-width:100%;box-sizing:border-box}[class*='uk-position-center-left'],[class*='uk-position-center-right']{top:50%;transform:translateY(-50%)}.uk-position-center-left{left:0}.uk-position-center-right{right:0}.uk-position-center-left-out{right:100%;width:max-content}.uk-position-center-right-out{left:100%;width:max-content}.uk-position-top-center,.uk-position-bottom-center{left:50%;transform:translateX(-50%);display:table;width:-moz-max-content;max-width:100%;box-sizing:border-box}.uk-position-top-center{top:0}.uk-position-bottom-center{bottom:0}.uk-position-cover{position:absolute;top:0;bottom:0;left:0;right:0}.uk-position-relative{position:relative !important}.uk-position-absolute{position:absolute !important}.uk-position-fixed{position:fixed !important}.uk-position-z-index{z-index:1}.uk-position-small{margin:15px}.uk-position-small.uk-position-center{transform:translate(-50%, -50%) translate(-15px, -15px)}.uk-position-small[class*='uk-position-center-left'],.uk-position-small[class*='uk-position-center-right']{transform:translateY(-50%) translateY(-15px)}.uk-position-small.uk-position-top-center,.uk-position-small.uk-position-bottom-center{transform:translateX(-50%) translateX(-15px)}.uk-position-medium{margin:30px}.uk-position-medium.uk-position-center{transform:translate(-50%, -50%) translate(-30px, -30px)}.uk-position-medium[class*='uk-position-center-left'],.uk-position-medium[class*='uk-position-center-right']{transform:translateY(-50%) translateY(-30px)}.uk-position-medium.uk-position-top-center,.uk-position-medium.uk-position-bottom-center{transform:translateX(-50%) translateX(-30px)}.uk-position-large{margin:30px}.uk-position-large.uk-position-center{transform:translate(-50%, -50%) translate(-30px, -30px)}.uk-position-large[class*='uk-position-center-left'],.uk-position-large[class*='uk-position-center-right']{transform:translateY(-50%) translateY(-30px)}.uk-position-large.uk-position-top-center,.uk-position-large.uk-position-bottom-center{transform:translateX(-50%) translateX(-30px)}@media (min-width: 1200px){.uk-position-large{margin:50px}.uk-position-large.uk-position-center{transform:translate(-50%, -50%) translate(-50px, -50px)}.uk-position-large[class*='uk-position-center-left'],.uk-position-large[class*='uk-position-center-right']{transform:translateY(-50%) translateY(-50px)}.uk-position-large.uk-position-top-center,.uk-position-large.uk-position-bottom-center{transform:translateX(-50%) translateX(-50px)}}[hidden],.uk-hidden{display:none !important}@media (min-width: 640px){.uk-hidden\@s{display:none !important}}@media (min-width: 960px){.uk-hidden\@m{display:none !important}}@media (min-width: 1200px){.uk-hidden\@l{display:none !important}}@media (min-width: 1600px){.uk-hidden\@xl{display:none !important}}@media (max-width: 639px){.uk-visible\@s{display:none !important}}@media (max-width: 959px){.uk-visible\@m{display:none !important}}@media (max-width: 1199px){.uk-visible\@l{display:none !important}}@media (max-width: 1599px){.uk-visible\@xl{display:none !important}}.uk-invisible{visibility:hidden !important}.uk-visible-toggle:not(:hover):not(.uk-hover) .uk-hidden-hover:not(:focus){position:absolute !important;width:0 !important;height:0 !important;padding:0 !important;margin:0 !important;overflow:hidden !important}.uk-visible-toggle:not(:hover):not(.uk-hover) .uk-invisible-hover:not(:focus){opacity:0 !important}@media (pointer: coarse){.uk-hidden-touch{display:none !important}}.uk-hidden-notouch{display:none !important}@media (pointer: coarse){.uk-hidden-notouch{display:block !important}}@media print{*,*::before,*::after{background:transparent !important;color:black !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.highlight,.highlighter-rouge{background-color:#F7F8FA;color:#5A6575;border:none}.highlight .lineno{color:#B1B8C4}.highlight .c{color:#B1B8C4}.highlight .err{color:#5A6575}.highlight .g{color:#5A6575}.highlight .k{color:#25BEA1}.highlight .l{color:#5A6575}.highlight .n{color:#5A6575}.highlight .o{color:#25BEA1}.highlight .x{color:#FBBC09}.highlight .p{color:#25BEA1}.highlight .cm{color:#B1B8C4}.highlight .cp{color:#25BEA1}.highlight .c1{color:#B1B8C4}.highlight .cs{color:#25BEA1}.highlight .gd{color:#E56134}.highlight .ge{color:#5A6575;font-style:italic}.highlight .gr{color:#9961C9}.highlight .gh{color:#FBBC09}.highlight .gi{color:#25BEA1}.highlight .go{color:#5A6575}.highlight .gp{color:#FBBC09}.highlight .gs{color:#5A6575;font-weight:bold}.highlight .gu{color:#FBBC09}.highlight .gt{color:#5A6575}.highlight .kc{color:#FBBC09}.highlight .kd{color:#11A0F3}.highlight .kn{color:#25BEA1}.highlight .kp{color:#25BEA1}.highlight .kr{color:#11A0F3}.highlight .kt{color:#9961C9}.highlight .ld{color:#5A6575}.highlight .m{color:#E56134}.highlight .s{color:#E56134}.highlight .na{color:#5A6575}.highlight .nb{color:#5A6575}.highlight .nc{color:#11A0F3}.highlight .no{color:#5A6575}.highlight .nd{color:#11A0F3}.highlight .ni{color:#FBBC09}.highlight .ne{color:#FBBC09}.highlight .nf{color:#11A0F3}.highlight .nl{color:#5A6575}.highlight .nn{color:#5A6575}.highlight .nx{color:#5A6575}.highlight .py{color:#5A6575}.highlight .nt{color:#11A0F3}.highlight .nv{color:#5A6575}.highlight .ow{color:#25BEA1}.highlight .w{color:#5A6575}.highlight .mf{color:#E56134}.highlight .mh{color:#E56134}.highlight .mi{color:#E56134}.highlight .mo{color:#E56134}.highlight .sb{color:#B1B8C4}.highlight .sc{color:#E56134}.highlight .sd{color:#5A6575}.highlight .s2{color:#E56134}.highlight .se{color:#FBBC09}.highlight .sh{color:#5A6575}.highlight .si{color:#E56134}.highlight .sx{color:#E56134}.highlight .sr{color:#9961C9}.highlight .s1{color:#E56134}.highlight .ss{color:#E56134}.highlight .bp{color:#11A0F3}.highlight .vc{color:#11A0F3}.highlight .vg{color:#11A0F3}.highlight .vi{color:#11A0F3}.highlight .il{color:#E56134} diff --git a/_site/assets/img/favicon.png b/_site/assets/img/favicon.png deleted file mode 100644 index 09d6ca94a9..0000000000 Binary files a/_site/assets/img/favicon.png and /dev/null differ diff --git a/_site/assets/img/location.svg b/_site/assets/img/location.svg deleted file mode 100644 index e71e58b04c..0000000000 --- a/_site/assets/img/location.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - location-dark - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/_site/assets/img/touch-icon.png b/_site/assets/img/touch-icon.png deleted file mode 100644 index 243615bb9f..0000000000 Binary files a/_site/assets/img/touch-icon.png and /dev/null differ diff --git a/_site/assets/js/custom.js b/_site/assets/js/custom.js deleted file mode 100644 index d123eb3515..0000000000 --- a/_site/assets/js/custom.js +++ /dev/null @@ -1 +0,0 @@ -// Custom scripts diff --git a/_site/assets/js/main.js b/_site/assets/js/main.js deleted file mode 100644 index 031cf722e2..0000000000 --- a/_site/assets/js/main.js +++ /dev/null @@ -1,8772 +0,0 @@ -(function(global, factory) { - typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define("uikit", factory) : global.UIkit = factory(); -})(this, function() { - "use strict"; - function bind(fn, context) { - return function(a) { - var l = arguments.length; - return l ? l > 1 ? fn.apply(context, arguments) : fn.call(context, a) : fn.call(context); - }; - } - var ref = Object.prototype; - var hasOwnProperty = ref.hasOwnProperty; - function hasOwn(obj, key) { - return hasOwnProperty.call(obj, key); - } - var hyphenateRe = /([a-z\d])([A-Z])/g; - function hyphenate(str) { - return str.replace(hyphenateRe, "$1-$2").toLowerCase(); - } - var camelizeRE = /-(\w)/g; - function camelize(str) { - return str.replace(camelizeRE, toUpper); - } - function toUpper(_, c) { - return c ? c.toUpperCase() : ""; - } - function ucfirst(str) { - return str.length ? toUpper(null, str.charAt(0)) + str.slice(1) : ""; - } - var strPrototype = String.prototype; - var startsWithFn = strPrototype.startsWith || function(search) { - return this.lastIndexOf(search, 0) === 0; - }; - function startsWith(str, search) { - return startsWithFn.call(str, search); - } - var endsWithFn = strPrototype.endsWith || function(search) { - return this.substr(-search.length) === search; - }; - function endsWith(str, search) { - return endsWithFn.call(str, search); - } - var includesFn = function(search) { - return ~this.indexOf(search); - }; - var includesStr = strPrototype.includes || includesFn; - var includesArray = Array.prototype.includes || includesFn; - function includes(obj, search) { - return obj && (isString(obj) ? includesStr : includesArray).call(obj, search); - } - var isArray = Array.isArray; - function isFunction(obj) { - return typeof obj === "function"; - } - function isObject(obj) { - return obj !== null && typeof obj === "object"; - } - function isPlainObject(obj) { - return isObject(obj) && Object.getPrototypeOf(obj) === Object.prototype; - } - function isWindow(obj) { - return isObject(obj) && obj === obj.window; - } - function isDocument(obj) { - return isObject(obj) && obj.nodeType === 9; - } - function isJQuery(obj) { - return isObject(obj) && !!obj.jquery; - } - function isNode(element) { - return element instanceof Node || isObject(element) && element.nodeType === 1; - } - function isNodeCollection(element) { - return element instanceof NodeList || element instanceof HTMLCollection; - } - function isBoolean(value) { - return typeof value === "boolean"; - } - function isString(value) { - return typeof value === "string"; - } - function isNumber(value) { - return typeof value === "number"; - } - function isNumeric(value) { - return isNumber(value) || isString(value) && !isNaN(value - parseFloat(value)); - } - function isUndefined(value) { - return value === void 0; - } - function toBoolean(value) { - return isBoolean(value) ? value : value === "true" || value === "1" || value === "" ? true : value === "false" || value === "0" ? false : value; - } - function toNumber(value) { - var number = Number(value); - return !isNaN(number) ? number : false; - } - function toFloat(value) { - return parseFloat(value) || 0; - } - function toNode(element) { - return isNode(element) || isWindow(element) || isDocument(element) ? element : isNodeCollection(element) || isJQuery(element) ? element[0] : isArray(element) ? toNode(element[0]) : null; - } - var arrayProto = Array.prototype; - function toNodes(element) { - return isNode(element) ? [ element ] : isNodeCollection(element) ? arrayProto.slice.call(element) : isArray(element) ? element.map(toNode).filter(Boolean) : isJQuery(element) ? element.toArray() : []; - } - function toList(value) { - return isArray(value) ? value : isString(value) ? value.split(/,(?![^(]*\))/).map(function(value) { - return isNumeric(value) ? toNumber(value) : toBoolean(value.trim()); - }) : [ value ]; - } - function toMs(time) { - return !time ? 0 : endsWith(time, "ms") ? toFloat(time) : toFloat(time) * 1e3; - } - function swap(value, a, b) { - return value.replace(new RegExp(a + "|" + b, "mg"), function(match) { - return match === a ? b : a; - }); - } - var assign = Object.assign || function(target) { - var args = [], len = arguments.length - 1; - while (len-- > 0) args[len] = arguments[len + 1]; - target = Object(target); - for (var i = 0; i < args.length; i++) { - var source = args[i]; - if (source !== null) { - for (var key in source) { - if (hasOwn(source, key)) { - target[key] = source[key]; - } - } - } - } - return target; - }; - function each(obj, cb) { - for (var key in obj) { - if (cb.call(obj[key], obj[key], key) === false) { - break; - } - } - } - function sortBy(collection, prop) { - return collection.sort(function(a, b) { - return a[prop] - b[prop]; - }); - } - function clamp(number, min, max) { - if (min === void 0) min = 0; - if (max === void 0) max = 1; - return Math.min(Math.max(number, min), max); - } - function noop() {} - function intersectRect(r1, r2) { - return r1.left <= r2.right && r2.left <= r1.right && r1.top <= r2.bottom && r2.top <= r1.bottom; - } - function pointInRect(point, rect) { - return intersectRect({ - top: point.y, - bottom: point.y, - left: point.x, - right: point.x - }, rect); - } - var Dimensions = { - ratio: function ratio(dimensions, prop, value) { - var obj; - var aProp = prop === "width" ? "height" : "width"; - return obj = {}, obj[aProp] = Math.round(value * dimensions[aProp] / dimensions[prop]), - obj[prop] = value, obj; - }, - contain: function contain(dimensions, maxDimensions) { - var this$1 = this; - dimensions = assign({}, dimensions); - each(dimensions, function(_, prop) { - return dimensions = dimensions[prop] > maxDimensions[prop] ? this$1.ratio(dimensions, prop, maxDimensions[prop]) : dimensions; - }); - return dimensions; - }, - cover: function cover(dimensions, maxDimensions) { - var this$1 = this; - dimensions = this.contain(dimensions, maxDimensions); - each(dimensions, function(_, prop) { - return dimensions = dimensions[prop] < maxDimensions[prop] ? this$1.ratio(dimensions, prop, maxDimensions[prop]) : dimensions; - }); - return dimensions; - } - }; - function attr(element, name, value) { - if (isObject(name)) { - for (var key in name) { - attr(element, key, name[key]); - } - return; - } - if (isUndefined(value)) { - element = toNode(element); - return element && element.getAttribute(name); - } else { - toNodes(element).forEach(function(element) { - if (isFunction(value)) { - value = value.call(element, attr(element, name)); - } - if (value === null) { - removeAttr(element, name); - } else { - element.setAttribute(name, value); - } - }); - } - } - function hasAttr(element, name) { - return toNodes(element).some(function(element) { - return element.hasAttribute(name); - }); - } - function removeAttr(element, name) { - element = toNodes(element); - name.split(" ").forEach(function(name) { - return element.forEach(function(element) { - return element.removeAttribute(name); - }); - }); - } - function filterAttr(element, attribute, pattern, replacement) { - attr(element, attribute, function(value) { - return value ? value.replace(pattern, replacement) : value; - }); - } - function data(element, attribute) { - for (var i = 0, attrs = [ attribute, "data-" + attribute ]; i < attrs.length; i++) { - if (hasAttr(element, attrs[i])) { - return attr(element, attrs[i]); - } - } - } - function query(selector, context) { - return toNode(selector) || find(selector, isContextSelector(selector) ? context : document); - } - function queryAll(selector, context) { - var nodes = toNodes(selector); - return nodes.length && nodes || findAll(selector, isContextSelector(selector) ? context : document); - } - function find(selector, context) { - return toNode(_query(selector, context, "querySelector")); - } - function findAll(selector, context) { - return toNodes(_query(selector, context, "querySelectorAll")); - } - function _query(selector, context, queryFn) { - if (context === void 0) context = document; - if (!selector || !isString(selector)) { - return null; - } - selector = selector.replace(contextSanitizeRe, "$1 *"); - var removes; - if (isContextSelector(selector)) { - removes = []; - selector = selector.split(",").map(function(selector, i) { - var ctx = context; - selector = selector.trim(); - if (selector[0] === "!") { - var selectors = selector.substr(1).trim().split(" "); - ctx = closest(context.parentNode, selectors[0]); - selector = selectors.slice(1).join(" "); - } - if (!ctx) { - return null; - } - if (!ctx.id) { - ctx.id = "uk-" + Date.now() + i; - removes.push(function() { - return removeAttr(ctx, "id"); - }); - } - return "#" + escape(ctx.id) + " " + selector; - }).filter(Boolean).join(","); - context = document; - } - try { - return context[queryFn](selector); - } catch (e) { - return null; - } finally { - removes && removes.forEach(function(remove) { - return remove(); - }); - } - } - var contextSelectorRe = /(^|,)\s*[!>+~]/; - var contextSanitizeRe = /([!>+~])(?=\s+[!>+~]|\s*$)/g; - function isContextSelector(selector) { - return isString(selector) && selector.match(contextSelectorRe); - } - var elProto = Element.prototype; - var matchesFn = elProto.matches || elProto.webkitMatchesSelector || elProto.msMatchesSelector; - function matches(element, selector) { - return toNodes(element).some(function(element) { - return matchesFn.call(element, selector); - }); - } - var closestFn = elProto.closest || function(selector) { - var ancestor = this; - do { - if (matches(ancestor, selector)) { - return ancestor; - } - ancestor = ancestor.parentNode; - } while (ancestor && ancestor.nodeType === 1); - }; - function closest(element, selector) { - if (startsWith(selector, ">")) { - selector = selector.slice(1); - } - return isNode(element) ? element.parentNode && closestFn.call(element, selector) : toNodes(element).map(function(element) { - return element.parentNode && closestFn.call(element, selector); - }).filter(Boolean); - } - function parents(element, selector) { - var elements = []; - var parent = toNode(element).parentNode; - while (parent && parent.nodeType === 1) { - if (matches(parent, selector)) { - elements.push(parent); - } - parent = parent.parentNode; - } - return elements; - } - var escapeFn = window.CSS && CSS.escape || function(css) { - return css.replace(/([^\x7f-\uFFFF\w-])/g, function(match) { - return "\\" + match; - }); - }; - function escape(css) { - return isString(css) ? escapeFn.call(null, css) : ""; - } - var voidElements = { - area: true, - base: true, - br: true, - col: true, - embed: true, - hr: true, - img: true, - input: true, - keygen: true, - link: true, - menuitem: true, - meta: true, - param: true, - source: true, - track: true, - wbr: true - }; - function isVoidElement(element) { - return toNodes(element).some(function(element) { - return voidElements[element.tagName.toLowerCase()]; - }); - } - function isVisible(element) { - return toNodes(element).some(function(element) { - return element.offsetHeight || element.getBoundingClientRect().height; - }); - } - var selInput = "input,select,textarea,button"; - function isInput(element) { - return toNodes(element).some(function(element) { - return matches(element, selInput); - }); - } - function filter(element, selector) { - return toNodes(element).filter(function(element) { - return matches(element, selector); - }); - } - function within(element, selector) { - return !isString(selector) ? element === selector || (isDocument(selector) ? selector.documentElement : toNode(selector)).contains(toNode(element)) : matches(element, selector) || closest(element, selector); - } - function on() { - var args = [], len = arguments.length; - while (len--) args[len] = arguments[len]; - var ref = getArgs(args); - var target = ref[0]; - var type = ref[1]; - var selector = ref[2]; - var listener = ref[3]; - var useCapture = ref[4]; - target = toEventTarget(target); - if (selector) { - listener = delegate(target, selector, listener); - } - if (listener.length > 1) { - listener = detail(listener); - } - type.split(" ").forEach(function(type) { - return target && target.addEventListener(type, listener, useCapture); - }); - return function() { - return off(target, type, listener, useCapture); - }; - } - function off(target, type, listener, useCapture) { - if (useCapture === void 0) useCapture = false; - target = toEventTarget(target); - target && type.split(" ").forEach(function(type) { - return target.removeEventListener(type, listener, useCapture); - }); - } - function once() { - var args = [], len = arguments.length; - while (len--) args[len] = arguments[len]; - var ref = getArgs(args); - var element = ref[0]; - var type = ref[1]; - var selector = ref[2]; - var listener = ref[3]; - var useCapture = ref[4]; - var condition = ref[5]; - var off = on(element, type, selector, function(e) { - var result = !condition || condition(e); - if (result) { - off(); - listener(e, result); - } - }, useCapture); - return off; - } - function trigger(target, event, detail) { - return toEventTargets(target).reduce(function(notCanceled, target) { - return notCanceled && target.dispatchEvent(createEvent(event, true, true, detail)); - }, true); - } - function createEvent(e, bubbles, cancelable, detail) { - if (bubbles === void 0) bubbles = true; - if (cancelable === void 0) cancelable = false; - if (isString(e)) { - var event = document.createEvent("CustomEvent"); - event.initCustomEvent(e, bubbles, cancelable, detail); - e = event; - } - return e; - } - function getArgs(args) { - if (isString(args[0])) { - args[0] = find(args[0]); - } - if (isFunction(args[2])) { - args.splice(2, 0, false); - } - return args; - } - function delegate(element, selector, listener) { - var this$1 = this; - return function(e) { - var target = e.target; - var current = selector[0] === ">" ? findAll(selector, element).reverse().filter(function(element) { - return within(target, element); - })[0] : closest(target, selector); - if (current) { - e.delegate = element; - e.current = current; - listener.call(this$1, e); - } - }; - } - function detail(listener) { - return function(e) { - return isArray(e.detail) ? listener.apply(listener, [ e ].concat(e.detail)) : listener(e); - }; - } - function isEventTarget(target) { - return "EventTarget" in window ? target instanceof EventTarget : target && "addEventListener" in target; - } - function toEventTarget(target) { - return isEventTarget(target) ? target : toNode(target); - } - function toEventTargets(target) { - return isEventTarget(target) ? [ target ] : isArray(target) ? target.map(toEventTarget).filter(Boolean) : toNodes(target); - } - function preventClick() { - var timer = setTimeout(once(document, "click", function(e) { - e.preventDefault(); - e.stopImmediatePropagation(); - clearTimeout(timer); - }, true)); - trigger(document, "touchcancel"); - } - var Promise = "Promise" in window ? window.Promise : PromiseFn; - var Deferred = function Deferred() { - var this$1 = this; - this.promise = new Promise(function(resolve, reject) { - this$1.reject = reject; - this$1.resolve = resolve; - }); - }; - var RESOLVED = 0; - var REJECTED = 1; - var PENDING = 2; - var async = "setImmediate" in window ? setImmediate : setTimeout; - function PromiseFn(executor) { - this.state = PENDING; - this.value = undefined; - this.deferred = []; - var promise = this; - try { - executor(function(x) { - promise.resolve(x); - }, function(r) { - promise.reject(r); - }); - } catch (e) { - promise.reject(e); - } - } - PromiseFn.reject = function(r) { - return new PromiseFn(function(resolve, reject) { - reject(r); - }); - }; - PromiseFn.resolve = function(x) { - return new PromiseFn(function(resolve, reject) { - resolve(x); - }); - }; - PromiseFn.all = function all(iterable) { - return new PromiseFn(function(resolve, reject) { - var result = []; - var count = 0; - if (iterable.length === 0) { - resolve(result); - } - function resolver(i) { - return function(x) { - result[i] = x; - count += 1; - if (count === iterable.length) { - resolve(result); - } - }; - } - for (var i = 0; i < iterable.length; i += 1) { - PromiseFn.resolve(iterable[i]).then(resolver(i), reject); - } - }); - }; - PromiseFn.race = function race(iterable) { - return new PromiseFn(function(resolve, reject) { - for (var i = 0; i < iterable.length; i += 1) { - PromiseFn.resolve(iterable[i]).then(resolve, reject); - } - }); - }; - var p = PromiseFn.prototype; - p.resolve = function resolve(x) { - var promise = this; - if (promise.state === PENDING) { - if (x === promise) { - throw new TypeError("Promise settled with itself."); - } - var called = false; - try { - var then = x && x.then; - if (x !== null && isObject(x) && isFunction(then)) { - then.call(x, function(x) { - if (!called) { - promise.resolve(x); - } - called = true; - }, function(r) { - if (!called) { - promise.reject(r); - } - called = true; - }); - return; - } - } catch (e) { - if (!called) { - promise.reject(e); - } - return; - } - promise.state = RESOLVED; - promise.value = x; - promise.notify(); - } - }; - p.reject = function reject(reason) { - var promise = this; - if (promise.state === PENDING) { - if (reason === promise) { - throw new TypeError("Promise settled with itself."); - } - promise.state = REJECTED; - promise.value = reason; - promise.notify(); - } - }; - p.notify = function notify() { - var this$1 = this; - async(function() { - if (this$1.state !== PENDING) { - while (this$1.deferred.length) { - var ref = this$1.deferred.shift(); - var onResolved = ref[0]; - var onRejected = ref[1]; - var resolve = ref[2]; - var reject = ref[3]; - try { - if (this$1.state === RESOLVED) { - if (isFunction(onResolved)) { - resolve(onResolved.call(undefined, this$1.value)); - } else { - resolve(this$1.value); - } - } else if (this$1.state === REJECTED) { - if (isFunction(onRejected)) { - resolve(onRejected.call(undefined, this$1.value)); - } else { - reject(this$1.value); - } - } - } catch (e) { - reject(e); - } - } - } - }); - }; - p.then = function then(onResolved, onRejected) { - var this$1 = this; - return new PromiseFn(function(resolve, reject) { - this$1.deferred.push([ onResolved, onRejected, resolve, reject ]); - this$1.notify(); - }); - }; - p.catch = function(onRejected) { - return this.then(undefined, onRejected); - }; - function ajax(url, options) { - return new Promise(function(resolve, reject) { - var env = assign({ - data: null, - method: "GET", - headers: {}, - xhr: new XMLHttpRequest(), - beforeSend: noop, - responseType: "" - }, options); - env.beforeSend(env); - var xhr = env.xhr; - for (var prop in env) { - if (prop in xhr) { - try { - xhr[prop] = env[prop]; - } catch (e) {} - } - } - xhr.open(env.method.toUpperCase(), url); - for (var header in env.headers) { - xhr.setRequestHeader(header, env.headers[header]); - } - on(xhr, "load", function() { - if (xhr.status === 0 || xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) { - resolve(xhr); - } else { - reject(assign(Error(xhr.statusText), { - xhr: xhr, - status: xhr.status - })); - } - }); - on(xhr, "error", function() { - return reject(assign(Error("Network Error"), { - xhr: xhr - })); - }); - on(xhr, "timeout", function() { - return reject(assign(Error("Network Timeout"), { - xhr: xhr - })); - }); - xhr.send(env.data); - }); - } - function getImage(src) { - return new Promise(function(resolve, reject) { - var img = new Image(); - img.onerror = reject; - img.onload = function() { - return resolve(img); - }; - img.src = src; - }); - } - function isReady() { - return document.readyState === "complete" || document.readyState !== "loading" && !document.documentElement.doScroll; - } - function ready(fn) { - if (isReady()) { - fn(); - return; - } - var handle = function() { - unbind1(); - unbind2(); - fn(); - }; - var unbind1 = on(document, "DOMContentLoaded", handle); - var unbind2 = on(window, "load", handle); - } - function index(element, ref) { - return ref ? toNodes(element).indexOf(toNode(ref)) : toNodes((element = toNode(element)) && element.parentNode.children).indexOf(element); - } - function getIndex(i, elements, current, finite) { - if (current === void 0) current = 0; - if (finite === void 0) finite = false; - elements = toNodes(elements); - var length = elements.length; - i = isNumeric(i) ? toNumber(i) : i === "next" ? current + 1 : i === "previous" ? current - 1 : index(elements, i); - if (finite) { - return clamp(i, 0, length - 1); - } - i %= length; - return i < 0 ? i + length : i; - } - function empty(element) { - element = toNode(element); - element.innerHTML = ""; - return element; - } - function html(parent, html) { - parent = toNode(parent); - return isUndefined(html) ? parent.innerHTML : append(parent.hasChildNodes() ? empty(parent) : parent, html); - } - function prepend(parent, element) { - parent = toNode(parent); - if (!parent.hasChildNodes()) { - return append(parent, element); - } else { - return insertNodes(element, function(element) { - return parent.insertBefore(element, parent.firstChild); - }); - } - } - function append(parent, element) { - parent = toNode(parent); - return insertNodes(element, function(element) { - return parent.appendChild(element); - }); - } - function before(ref, element) { - ref = toNode(ref); - return insertNodes(element, function(element) { - return ref.parentNode.insertBefore(element, ref); - }); - } - function after(ref, element) { - ref = toNode(ref); - return insertNodes(element, function(element) { - return ref.nextSibling ? before(ref.nextSibling, element) : append(ref.parentNode, element); - }); - } - function insertNodes(element, fn) { - element = isString(element) ? fragment(element) : element; - return element ? "length" in element ? toNodes(element).map(fn) : fn(element) : null; - } - function remove(element) { - toNodes(element).map(function(element) { - return element.parentNode && element.parentNode.removeChild(element); - }); - } - function wrapAll(element, structure) { - structure = toNode(before(element, structure)); - while (structure.firstChild) { - structure = structure.firstChild; - } - append(structure, element); - return structure; - } - function wrapInner(element, structure) { - return toNodes(toNodes(element).map(function(element) { - return element.hasChildNodes ? wrapAll(toNodes(element.childNodes), structure) : append(element, structure); - })); - } - function unwrap(element) { - toNodes(element).map(function(element) { - return element.parentNode; - }).filter(function(value, index, self) { - return self.indexOf(value) === index; - }).forEach(function(parent) { - before(parent, parent.childNodes); - remove(parent); - }); - } - var fragmentRE = /^\s*<(\w+|!)[^>]*>/; - var singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>)?$/; - function fragment(html) { - var matches = singleTagRE.exec(html); - if (matches) { - return document.createElement(matches[1]); - } - var container = document.createElement("div"); - if (fragmentRE.test(html)) { - container.insertAdjacentHTML("beforeend", html.trim()); - } else { - container.textContent = html; - } - return container.childNodes.length > 1 ? toNodes(container.childNodes) : container.firstChild; - } - function apply(node, fn) { - if (!node || node.nodeType !== 1) { - return; - } - fn(node); - node = node.firstElementChild; - while (node) { - apply(node, fn); - node = node.nextElementSibling; - } - } - function addClass(element) { - var args = [], len = arguments.length - 1; - while (len-- > 0) args[len] = arguments[len + 1]; - apply$1(element, args, "add"); - } - function removeClass(element) { - var args = [], len = arguments.length - 1; - while (len-- > 0) args[len] = arguments[len + 1]; - apply$1(element, args, "remove"); - } - function removeClasses(element, cls) { - filterAttr(element, "class", new RegExp("(^|\\s)" + cls + "(?!\\S)", "g"), ""); - } - function replaceClass(element) { - var args = [], len = arguments.length - 1; - while (len-- > 0) args[len] = arguments[len + 1]; - args[0] && removeClass(element, args[0]); - args[1] && addClass(element, args[1]); - } - function hasClass(element, cls) { - return toNodes(element).some(function(element) { - return element.classList.contains(cls); - }); - } - function toggleClass(element) { - var args = [], len = arguments.length - 1; - while (len-- > 0) args[len] = arguments[len + 1]; - if (!args.length) { - return; - } - args = getArgs$1(args); - var force = !isString(args[args.length - 1]) ? args.pop() : []; - args = args.filter(Boolean); - toNodes(element).forEach(function(ref) { - var classList = ref.classList; - for (var i = 0; i < args.length; i++) { - supports.Force ? classList.toggle.apply(classList, [ args[i] ].concat(force)) : classList[(!isUndefined(force) ? force : !classList.contains(args[i])) ? "add" : "remove"](args[i]); - } - }); - } - function apply$1(element, args, fn) { - args = getArgs$1(args).filter(Boolean); - args.length && toNodes(element).forEach(function(ref) { - var classList = ref.classList; - supports.Multiple ? classList[fn].apply(classList, args) : args.forEach(function(cls) { - return classList[fn](cls); - }); - }); - } - function getArgs$1(args) { - return args.reduce(function(args, arg) { - return args.concat.call(args, isString(arg) && includes(arg, " ") ? arg.trim().split(" ") : arg); - }, []); - } - var supports = {}; - (function() { - var list = document.createElement("_").classList; - if (list) { - list.add("a", "b"); - list.toggle("c", false); - supports.Multiple = list.contains("b"); - supports.Force = !list.contains("c"); - } - list = null; - })(); - var cssNumber = { - "animation-iteration-count": true, - "column-count": true, - "fill-opacity": true, - "flex-grow": true, - "flex-shrink": true, - "font-weight": true, - "line-height": true, - opacity: true, - order: true, - orphans: true, - widows: true, - "z-index": true, - zoom: true - }; - function css(element, property, value) { - return toNodes(element).map(function(element) { - if (isString(property)) { - property = propName(property); - if (isUndefined(value)) { - return getStyle(element, property); - } else if (!value && value !== 0) { - element.style.removeProperty(property); - } else { - element.style[property] = isNumeric(value) && !cssNumber[property] ? value + "px" : value; - } - } else if (isArray(property)) { - var styles = getStyles(element); - return property.reduce(function(props, property) { - props[property] = styles[propName(property)]; - return props; - }, {}); - } else if (isObject(property)) { - each(property, function(value, property) { - return css(element, property, value); - }); - } - return element; - })[0]; - } - function getStyles(element, pseudoElt) { - element = toNode(element); - return element.ownerDocument.defaultView.getComputedStyle(element, pseudoElt); - } - function getStyle(element, property, pseudoElt) { - return getStyles(element, pseudoElt)[property]; - } - var vars = {}; - function getCssVar(name) { - if (!(name in vars)) { - var element = append(document.documentElement, document.createElement("div")); - addClass(element, "var-" + name); - try { - vars[name] = getStyle(element, "content", ":before").replace(/^["'](.*)["']$/, "$1"); - vars[name] = JSON.parse(vars[name]); - } catch (e) {} - document.documentElement.removeChild(element); - } - return vars[name]; - } - var cssProps = {}; - function propName(name) { - var ret = cssProps[name]; - if (!ret) { - ret = cssProps[name] = vendorPropName(name) || name; - } - return ret; - } - var cssPrefixes = [ "webkit", "moz", "ms" ]; - var ref$1 = document.createElement("_"); - var style = ref$1.style; - function vendorPropName(name) { - name = hyphenate(name); - if (name in style) { - return name; - } - var i = cssPrefixes.length, prefixedName; - while (i--) { - prefixedName = "-" + cssPrefixes[i] + "-" + name; - if (prefixedName in style) { - return prefixedName; - } - } - } - function transition(element, props, duration, timing) { - if (duration === void 0) duration = 400; - if (timing === void 0) timing = "linear"; - return Promise.all(toNodes(element).map(function(element) { - return new Promise(function(resolve, reject) { - for (var name in props) { - var value = css(element, name); - if (value === "") { - css(element, name, value); - } - } - var timer = setTimeout(function() { - return trigger(element, "transitionend"); - }, duration); - once(element, "transitionend transitioncanceled", function(ref) { - var type = ref.type; - clearTimeout(timer); - removeClass(element, "uk-transition"); - css(element, { - "transition-property": "", - "transition-duration": "", - "transition-timing-function": "" - }); - type === "transitioncanceled" ? reject() : resolve(); - }, false, function(ref) { - var target = ref.target; - return element === target; - }); - addClass(element, "uk-transition"); - css(element, assign({ - "transition-property": Object.keys(props).map(propName).join(","), - "transition-duration": duration + "ms", - "transition-timing-function": timing - }, props)); - }); - })); - } - var Transition = { - start: transition, - stop: function stop(element) { - trigger(element, "transitionend"); - return Promise.resolve(); - }, - cancel: function cancel(element) { - trigger(element, "transitioncanceled"); - }, - inProgress: function inProgress(element) { - return hasClass(element, "uk-transition"); - } - }; - var animationPrefix = "uk-animation-"; - var clsCancelAnimation = "uk-cancel-animation"; - function animate(element, animation, duration, origin, out) { - var arguments$1 = arguments; - if (duration === void 0) duration = 200; - return Promise.all(toNodes(element).map(function(element) { - return new Promise(function(resolve, reject) { - if (hasClass(element, clsCancelAnimation)) { - requestAnimationFrame(function() { - return Promise.resolve().then(function() { - return animate.apply(void 0, arguments$1).then(resolve, reject); - }); - }); - return; - } - var cls = animation + " " + animationPrefix + (out ? "leave" : "enter"); - if (startsWith(animation, animationPrefix)) { - if (origin) { - cls += " uk-transform-origin-" + origin; - } - if (out) { - cls += " " + animationPrefix + "reverse"; - } - } - reset(); - once(element, "animationend animationcancel", function(ref) { - var type = ref.type; - var hasReset = false; - if (type === "animationcancel") { - reject(); - reset(); - } else { - resolve(); - Promise.resolve().then(function() { - hasReset = true; - reset(); - }); - } - requestAnimationFrame(function() { - if (!hasReset) { - addClass(element, clsCancelAnimation); - requestAnimationFrame(function() { - return removeClass(element, clsCancelAnimation); - }); - } - }); - }, false, function(ref) { - var target = ref.target; - return element === target; - }); - css(element, "animationDuration", duration + "ms"); - addClass(element, cls); - function reset() { - css(element, "animationDuration", ""); - removeClasses(element, animationPrefix + "\\S*"); - } - }); - })); - } - var inProgress = new RegExp(animationPrefix + "(enter|leave)"); - var Animation = { - in: function in$1(element, animation, duration, origin) { - return animate(element, animation, duration, origin, false); - }, - out: function out(element, animation, duration, origin) { - return animate(element, animation, duration, origin, true); - }, - inProgress: function inProgress$1(element) { - return inProgress.test(attr(element, "class")); - }, - cancel: function cancel(element) { - trigger(element, "animationcancel"); - } - }; - function $(selector, context) { - return !isString(selector) ? toNode(selector) : isHtml(selector) ? toNode(fragment(selector)) : find(selector, context); - } - function $$(selector, context) { - return !isString(selector) ? toNodes(selector) : isHtml(selector) ? toNodes(fragment(selector)) : findAll(selector, context); - } - function isHtml(str) { - return str[0] === "<" || str.match(/^\s* boundary[alignFlip]) { - var centerOffset = dim[prop] / 2; - var centerTargetOffset = targetAttach[dir] === "center" ? -targetDim[prop] / 2 : 0; - elAttach[dir] === "center" && (apply(centerOffset, centerTargetOffset) || apply(-centerOffset, -centerTargetOffset)) || apply(elemOffset, targetOffset); - } - function apply(elemOffset, targetOffset) { - var newVal = position[align] + elemOffset + targetOffset - elOffset[dir] * 2; - if (newVal >= boundary[align] && newVal + dim[prop] <= boundary[alignFlip]) { - position[align] = newVal; - [ "element", "target" ].forEach(function(el) { - flipped[el][dir] = !elemOffset ? flipped[el][dir] : flipped[el][dir] === dirs[prop][1] ? dirs[prop][2] : dirs[prop][1]; - }); - return true; - } - } - }); - } - offset(element, position); - return flipped; - } - function offset(element, coordinates) { - element = toNode(element); - if (coordinates) { - var currentOffset = offset(element); - var pos = css(element, "position"); - [ "left", "top" ].forEach(function(prop) { - if (prop in coordinates) { - var value = css(element, prop); - element.style[prop] = coordinates[prop] - currentOffset[prop] + toFloat(pos === "absolute" && value === "auto" ? position(element)[prop] : value) + "px"; - } - }); - return; - } - return getDimensions(element); - } - function getDimensions(element) { - element = toNode(element); - var ref = window$1(element); - var top = ref.pageYOffset; - var left = ref.pageXOffset; - if (isWindow(element)) { - var height = element.innerHeight; - var width = element.innerWidth; - return { - top: top, - left: left, - height: height, - width: width, - bottom: top + height, - right: left + width - }; - } - var display = false; - if (!isVisible(element)) { - display = element.style.display; - element.style.display = "block"; - } - var rect = element.getBoundingClientRect(); - if (display !== false) { - element.style.display = display; - } - return { - height: rect.height, - width: rect.width, - top: rect.top + top, - left: rect.left + left, - bottom: rect.bottom + top, - right: rect.right + left - }; - } - function position(element) { - element = toNode(element); - var parent = offsetParent(element); - var parentOffset = parent === docEl(element) ? { - top: 0, - left: 0 - } : offset(parent); - var ref = [ "top", "left" ].reduce(function(props, prop) { - var propName$$1 = ucfirst(prop); - props[prop] -= parentOffset[prop] + (toFloat(css(element, "margin" + propName$$1)) || 0) + (toFloat(css(parent, "border" + propName$$1 + "Width")) || 0); - return props; - }, offset(element)); - var top = ref.top; - var left = ref.left; - return { - top: top, - left: left - }; - } - function offsetParent(element) { - var parent = toNode(element).offsetParent; - while (parent && css(parent, "position") === "static") { - parent = parent.offsetParent; - } - return parent || docEl(element); - } - var height = dimension("height"); - var width = dimension("width"); - function dimension(prop) { - var propName$$1 = ucfirst(prop); - return function(element, value) { - element = toNode(element); - if (isUndefined(value)) { - if (isWindow(element)) { - return element["inner" + propName$$1]; - } - if (isDocument(element)) { - var doc = element.documentElement; - return Math.max(doc["offset" + propName$$1], doc["scroll" + propName$$1]); - } - value = css(element, prop); - value = value === "auto" ? element["offset" + propName$$1] : toFloat(value) || 0; - return value - boxModelAdjust(prop, element); - } else { - css(element, prop, !value && value !== 0 ? "" : +value + boxModelAdjust(prop, element) + "px"); - } - }; - } - function boxModelAdjust(prop, element) { - return css(element, "boxSizing") === "border-box" ? dirs[prop].slice(1).map(ucfirst).reduce(function(value, prop) { - return value + toFloat(css(element, "padding" + prop)) + toFloat(css(element, "border" + prop + "Width")); - }, 0) : 0; - } - function moveTo(position, attach, dim, factor) { - each(dirs, function(ref, prop) { - var dir = ref[0]; - var align = ref[1]; - var alignFlip = ref[2]; - if (attach[dir] === alignFlip) { - position[align] += dim[prop] * factor; - } else if (attach[dir] === "center") { - position[align] += dim[prop] * factor / 2; - } - }); - } - function getPos(pos) { - var x = /left|center|right/; - var y = /top|center|bottom/; - pos = (pos || "").split(" "); - if (pos.length === 1) { - pos = x.test(pos[0]) ? pos.concat([ "center" ]) : y.test(pos[0]) ? [ "center" ].concat(pos) : [ "center", "center" ]; - } - return { - x: x.test(pos[0]) ? pos[0] : "center", - y: y.test(pos[1]) ? pos[1] : "center" - }; - } - function getOffsets(offsets, width, height) { - var ref = (offsets || "").split(" "); - var x = ref[0]; - var y = ref[1]; - return { - x: x ? toFloat(x) * (endsWith(x, "%") ? width / 100 : 1) : 0, - y: y ? toFloat(y) * (endsWith(y, "%") ? height / 100 : 1) : 0 - }; - } - function flipPosition(pos) { - switch (pos) { - case "left": - return "right"; - - case "right": - return "left"; - - case "top": - return "bottom"; - - case "bottom": - return "top"; - - default: - return pos; - } - } - function isInView(element, top, left) { - if (top === void 0) top = 0; - if (left === void 0) left = 0; - element = toNode(element); - var win = window$1(element); - return isVisible(element) && intersectRect(element.getBoundingClientRect(), { - top: top, - left: left, - bottom: top + height(win), - right: left + width(win) - }); - } - function scrolledOver(element) { - if (!isVisible(element)) { - return 0; - } - element = toNode(element); - var win = window$1(element); - var doc = document$1(element); - var elHeight = element.offsetHeight; - var top = positionTop(element); - var vp = height(win); - var vh = vp + Math.min(0, top - vp); - var diff = Math.max(0, vp - (height(doc) - (top + elHeight))); - return clamp((vh + win.pageYOffset - top) / ((vh + (elHeight - (diff < vp ? diff : 0))) / 100) / 100); - } - function positionTop(element) { - var top = 0; - do { - top += element.offsetTop; - } while (element = element.offsetParent); - return top; - } - function window$1(element) { - return isWindow(element) ? element : document$1(element).defaultView; - } - function document$1(element) { - return toNode(element).ownerDocument; - } - function docEl(element) { - return document$1(element).documentElement; - } - var isRtl = attr(document.documentElement, "dir") === "rtl"; - var hasTouchEvents = "ontouchstart" in window; - var hasPointerEvents = window.PointerEvent; - var hasTouch = hasTouchEvents || window.DocumentTouch && document instanceof DocumentTouch || navigator.maxTouchPoints; - var pointerDown = !hasTouch ? "mousedown" : "mousedown " + (hasTouchEvents ? "touchstart" : "pointerdown"); - var pointerMove = !hasTouch ? "mousemove" : "mousemove " + (hasTouchEvents ? "touchmove" : "pointermove"); - var pointerUp = !hasTouch ? "mouseup" : "mouseup " + (hasTouchEvents ? "touchend" : "pointerup"); - var pointerEnter = hasTouch && hasPointerEvents ? "pointerenter" : "mouseenter"; - var pointerLeave = hasTouch && hasPointerEvents ? "pointerleave" : "mouseleave"; - var fastdom = { - reads: [], - writes: [], - read: function read(task) { - this.reads.push(task); - scheduleFlush(); - return task; - }, - write: function write(task) { - this.writes.push(task); - scheduleFlush(); - return task; - }, - clear: function clear(task) { - return remove$1(this.reads, task) || remove$1(this.writes, task); - }, - flush: function flush() { - runTasks(this.reads); - runTasks(this.writes.splice(0, this.writes.length)); - this.scheduled = false; - if (this.reads.length || this.writes.length) { - scheduleFlush(); - } - } - }; - function scheduleFlush() { - if (!fastdom.scheduled) { - fastdom.scheduled = true; - requestAnimationFrame(fastdom.flush.bind(fastdom)); - } - } - function runTasks(tasks) { - var task; - while (task = tasks.shift()) { - task(); - } - } - function remove$1(array, item) { - var index = array.indexOf(item); - return !!~index && !!array.splice(index, 1); - } - function MouseTracker() {} - MouseTracker.prototype = { - positions: [], - position: null, - init: function init() { - var this$1 = this; - this.positions = []; - this.position = null; - var ticking = false; - this.unbind = on(document, "mousemove", function(e) { - if (ticking) { - return; - } - setTimeout(function() { - var time = Date.now(); - var ref = this$1.positions; - var length = ref.length; - if (length && time - this$1.positions[length - 1].time > 100) { - this$1.positions.splice(0, length); - } - this$1.positions.push({ - time: time, - x: e.pageX, - y: e.pageY - }); - if (this$1.positions.length > 5) { - this$1.positions.shift(); - } - ticking = false; - }, 5); - ticking = true; - }); - }, - cancel: function cancel() { - if (this.unbind) { - this.unbind(); - } - }, - movesTo: function movesTo(target) { - if (this.positions.length < 2) { - return false; - } - var p = offset(target); - var position$$1 = this.positions[this.positions.length - 1]; - var ref = this.positions; - var prevPos = ref[0]; - if (p.left <= position$$1.x && position$$1.x <= p.right && p.top <= position$$1.y && position$$1.y <= p.bottom) { - return false; - } - var points = [ [ { - x: p.left, - y: p.top - }, { - x: p.right, - y: p.bottom - } ], [ { - x: p.right, - y: p.top - }, { - x: p.left, - y: p.bottom - } ] ]; - if (p.right <= position$$1.x) {} else if (p.left >= position$$1.x) { - points[0].reverse(); - points[1].reverse(); - } else if (p.bottom <= position$$1.y) { - points[0].reverse(); - } else if (p.top >= position$$1.y) { - points[1].reverse(); - } - return !!points.reduce(function(result, point) { - return result + (slope(prevPos, point[0]) < slope(position$$1, point[0]) && slope(prevPos, point[1]) > slope(position$$1, point[1])); - }, 0); - } - }; - function slope(a, b) { - return (b.y - a.y) / (b.x - a.x); - } - var strats = {}; - strats.args = strats.events = strats.init = strats.created = strats.beforeConnect = strats.connected = strats.ready = strats.beforeDisconnect = strats.disconnected = strats.destroy = function(parentVal, childVal) { - parentVal = parentVal && !isArray(parentVal) ? [ parentVal ] : parentVal; - return childVal ? parentVal ? parentVal.concat(childVal) : isArray(childVal) ? childVal : [ childVal ] : parentVal; - }; - strats.update = function(parentVal, childVal) { - return strats.args(parentVal, isFunction(childVal) ? { - read: childVal - } : childVal); - }; - strats.props = function(parentVal, childVal) { - if (isArray(childVal)) { - childVal = childVal.reduce(function(value, key) { - value[key] = String; - return value; - }, {}); - } - return strats.methods(parentVal, childVal); - }; - strats.computed = strats.defaults = strats.methods = function(parentVal, childVal) { - return childVal ? parentVal ? assign({}, parentVal, childVal) : childVal : parentVal; - }; - var defaultStrat = function(parentVal, childVal) { - return isUndefined(childVal) ? parentVal : childVal; - }; - function mergeOptions(parent, child) { - var options = {}; - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i]); - } - } - for (var key in parent) { - mergeKey(key); - } - for (var key$1 in child) { - if (!hasOwn(parent, key$1)) { - mergeKey(key$1); - } - } - function mergeKey(key) { - options[key] = (strats[key] || defaultStrat)(parent[key], child[key]); - } - return options; - } - var id = 0; - var Player = function Player(el) { - this.id = ++id; - this.el = toNode(el); - }; - Player.prototype.isVideo = function isVideo() { - return this.isYoutube() || this.isVimeo() || this.isHTML5(); - }; - Player.prototype.isHTML5 = function isHTML5() { - return this.el.tagName === "VIDEO"; - }; - Player.prototype.isIFrame = function isIFrame() { - return this.el.tagName === "IFRAME"; - }; - Player.prototype.isYoutube = function isYoutube() { - return this.isIFrame() && !!this.el.src.match(/\/\/.*?youtube(-nocookie)?\.[a-z]+\/(watch\?v=[^&\s]+|embed)|youtu\.be\/.*/); - }; - Player.prototype.isVimeo = function isVimeo() { - return this.isIFrame() && !!this.el.src.match(/vimeo\.com\/video\/.*/); - }; - Player.prototype.enableApi = function enableApi() { - var this$1 = this; - if (this.ready) { - return this.ready; - } - var youtube = this.isYoutube(); - var vimeo = this.isVimeo(); - var poller; - if (youtube || vimeo) { - return this.ready = new Promise(function(resolve) { - once(this$1.el, "load", function() { - if (youtube) { - var listener = function() { - return post(this$1.el, { - event: "listening", - id: this$1.id - }); - }; - poller = setInterval(listener, 100); - listener(); - } - }); - listen(function(data$$1) { - return youtube && data$$1.id === this$1.id && data$$1.event === "onReady" || vimeo && Number(data$$1.player_id) === this$1.id; - }).then(function() { - resolve(); - poller && clearInterval(poller); - }); - attr(this$1.el, "src", "" + this$1.el.src + (includes(this$1.el.src, "?") ? "&" : "?") + (youtube ? "enablejsapi=1" : "api=1&player_id=" + this$1.id)); - }); - } - return Promise.resolve(); - }; - Player.prototype.play = function play() { - var this$1 = this; - if (!this.isVideo()) { - return; - } - if (this.isIFrame()) { - this.enableApi().then(function() { - return post(this$1.el, { - func: "playVideo", - method: "play" - }); - }); - } else if (this.isHTML5()) { - try { - var promise = this.el.play(); - if (promise) { - promise.catch(noop); - } - } catch (e) {} - } - }; - Player.prototype.pause = function pause() { - var this$1 = this; - if (!this.isVideo()) { - return; - } - if (this.isIFrame()) { - this.enableApi().then(function() { - return post(this$1.el, { - func: "pauseVideo", - method: "pause" - }); - }); - } else if (this.isHTML5()) { - this.el.pause(); - } - }; - Player.prototype.mute = function mute() { - var this$1 = this; - if (!this.isVideo()) { - return; - } - if (this.isIFrame()) { - this.enableApi().then(function() { - return post(this$1.el, { - func: "mute", - method: "setVolume", - value: 0 - }); - }); - } else if (this.isHTML5()) { - this.el.muted = true; - attr(this.el, "muted", ""); - } - }; - function post(el, cmd) { - try { - el.contentWindow.postMessage(JSON.stringify(assign({ - event: "command" - }, cmd)), "*"); - } catch (e) {} - } - function listen(cb) { - return new Promise(function(resolve) { - once(window, "message", function(_, data$$1) { - return resolve(data$$1); - }, false, function(ref) { - var data$$1 = ref.data; - if (!data$$1 || !isString(data$$1)) { - return; - } - try { - data$$1 = JSON.parse(data$$1); - } catch (e) { - return; - } - return data$$1 && cb(data$$1); - }); - }); - } - var touch = {}; - var clickTimeout; - var swipeTimeout; - var tapTimeout; - var clicked; - function swipeDirection(ref) { - var x1 = ref.x1; - var x2 = ref.x2; - var y1 = ref.y1; - var y2 = ref.y2; - return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? x1 - x2 > 0 ? "Left" : "Right" : y1 - y2 > 0 ? "Up" : "Down"; - } - function cancelAll() { - clickTimeout && clearTimeout(clickTimeout); - swipeTimeout && clearTimeout(swipeTimeout); - tapTimeout && clearTimeout(tapTimeout); - clickTimeout = swipeTimeout = tapTimeout = null; - touch = {}; - } - ready(function() { - on(document, "click", function() { - return clicked = true; - }, true); - on(document, pointerDown, function(e) { - var target = e.target; - var ref = getPos$1(e); - var x = ref.x; - var y = ref.y; - var now = Date.now(); - var type = getType(e.type); - if (touch.type && touch.type !== type) { - return; - } - touch.el = "tagName" in target ? target : target.parentNode; - clickTimeout && clearTimeout(clickTimeout); - touch.x1 = x; - touch.y1 = y; - if (touch.last && now - touch.last <= 250) { - touch = {}; - } - touch.type = type; - touch.last = now; - clicked = e.button > 0; - }); - on(document, pointerMove, function(e) { - if (e.defaultPrevented) { - return; - } - var ref = getPos$1(e); - var x = ref.x; - var y = ref.y; - touch.x2 = x; - touch.y2 = y; - }); - on(document, pointerUp, function(ref) { - var type = ref.type; - var target = ref.target; - if (touch.type !== getType(type)) { - return; - } - if (touch.x2 && Math.abs(touch.x1 - touch.x2) > 30 || touch.y2 && Math.abs(touch.y1 - touch.y2) > 30) { - swipeTimeout = setTimeout(function() { - if (touch.el) { - trigger(touch.el, "swipe"); - trigger(touch.el, "swipe" + swipeDirection(touch)); - } - touch = {}; - }); - } else if ("last" in touch) { - tapTimeout = setTimeout(function() { - return trigger(touch.el, "tap"); - }); - if (touch.el && type !== "mouseup" && within(target, touch.el)) { - clickTimeout = setTimeout(function() { - clickTimeout = null; - if (touch.el && !clicked) { - trigger(touch.el, "click"); - } - touch = {}; - }, 350); - } - } else { - touch = {}; - } - }); - on(document, "touchcancel", cancelAll); - on(window, "scroll", cancelAll); - }); - var touching = false; - on(document, "touchstart", function() { - return touching = true; - }, true); - on(document, "click", function() { - touching = false; - }); - on(document, "touchcancel", function() { - return touching = false; - }, true); - function isTouch(e) { - return touching || e.pointerType === "touch"; - } - function getPos$1(e) { - var touches = e.touches; - var changedTouches = e.changedTouches; - var ref = touches && touches[0] || changedTouches && changedTouches[0] || e; - var x = ref.pageX; - var y = ref.pageY; - return { - x: x, - y: y - }; - } - function getType(type) { - return type.slice(0, 5); - } - var util = Object.freeze({ - ajax: ajax, - getImage: getImage, - transition: transition, - Transition: Transition, - animate: animate, - Animation: Animation, - attr: attr, - hasAttr: hasAttr, - removeAttr: removeAttr, - filterAttr: filterAttr, - data: data, - addClass: addClass, - removeClass: removeClass, - removeClasses: removeClasses, - replaceClass: replaceClass, - hasClass: hasClass, - toggleClass: toggleClass, - $: $, - $$: $$, - positionAt: positionAt, - offset: offset, - position: position, - height: height, - width: width, - flipPosition: flipPosition, - isInView: isInView, - scrolledOver: scrolledOver, - isReady: isReady, - ready: ready, - index: index, - getIndex: getIndex, - empty: empty, - html: html, - prepend: prepend, - append: append, - before: before, - after: after, - remove: remove, - wrapAll: wrapAll, - wrapInner: wrapInner, - unwrap: unwrap, - fragment: fragment, - apply: apply, - isRtl: isRtl, - hasTouch: hasTouch, - pointerDown: pointerDown, - pointerMove: pointerMove, - pointerUp: pointerUp, - pointerEnter: pointerEnter, - pointerLeave: pointerLeave, - on: on, - off: off, - once: once, - trigger: trigger, - createEvent: createEvent, - toEventTargets: toEventTargets, - preventClick: preventClick, - fastdom: fastdom, - isVoidElement: isVoidElement, - isVisible: isVisible, - selInput: selInput, - isInput: isInput, - filter: filter, - within: within, - bind: bind, - hasOwn: hasOwn, - hyphenate: hyphenate, - camelize: camelize, - ucfirst: ucfirst, - startsWith: startsWith, - endsWith: endsWith, - includes: includes, - isArray: isArray, - isFunction: isFunction, - isObject: isObject, - isPlainObject: isPlainObject, - isWindow: isWindow, - isDocument: isDocument, - isJQuery: isJQuery, - isNode: isNode, - isNodeCollection: isNodeCollection, - isBoolean: isBoolean, - isString: isString, - isNumber: isNumber, - isNumeric: isNumeric, - isUndefined: isUndefined, - toBoolean: toBoolean, - toNumber: toNumber, - toFloat: toFloat, - toNode: toNode, - toNodes: toNodes, - toList: toList, - toMs: toMs, - swap: swap, - assign: assign, - each: each, - sortBy: sortBy, - clamp: clamp, - noop: noop, - intersectRect: intersectRect, - pointInRect: pointInRect, - Dimensions: Dimensions, - MouseTracker: MouseTracker, - mergeOptions: mergeOptions, - Player: Player, - Promise: Promise, - Deferred: Deferred, - query: query, - queryAll: queryAll, - find: find, - findAll: findAll, - matches: matches, - closest: closest, - parents: parents, - escape: escape, - css: css, - getStyles: getStyles, - getStyle: getStyle, - getCssVar: getCssVar, - propName: propName, - isTouch: isTouch, - getPos: getPos$1 - }); - function componentAPI(UIkit) { - var DATA = UIkit.data; - UIkit.components = {}; - UIkit.component = function(id, options) { - var name = camelize(id); - if (isPlainObject(options)) { - options.name = name; - options = UIkit.extend(options); - } else if (isUndefined(options)) { - return UIkit.components[name]; - } else { - options.options.name = name; - } - UIkit.components[name] = options; - UIkit[name] = function(element, data) { - var i = arguments.length, argsArray = Array(i); - while (i--) argsArray[i] = arguments[i]; - if (isPlainObject(element)) { - return new UIkit.components[name]({ - data: element - }); - } - if (UIkit.components[name].options.functional) { - return new UIkit.components[name]({ - data: [].concat(argsArray) - }); - } - return element && element.nodeType ? init(element) : $$(element).map(init)[0]; - function init(element) { - var cmp = UIkit.getComponent(element, name); - if (cmp && data) { - cmp.$reset(data); - } - return cmp || new UIkit.components[name]({ - el: element, - data: data || {} - }); - } - }; - if (UIkit._initialized && !options.options.functional) { - fastdom.read(function() { - return UIkit[name]("[uk-" + id + "],[data-uk-" + id + "]"); - }); - } - return UIkit.components[name]; - }; - UIkit.getComponents = function(element) { - return element && element[DATA] || {}; - }; - UIkit.getComponent = function(element, name) { - return UIkit.getComponents(element)[name]; - }; - UIkit.connect = function(node) { - if (node[DATA]) { - for (var name in node[DATA]) { - node[DATA][name]._callConnected(); - } - } - for (var i = 0; i < node.attributes.length; i++) { - var name$1 = getComponentName(node.attributes[i].name); - if (name$1 && name$1 in UIkit.components) { - UIkit[name$1](node); - } - } - }; - UIkit.disconnect = function(node) { - for (var name in node[DATA]) { - node[DATA][name]._callDisconnected(); - } - }; - } - function getComponentName(attribute) { - return startsWith(attribute, "uk-") || startsWith(attribute, "data-uk-") ? camelize(attribute.replace("data-uk-", "").replace("uk-", "")) : false; - } - function boot(UIkit) { - var connect = UIkit.connect; - var disconnect = UIkit.disconnect; - if (!("MutationObserver" in window)) { - return; - } - if (document.body) { - init(); - } else { - new MutationObserver(function() { - if (document.body) { - this.disconnect(); - init(); - } - }).observe(document, { - childList: true, - subtree: true - }); - } - function init() { - apply(document.body, connect); - fastdom.flush(); - new MutationObserver(function(mutations) { - return mutations.forEach(applyMutation); - }).observe(document, { - childList: true, - subtree: true, - characterData: true, - attributes: true - }); - UIkit._initialized = true; - } - function applyMutation(mutation) { - var target = mutation.target; - var type = mutation.type; - var update = type !== "attributes" ? applyChildList(mutation) : applyAttribute(mutation); - update && UIkit.update(target); - } - function applyAttribute(ref) { - var target = ref.target; - var attributeName = ref.attributeName; - if (attributeName === "href") { - return true; - } - var name = getComponentName(attributeName); - if (!name || !(name in UIkit.components)) { - return; - } - if (hasAttr(target, attributeName)) { - UIkit[name](target); - return true; - } - var component = UIkit.getComponent(target, name); - if (component) { - component.$destroy(); - return true; - } - } - function applyChildList(ref) { - var addedNodes = ref.addedNodes; - var removedNodes = ref.removedNodes; - for (var i = 0; i < addedNodes.length; i++) { - apply(addedNodes[i], connect); - } - for (var i$1 = 0; i$1 < removedNodes.length; i$1++) { - apply(removedNodes[i$1], disconnect); - } - return true; - } - function apply(node, fn) { - if (node.nodeType !== 1 || hasAttr(node, "uk-no-boot")) { - return; - } - fn(node); - node = node.firstElementChild; - while (node) { - var next = node.nextElementSibling; - apply(node, fn); - node = next; - } - } - } - function globalAPI(UIkit) { - var DATA = UIkit.data; - UIkit.use = function(plugin) { - if (plugin.installed) { - return; - } - plugin.call(null, this); - plugin.installed = true; - return this; - }; - UIkit.mixin = function(mixin, component) { - component = (isString(component) ? UIkit.components[component] : component) || this; - mixin = mergeOptions({}, mixin); - mixin.mixins = component.options.mixins; - delete component.options.mixins; - component.options = mergeOptions(mixin, component.options); - }; - UIkit.extend = function(options) { - options = options || {}; - var Super = this; - var Sub = function UIkitComponent(options) { - this._init(options); - }; - Sub.prototype = Object.create(Super.prototype); - Sub.prototype.constructor = Sub; - Sub.options = mergeOptions(Super.options, options); - Sub["super"] = Super; - Sub.extend = Super.extend; - return Sub; - }; - UIkit.update = function(element, e) { - e = createEvent(e || "update"); - element = element ? toNode(element) : document.body; - apply(element, function(element) { - return update(element[DATA], e); - }); - while (element && element.parentNode) { - update(element.parentNode[DATA], e); - element = element.parentNode; - } - }; - var container; - Object.defineProperty(UIkit, "container", { - get: function get() { - return container || document.body; - }, - set: function set(element) { - container = $(element); - } - }); - function update(data, e) { - if (!data) { - return; - } - for (var name in data) { - if (data[name]._isReady) { - data[name]._callUpdate(e); - } - } - } - } - function hooksAPI(UIkit) { - UIkit.prototype._callHook = function(hook) { - var this$1 = this; - var handlers = this.$options[hook]; - if (handlers) { - handlers.forEach(function(handler) { - return handler.call(this$1); - }); - } - }; - UIkit.prototype._callConnected = function() { - var this$1 = this; - if (this._connected) { - return; - } - this._data = {}; - this._callHook("beforeConnect"); - this._connected = true; - this._initEvents(); - this._initObserver(); - this._callHook("connected"); - if (!this._isReady) { - ready(function() { - return this$1._callReady(); - }); - } - this._callUpdate(); - }; - UIkit.prototype._callDisconnected = function() { - if (!this._connected) { - return; - } - this._callHook("beforeDisconnect"); - if (this._observer) { - this._observer.disconnect(); - this._observer = null; - } - this._unbindEvents(); - this._callHook("disconnected"); - this._connected = false; - }; - UIkit.prototype._callReady = function() { - if (this._isReady) { - return; - } - this._isReady = true; - this._callHook("ready"); - this._resetComputeds(); - this._callUpdate(); - }; - UIkit.prototype._callUpdate = function(e) { - var this$1 = this; - e = createEvent(e || "update"); - var type = e.type; - if (includes([ "update", "load", "resize" ], type)) { - this._resetComputeds(); - } - var updates = this.$options.update; - var ref = this._frames; - var reads = ref.reads; - var writes = ref.writes; - if (!updates) { - return; - } - updates.forEach(function(ref, i) { - var read = ref.read; - var write = ref.write; - var events = ref.events; - if (type !== "update" && !includes(events, type)) { - return; - } - if (read && !includes(fastdom.reads, reads[i])) { - reads[i] = fastdom.read(function() { - var result = read.call(this$1, this$1._data, e); - if (result === false && write) { - fastdom.clear(writes[i]); - delete writes[i]; - } else if (isPlainObject(result)) { - assign(this$1._data, result); - } - delete reads[i]; - }); - } - if (write && !includes(fastdom.writes, writes[i])) { - writes[i] = fastdom.write(function() { - write.call(this$1, this$1._data, e); - delete writes[i]; - }); - } - }); - }; - } - function stateAPI(UIkit) { - var uid = 0; - UIkit.prototype.props = {}; - UIkit.prototype._init = function(options) { - options = options || {}; - options = this.$options = mergeOptions(this.constructor.options, options, this); - this.$el = null; - this.$name = UIkit.prefix + hyphenate(this.$options.name); - this.$props = {}; - this._frames = { - reads: {}, - writes: {} - }; - this._events = []; - this._uid = uid++; - this._initData(); - this._initMethods(); - this._initComputeds(); - this._callHook("created"); - if (options.el) { - this.$mount(options.el); - } - }; - UIkit.prototype._initData = function() { - var this$1 = this; - var ref = this.$options; - var defaults = ref.defaults; - var data$$1 = ref.data; - if (data$$1 === void 0) data$$1 = {}; - var args = ref.args; - if (args === void 0) args = []; - var props = ref.props; - if (props === void 0) props = {}; - var el = ref.el; - if (args.length && isArray(data$$1)) { - data$$1 = data$$1.slice(0, args.length).reduce(function(data$$1, value, index) { - if (isPlainObject(value)) { - assign(data$$1, value); - } else { - data$$1[args[index]] = value; - } - return data$$1; - }, {}); - } - for (var key in assign({}, defaults, props)) { - this$1.$props[key] = this$1[key] = hasOwn(data$$1, key) && !isUndefined(data$$1[key]) ? coerce(props[key], data$$1[key], el) : defaults ? defaults[key] && isArray(defaults[key]) ? defaults[key].concat() : defaults[key] : null; - } - }; - UIkit.prototype._initMethods = function() { - var this$1 = this; - var ref = this.$options; - var methods = ref.methods; - if (methods) { - for (var key in methods) { - this$1[key] = bind(methods[key], this$1); - } - } - }; - UIkit.prototype._initComputeds = function() { - var this$1 = this; - var ref = this.$options; - var computed = ref.computed; - this._resetComputeds(); - if (computed) { - for (var key in computed) { - registerComputed(this$1, key, computed[key]); - } - } - }; - UIkit.prototype._resetComputeds = function() { - this._computeds = {}; - }; - UIkit.prototype._initProps = function(props) { - var this$1 = this; - var key; - this._resetComputeds(); - props = props || getProps(this.$options, this.$name); - for (key in props) { - if (!isUndefined(props[key])) { - this$1.$props[key] = props[key]; - } - } - var exclude = [ this.$options.computed, this.$options.methods ]; - for (key in this$1.$props) { - if (key in props && notIn(exclude, key)) { - this$1[key] = this$1.$props[key]; - } - } - }; - UIkit.prototype._initEvents = function() { - var this$1 = this; - var ref = this.$options; - var events = ref.events; - if (events) { - events.forEach(function(event) { - if (!hasOwn(event, "handler")) { - for (var key in event) { - registerEvent(this$1, event[key], key); - } - } else { - registerEvent(this$1, event); - } - }); - } - }; - UIkit.prototype._unbindEvents = function() { - this._events.forEach(function(unbind) { - return unbind(); - }); - this._events = []; - }; - UIkit.prototype._initObserver = function() { - var this$1 = this; - var ref = this.$options; - var attrs = ref.attrs; - var props = ref.props; - var el = ref.el; - if (this._observer || !props || !attrs) { - return; - } - attrs = isArray(attrs) ? attrs : Object.keys(props).map(function(key) { - return hyphenate(key); - }); - this._observer = new MutationObserver(function() { - var data$$1 = getProps(this$1.$options, this$1.$name); - if (attrs.some(function(key) { - return !isUndefined(data$$1[key]) && data$$1[key] !== this$1.$props[key]; - })) { - this$1.$reset(data$$1); - } - }); - this._observer.observe(el, { - attributes: true, - attributeFilter: attrs.concat([ this.$name, "data-" + this.$name ]) - }); - }; - function getProps(opts, name) { - var data$$1 = {}; - var args = opts.args; - if (args === void 0) args = []; - var props = opts.props; - if (props === void 0) props = {}; - var el = opts.el; - if (!props) { - return data$$1; - } - for (var key in props) { - var prop = hyphenate(key); - if (hasAttr(el, prop)) { - var value = coerce(props[key], attr(el, prop), el); - if (prop === "target" && (!value || startsWith(value, "_"))) { - continue; - } - data$$1[key] = value; - } - } - var options = parseOptions(data(el, name), args); - for (var key$1 in options) { - var prop$1 = camelize(key$1); - if (props[prop$1] !== undefined) { - data$$1[prop$1] = coerce(props[prop$1], options[key$1], el); - } - } - return data$$1; - } - function parseOptions(options, args) { - var obj; - if (args === void 0) args = []; - try { - return !options ? {} : startsWith(options, "{") ? JSON.parse(options) : args.length && !includes(options, ":") ? (obj = {}, - obj[args[0]] = options, obj) : options.split(";").reduce(function(options, option) { - var ref = option.split(/:(.+)/); - var key = ref[0]; - var value = ref[1]; - if (key && value) { - options[key.trim()] = value.trim(); - } - return options; - }, {}); - } catch (e) { - return {}; - } - } - function registerComputed(component, key, cb) { - Object.defineProperty(component, key, { - enumerable: true, - get: function get() { - var _computeds = component._computeds; - var $props = component.$props; - var $el = component.$el; - if (!hasOwn(_computeds, key)) { - _computeds[key] = cb.call(component, $props, $el); - } - return _computeds[key]; - }, - set: function set(value) { - component._computeds[key] = value; - } - }); - } - function registerEvent(component, event, key) { - if (!isPlainObject(event)) { - event = { - name: key, - handler: event - }; - } - var name = event.name; - var el = event.el; - var handler = event.handler; - var capture = event.capture; - var delegate = event.delegate; - var filter = event.filter; - var self = event.self; - el = isFunction(el) ? el.call(component) : el || component.$el; - if (isArray(el)) { - el.forEach(function(el) { - return registerEvent(component, assign({}, event, { - el: el - }), key); - }); - return; - } - if (!el || filter && !filter.call(component)) { - return; - } - handler = detail(isString(handler) ? component[handler] : bind(handler, component)); - if (self) { - handler = selfFilter(handler); - } - component._events.push(on(el, name, !delegate ? null : isString(delegate) ? delegate : delegate.call(component), handler, capture)); - } - function selfFilter(handler) { - return function selfHandler(e) { - if (e.target === e.currentTarget || e.target === e.current) { - return handler.call(null, e); - } - }; - } - function notIn(options, key) { - return options.every(function(arr) { - return !arr || !hasOwn(arr, key); - }); - } - function detail(listener) { - return function(e) { - return isArray(e.detail) ? listener.apply(void 0, [ e ].concat(e.detail)) : listener(e); - }; - } - function coerce(type, value, context) { - if (type === Boolean) { - return toBoolean(value); - } else if (type === Number) { - return toNumber(value); - } else if (type === "query") { - return query(value, context); - } else if (type === "list") { - return toList(value); - } else if (type === "media") { - return toMedia(value); - } - return type ? type(value) : value; - } - function toMedia(value) { - if (isString(value)) { - if (value[0] === "@") { - var name = "media-" + value.substr(1); - value = toFloat(getCssVar(name)); - } else if (isNaN(value)) { - return value; - } - } - return value && !isNaN(value) ? "(min-width: " + value + "px)" : false; - } - } - function instanceAPI(UIkit) { - var DATA = UIkit.data; - UIkit.prototype.$mount = function(el) { - var ref = this.$options; - var name = ref.name; - if (!el[DATA]) { - el[DATA] = {}; - } - if (el[DATA][name]) { - return; - } - el[DATA][name] = this; - this.$el = this.$options.el = this.$options.el || el; - this._initProps(); - this._callHook("init"); - if (within(el, document)) { - this._callConnected(); - } - }; - UIkit.prototype.$emit = function(e) { - this._callUpdate(e); - }; - UIkit.prototype.$reset = function(data) { - this._callDisconnected(); - this._initProps(data); - this._callConnected(); - }; - UIkit.prototype.$destroy = function(removeEl) { - if (removeEl === void 0) removeEl = false; - var ref = this.$options; - var el = ref.el; - var name = ref.name; - if (el) { - this._callDisconnected(); - } - this._callHook("destroy"); - if (!el || !el[DATA]) { - return; - } - delete el[DATA][name]; - if (!Object.keys(el[DATA]).length) { - delete el[DATA]; - } - if (removeEl) { - remove(this.$el); - } - }; - } - var UIkit$2 = function(options) { - this._init(options); - }; - UIkit$2.util = util; - UIkit$2.data = "__uikit__"; - UIkit$2.prefix = "uk-"; - UIkit$2.options = {}; - globalAPI(UIkit$2); - hooksAPI(UIkit$2); - stateAPI(UIkit$2); - instanceAPI(UIkit$2); - componentAPI(UIkit$2); - var Class = { - init: function init() { - addClass(this.$el, this.$name); - } - }; - var Container = { - props: { - container: Boolean - }, - defaults: { - container: true - }, - computed: { - container: function container(ref) { - var container = ref.container; - return container === true && UIkit$2.container || container && $(container); - } - } - }; - var Togglable = { - props: { - cls: Boolean, - animation: "list", - duration: Number, - origin: String, - transition: String, - queued: Boolean - }, - defaults: { - cls: false, - animation: [ false ], - duration: 200, - origin: false, - transition: "linear", - queued: false, - initProps: { - overflow: "", - height: "", - paddingTop: "", - paddingBottom: "", - marginTop: "", - marginBottom: "" - }, - hideProps: { - overflow: "hidden", - height: 0, - paddingTop: 0, - paddingBottom: 0, - marginTop: 0, - marginBottom: 0 - } - }, - computed: { - hasAnimation: function hasAnimation(ref) { - var animation = ref.animation; - return !!animation[0]; - }, - hasTransition: function hasTransition(ref) { - var animation = ref.animation; - return this.hasAnimation && animation[0] === true; - } - }, - methods: { - toggleElement: function toggleElement(targets, show, animate) { - var this$1 = this; - return new Promise(function(resolve) { - targets = toNodes(targets); - var all = function(targets) { - return Promise.all(targets.map(function(el) { - return this$1._toggleElement(el, show, animate); - })); - }; - var toggled = targets.filter(function(el) { - return this$1.isToggled(el); - }); - var untoggled = targets.filter(function(el) { - return !includes(toggled, el); - }); - var p; - if (!this$1.queued || !isUndefined(animate) || !isUndefined(show) || !this$1.hasAnimation || targets.length < 2) { - p = all(untoggled.concat(toggled)); - } else { - var body = document.body; - var scroll = body.scrollTop; - var el = toggled[0]; - var inProgress = Animation.inProgress(el) && hasClass(el, "uk-animation-leave") || Transition.inProgress(el) && el.style.height === "0px"; - p = all(toggled); - if (!inProgress) { - p = p.then(function() { - var p = all(untoggled); - body.scrollTop = scroll; - return p; - }); - } - } - p.then(resolve, noop); - }); - }, - toggleNow: function toggleNow(targets, show) { - var this$1 = this; - return new Promise(function(resolve) { - return Promise.all(toNodes(targets).map(function(el) { - return this$1._toggleElement(el, show, false); - })).then(resolve, noop); - }); - }, - isToggled: function isToggled(el) { - var nodes = toNodes(el || this.$el); - return this.cls ? hasClass(nodes, this.cls.split(" ")[0]) : !hasAttr(nodes, "hidden"); - }, - updateAria: function updateAria(el) { - if (this.cls === false) { - attr(el, "aria-hidden", !this.isToggled(el)); - } - }, - _toggleElement: function _toggleElement(el, show, animate) { - var this$1 = this; - show = isBoolean(show) ? show : Animation.inProgress(el) ? hasClass(el, "uk-animation-leave") : Transition.inProgress(el) ? el.style.height === "0px" : !this.isToggled(el); - if (!trigger(el, "before" + (show ? "show" : "hide"), [ this ])) { - return Promise.reject(); - } - var promise = (animate === false || !this.hasAnimation ? this._toggleImmediate : this.hasTransition ? this._toggleHeight : this._toggleAnimation)(el, show); - trigger(el, show ? "show" : "hide", [ this ]); - return promise.then(function() { - trigger(el, show ? "shown" : "hidden", [ this$1 ]); - trigger(el, "resize"); - }); - }, - _toggle: function _toggle(el, toggled) { - if (!el) { - return; - } - var changed; - if (this.cls) { - changed = includes(this.cls, " ") || Boolean(toggled) !== hasClass(el, this.cls); - changed && toggleClass(el, this.cls, includes(this.cls, " ") ? undefined : toggled); - } else { - changed = Boolean(toggled) === hasAttr(el, "hidden"); - changed && attr(el, "hidden", !toggled ? "" : null); - } - $$("[autofocus]", el).some(function(el) { - return isVisible(el) && (el.focus() || true); - }); - this.updateAria(el); - changed && trigger(el, "resize"); - }, - _toggleImmediate: function _toggleImmediate(el, show) { - this._toggle(el, show); - return Promise.resolve(); - }, - _toggleHeight: function _toggleHeight(el, show) { - var this$1 = this; - var inProgress = Transition.inProgress(el); - var inner = el.hasChildNodes ? toFloat(css(el.firstElementChild, "marginTop")) + toFloat(css(el.lastElementChild, "marginBottom")) : 0; - var currentHeight = isVisible(el) ? height(el) + (inProgress ? 0 : inner) : 0; - Transition.cancel(el); - if (!this.isToggled(el)) { - this._toggle(el, true); - } - height(el, ""); - fastdom.flush(); - var endHeight = height(el) + (inProgress ? 0 : inner); - height(el, currentHeight); - return (show ? Transition.start(el, assign({}, this.initProps, { - overflow: "hidden", - height: endHeight - }), Math.round(this.duration * (1 - currentHeight / endHeight)), this.transition) : Transition.start(el, this.hideProps, Math.round(this.duration * (currentHeight / endHeight)), this.transition).then(function() { - return this$1._toggle(el, false); - })).then(function() { - return css(el, this$1.initProps); - }); - }, - _toggleAnimation: function _toggleAnimation(el, show) { - var this$1 = this; - Animation.cancel(el); - if (show) { - this._toggle(el, true); - return Animation.in(el, this.animation[0], this.duration, this.origin); - } - return Animation.out(el, this.animation[1] || this.animation[0], this.duration, this.origin).then(function() { - return this$1._toggle(el, false); - }); - } - } - }; - var active; - var Modal = { - mixins: [ Class, Container, Togglable ], - props: { - selPanel: String, - selClose: String, - escClose: Boolean, - bgClose: Boolean, - stack: Boolean - }, - defaults: { - cls: "uk-open", - escClose: true, - bgClose: true, - overlay: true, - stack: false - }, - computed: { - panel: function panel(ref, $el) { - var selPanel = ref.selPanel; - return $(selPanel, $el); - }, - transitionElement: function transitionElement() { - return this.panel; - }, - transitionDuration: function transitionDuration() { - return toMs(css(this.transitionElement, "transitionDuration")); - } - }, - events: [ { - name: "click", - delegate: function delegate() { - return this.selClose; - }, - handler: function handler(e) { - e.preventDefault(); - this.hide(); - } - }, { - name: "toggle", - self: true, - handler: function handler(e) { - if (e.defaultPrevented) { - return; - } - e.preventDefault(); - this.toggle(); - } - }, { - name: "beforeshow", - self: true, - handler: function handler(e) { - var prev = active && active !== this && active; - active = this; - if (prev) { - if (this.stack) { - this.prev = prev; - } else { - prev.hide().then(this.show); - e.preventDefault(); - return; - } - } - registerEvents(); - } - }, { - name: "beforehide", - self: true, - handler: function handler() { - active = active && active !== this && active || this.prev; - if (!active) { - deregisterEvents(); - } - } - }, { - name: "show", - self: true, - handler: function handler() { - if (!hasClass(document.documentElement, this.clsPage)) { - this.scrollbarWidth = width(window) - width(document); - css(document.body, "overflowY", this.scrollbarWidth && this.overlay ? "scroll" : ""); - } - addClass(document.documentElement, this.clsPage); - } - }, { - name: "hidden", - self: true, - handler: function handler() { - var this$1 = this; - var found; - var ref = this; - var prev = ref.prev; - while (prev) { - if (prev.clsPage === this$1.clsPage) { - found = true; - break; - } - prev = prev.prev; - } - if (!found) { - removeClass(document.documentElement, this.clsPage); - } - !this.prev && css(document.body, "overflowY", ""); - } - } ], - methods: { - toggle: function toggle() { - return this.isToggled() ? this.hide() : this.show(); - }, - show: function show() { - if (this.isToggled()) { - return; - } - if (this.container && this.$el.parentNode !== this.container) { - append(this.container, this.$el); - this._callConnected(); - } - return this.toggleNow(this.$el, true); - }, - hide: function hide() { - if (this.isToggled()) { - return this.toggleNow(this.$el, false); - } - }, - getActive: function getActive() { - return active; - }, - _toggleImmediate: function _toggleImmediate(el, show) { - var this$1 = this; - return new Promise(function(resolve) { - return requestAnimationFrame(function() { - this$1._toggle(el, show); - if (this$1.transitionDuration) { - once(this$1.transitionElement, "transitionend", resolve, false, function(e) { - return e.target === this$1.transitionElement; - }); - } else { - resolve(); - } - }); - }); - } - } - }; - var events; - function registerEvents() { - if (events) { - return; - } - events = [ on(document, "click", function(ref) { - var target = ref.target; - var defaultPrevented = ref.defaultPrevented; - if (active && active.bgClose && !defaultPrevented && (!active.overlay || within(target, active.$el)) && (!active.panel || !within(target, active.panel))) { - active.hide(); - } - }), on(document, "keydown", function(e) { - if (e.keyCode === 27 && active && active.escClose) { - e.preventDefault(); - active.hide(); - } - }) ]; - } - function deregisterEvents() { - events && events.forEach(function(unbind) { - return unbind(); - }); - events = null; - } - var Position = { - props: { - pos: String, - offset: null, - flip: Boolean, - clsPos: String - }, - defaults: { - pos: "bottom-" + (!isRtl ? "left" : "right"), - flip: true, - offset: false, - clsPos: "" - }, - computed: { - pos: function pos(ref) { - var pos = ref.pos; - return (pos + (!includes(pos, "-") ? "-center" : "")).split("-"); - }, - dir: function dir() { - return this.pos[0]; - }, - align: function align() { - return this.pos[1]; - } - }, - methods: { - positionAt: function positionAt$1(element, target, boundary) { - removeClasses(element, this.clsPos + "-(top|bottom|left|right)(-[a-z]+)?"); - css(element, { - top: "", - left: "" - }); - var node; - var ref = this; - var offset$$1 = ref.offset; - offset$$1 = isNumeric(offset$$1) ? offset$$1 : (node = $(offset$$1)) ? offset(node)[axis === "x" ? "left" : "top"] - offset(target)[axis === "x" ? "right" : "bottom"] : 0; - var axis = this.getAxis(); - var ref$1 = positionAt(element, target, axis === "x" ? flipPosition(this.dir) + " " + this.align : this.align + " " + flipPosition(this.dir), axis === "x" ? this.dir + " " + this.align : this.align + " " + this.dir, axis === "x" ? "" + (this.dir === "left" ? -offset$$1 : offset$$1) : " " + (this.dir === "top" ? -offset$$1 : offset$$1), null, this.flip, boundary).target; - var x = ref$1.x; - var y = ref$1.y; - this.dir = axis === "x" ? x : y; - this.align = axis === "x" ? y : x; - toggleClass(element, this.clsPos + "-" + this.dir + "-" + this.align, this.offset === false); - }, - getAxis: function getAxis() { - return this.dir === "top" || this.dir === "bottom" ? "y" : "x"; - } - } - }; - function mixin(UIkit) { - UIkit.mixin.class = Class; - UIkit.mixin.container = Container; - UIkit.mixin.modal = Modal; - UIkit.mixin.position = Position; - UIkit.mixin.togglable = Togglable; - } - function Accordion(UIkit) { - UIkit.component("accordion", { - mixins: [ Class, Togglable ], - props: { - targets: String, - active: null, - collapsible: Boolean, - multiple: Boolean, - toggle: String, - content: String, - transition: String - }, - defaults: { - targets: "> *", - active: false, - animation: [ true ], - collapsible: true, - multiple: false, - clsOpen: "uk-open", - toggle: "> .uk-accordion-title", - content: "> .uk-accordion-content", - transition: "ease" - }, - computed: { - items: function items(ref, $el) { - var targets = ref.targets; - return $$(targets, $el); - } - }, - events: [ { - name: "click", - delegate: function delegate() { - return this.targets + " " + this.$props.toggle; - }, - handler: function handler(e) { - e.preventDefault(); - this.toggle(index($$(this.targets + " " + this.$props.toggle, this.$el), e.current)); - } - } ], - connected: function connected() { - if (this.active === false) { - return; - } - var active = this.items[Number(this.active)]; - if (active && !hasClass(active, this.clsOpen)) { - this.toggle(active, false); - } - }, - update: function update() { - var this$1 = this; - this.items.forEach(function(el) { - return this$1._toggleImmediate($(this$1.content, el), hasClass(el, this$1.clsOpen)); - }); - var active = !this.collapsible && !hasClass(this.items, this.clsOpen) && this.items[0]; - if (active) { - this.toggle(active, false); - } - }, - methods: { - toggle: function toggle(item, animate) { - var this$1 = this; - var index = getIndex(item, this.items); - var active = filter(this.items, "." + this.clsOpen); - item = this.items[index]; - item && [ item ].concat(!this.multiple && !includes(active, item) && active || []).forEach(function(el) { - var isItem = el === item; - var state = isItem && !hasClass(el, this$1.clsOpen); - if (!state && isItem && !this$1.collapsible && active.length < 2) { - return; - } - toggleClass(el, this$1.clsOpen, state); - var content = el._wrapper ? el._wrapper.firstElementChild : $(this$1.content, el); - if (!el._wrapper) { - el._wrapper = wrapAll(content, "
    "); - attr(el._wrapper, "hidden", state ? "" : null); - } - this$1._toggleImmediate(content, true); - this$1.toggleElement(el._wrapper, state, animate).then(function() { - if (hasClass(el, this$1.clsOpen) === state) { - if (!state) { - this$1._toggleImmediate(content, false); - } - el._wrapper = null; - unwrap(content); - } - }); - }); - } - } - }); - } - function Alert(UIkit) { - UIkit.component("alert", { - attrs: true, - mixins: [ Class, Togglable ], - args: "animation", - props: { - close: String - }, - defaults: { - animation: [ true ], - selClose: ".uk-alert-close", - duration: 150, - hideProps: assign({ - opacity: 0 - }, Togglable.defaults.hideProps) - }, - events: [ { - name: "click", - delegate: function delegate() { - return this.selClose; - }, - handler: function handler(e) { - e.preventDefault(); - this.close(); - } - } ], - methods: { - close: function close() { - var this$1 = this; - this.toggleElement(this.$el).then(function() { - return this$1.$destroy(true); - }); - } - } - }); - } - function Core(UIkit) { - ready(function() { - var scroll = 0; - var started = 0; - on(window, "load resize", function(e) { - return UIkit.update(null, e); - }); - on(window, "scroll", function(e) { - e.dir = scroll <= window.pageYOffset ? "down" : "up"; - e.scrollY = scroll = window.pageYOffset; - UIkit.update(null, e); - }); - on(document, "animationstart", function(ref) { - var target = ref.target; - if ((css(target, "animationName") || "").match(/^uk-.*(left|right)/)) { - started++; - css(document.body, "overflowX", "hidden"); - setTimeout(function() { - if (!--started) { - css(document.body, "overflowX", ""); - } - }, toMs(css(target, "animationDuration")) + 100); - } - }, true); - if (!hasTouch) { - return; - } - var cls = "uk-hover"; - on(document, "tap", function(ref) { - var target = ref.target; - return $$("." + cls).forEach(function(el) { - return !within(target, el) && removeClass(el, cls); - }); - }); - Object.defineProperty(UIkit, "hoverSelector", { - set: function set(selector) { - on(document, "tap", selector, function(ref) { - var current = ref.current; - return addClass(current, cls); - }); - } - }); - UIkit.hoverSelector = ".uk-animation-toggle, .uk-transition-toggle, [uk-hover]"; - }); - } - function Cover(UIkit) { - UIkit.component("cover", { - mixins: [ Class, UIkit.components.video.options ], - props: { - width: Number, - height: Number - }, - defaults: { - automute: true - }, - update: { - write: function write() { - var el = this.$el; - if (!isVisible(el)) { - return; - } - var ref = el.parentNode; - var height = ref.offsetHeight; - var width = ref.offsetWidth; - css(css(el, { - width: "", - height: "" - }), Dimensions.cover({ - width: this.width || el.clientWidth, - height: this.height || el.clientHeight - }, { - width: width + (width % 2 ? 1 : 0), - height: height + (height % 2 ? 1 : 0) - })); - }, - events: [ "load", "resize" ] - }, - events: { - loadedmetadata: function loadedmetadata() { - this.$emit(); - } - } - }); - } - function Drop(UIkit) { - var active; - UIkit.component("drop", { - mixins: [ Position, Togglable ], - args: "pos", - props: { - mode: "list", - toggle: Boolean, - boundary: "query", - boundaryAlign: Boolean, - delayShow: Number, - delayHide: Number, - clsDrop: String - }, - defaults: { - mode: [ "click", "hover" ], - toggle: true, - boundary: window, - boundaryAlign: false, - delayShow: 0, - delayHide: 800, - clsDrop: false, - hoverIdle: 200, - animation: [ "uk-animation-fade" ], - cls: "uk-open" - }, - computed: { - clsDrop: function clsDrop(ref) { - var clsDrop = ref.clsDrop; - return clsDrop || "uk-" + this.$options.name; - }, - clsPos: function clsPos() { - return this.clsDrop; - } - }, - init: function init() { - this.tracker = new MouseTracker(); - addClass(this.$el, this.clsDrop); - }, - connected: function connected() { - var ref = this.$props; - var toggle = ref.toggle; - this.toggle = toggle && UIkit.toggle(isString(toggle) ? query(toggle, this.$el) : this.$el.previousElementSibling, { - target: this.$el, - mode: this.mode - }); - this.updateAria(this.$el); - }, - events: [ { - name: "click", - delegate: function delegate() { - return "." + this.clsDrop + "-close"; - }, - handler: function handler(e) { - e.preventDefault(); - this.hide(false); - } - }, { - name: "click", - delegate: function delegate() { - return 'a[href^="#"]'; - }, - handler: function handler(e) { - if (e.defaultPrevented) { - return; - } - var id = e.target.hash; - if (!id) { - e.preventDefault(); - } - if (!id || !within(id, this.$el)) { - this.hide(false); - } - } - }, { - name: "beforescroll", - handler: function handler() { - this.hide(false); - } - }, { - name: "toggle", - self: true, - handler: function handler(e, toggle) { - e.preventDefault(); - if (this.isToggled()) { - this.hide(false); - } else { - this.show(toggle, false); - } - } - }, { - name: pointerEnter, - filter: function filter() { - return includes(this.mode, "hover"); - }, - handler: function handler(e) { - if (isTouch(e)) { - return; - } - if (active && active !== this && active.toggle && includes(active.toggle.mode, "hover") && !within(e.target, active.toggle.$el) && !pointInRect({ - x: e.pageX, - y: e.pageY - }, offset(active.$el))) { - active.hide(false); - } - e.preventDefault(); - this.show(this.toggle); - } - }, { - name: "toggleshow", - handler: function handler(e, toggle) { - if (toggle && !includes(toggle.target, this.$el)) { - return; - } - e.preventDefault(); - this.show(toggle || this.toggle); - } - }, { - name: "togglehide " + pointerLeave, - handler: function handler(e, toggle) { - if (isTouch(e) || toggle && !includes(toggle.target, this.$el)) { - return; - } - e.preventDefault(); - if (this.toggle && includes(this.toggle.mode, "hover")) { - this.hide(); - } - } - }, { - name: "beforeshow", - self: true, - handler: function handler() { - this.clearTimers(); - Animation.cancel(this.$el); - this.position(); - } - }, { - name: "show", - self: true, - handler: function handler() { - this.tracker.init(); - if (this.toggle) { - addClass(this.toggle.$el, this.cls); - attr(this.toggle.$el, "aria-expanded", "true"); - } - registerEvent(); - } - }, { - name: "beforehide", - self: true, - handler: function handler() { - this.clearTimers(); - } - }, { - name: "hide", - handler: function handler(ref) { - var target = ref.target; - if (this.$el !== target) { - active = active === null && within(target, this.$el) && this.isToggled() ? this : active; - return; - } - active = this.isActive() ? null : active; - if (this.toggle) { - removeClass(this.toggle.$el, this.cls); - attr(this.toggle.$el, "aria-expanded", "false"); - this.toggle.$el.blur(); - $$("a, button", this.toggle.$el).forEach(function(el) { - return el.blur(); - }); - } - this.tracker.cancel(); - } - } ], - update: { - write: function write() { - if (this.isToggled() && !Animation.inProgress(this.$el)) { - this.position(); - } - }, - events: [ "resize" ] - }, - methods: { - show: function show(toggle, delay) { - var this$1 = this; - if (delay === void 0) delay = true; - var show = function() { - return !this$1.isToggled() && this$1.toggleElement(this$1.$el, true); - }; - var tryShow = function() { - this$1.toggle = toggle || this$1.toggle; - this$1.clearTimers(); - if (this$1.isActive()) { - return; - } else if (delay && active && active !== this$1 && active.isDelaying) { - this$1.showTimer = setTimeout(this$1.show, 10); - return; - } else if (this$1.isParentOf(active)) { - if (active.hideTimer) { - active.hide(false); - } else { - return; - } - } else if (active && !this$1.isChildOf(active) && !this$1.isParentOf(active)) { - var prev; - while (active && active !== prev && !this$1.isChildOf(active)) { - prev = active; - active.hide(false); - } - } - if (delay && this$1.delayShow) { - this$1.showTimer = setTimeout(show, this$1.delayShow); - } else { - show(); - } - active = this$1; - }; - if (toggle && this.toggle && toggle.$el !== this.toggle.$el) { - once(this.$el, "hide", tryShow); - this.hide(false); - } else { - tryShow(); - } - }, - hide: function hide(delay) { - var this$1 = this; - if (delay === void 0) delay = true; - var hide = function() { - return this$1.toggleNow(this$1.$el, false); - }; - this.clearTimers(); - this.isDelaying = this.tracker.movesTo(this.$el); - if (delay && this.isDelaying) { - this.hideTimer = setTimeout(this.hide, this.hoverIdle); - } else if (delay && this.delayHide) { - this.hideTimer = setTimeout(hide, this.delayHide); - } else { - hide(); - } - }, - clearTimers: function clearTimers() { - clearTimeout(this.showTimer); - clearTimeout(this.hideTimer); - this.showTimer = null; - this.hideTimer = null; - this.isDelaying = false; - }, - isActive: function isActive() { - return active === this; - }, - isChildOf: function isChildOf(drop) { - return drop && drop !== this && within(this.$el, drop.$el); - }, - isParentOf: function isParentOf(drop) { - return drop && drop !== this && within(drop.$el, this.$el); - }, - position: function position() { - removeClasses(this.$el, this.clsDrop + "-(stack|boundary)"); - css(this.$el, { - top: "", - left: "", - display: "block" - }); - toggleClass(this.$el, this.clsDrop + "-boundary", this.boundaryAlign); - var boundary = offset(this.boundary); - var alignTo = this.boundaryAlign ? boundary : offset(this.toggle.$el); - if (this.align === "justify") { - var prop = this.getAxis() === "y" ? "width" : "height"; - css(this.$el, prop, alignTo[prop]); - } else if (this.$el.offsetWidth > Math.max(boundary.right - alignTo.left, alignTo.right - boundary.left)) { - addClass(this.$el, this.clsDrop + "-stack"); - } - this.positionAt(this.$el, this.boundaryAlign ? this.boundary : this.toggle.$el, this.boundary); - css(this.$el, "display", ""); - } - } - }); - UIkit.drop.getActive = function() { - return active; - }; - var registered; - function registerEvent() { - if (registered) { - return; - } - registered = true; - on(document, "click", function(ref) { - var target = ref.target; - var defaultPrevented = ref.defaultPrevented; - var prev; - if (defaultPrevented) { - return; - } - while (active && active !== prev && !within(target, active.$el) && !(active.toggle && within(target, active.toggle.$el))) { - prev = active; - active.hide(false); - } - }); - } - } - function Dropdown(UIkit) { - UIkit.component("dropdown", UIkit.components.drop.extend({ - name: "dropdown" - })); - } - function FormCustom(UIkit) { - UIkit.component("form-custom", { - mixins: [ Class ], - args: "target", - props: { - target: Boolean - }, - defaults: { - target: false - }, - computed: { - input: function input(_, $el) { - return $(selInput, $el); - }, - state: function state() { - return this.input.nextElementSibling; - }, - target: function target(ref, $el) { - var target = ref.target; - return target && (target === true && this.input.parentNode === $el && this.input.nextElementSibling || query(target, $el)); - } - }, - update: function update() { - var ref = this; - var target = ref.target; - var input = ref.input; - if (!target) { - return; - } - var option; - target[isInput(target) ? "value" : "textContent"] = input.files && input.files[0] ? input.files[0].name : matches(input, "select") && (option = $$("option", input).filter(function(el) { - return el.selected; - })[0]) ? option.textContent : input.value; - }, - events: [ { - name: "focusin focusout mouseenter mouseleave", - delegate: selInput, - handler: function handler(ref) { - var type = ref.type; - var current = ref.current; - if (current === this.input) { - toggleClass(this.state, "uk-" + (includes(type, "focus") ? "focus" : "hover"), includes([ "focusin", "mouseenter" ], type)); - } - } - }, { - name: "change", - handler: function handler() { - this.$emit(); - } - } ] - }); - } - function Gif(UIkit) { - UIkit.component("gif", { - update: { - read: function read(data) { - var inview = isInView(this.$el); - if (!inview || data.isInView === inview) { - return false; - } - data.isInView = inview; - }, - write: function write() { - this.$el.src = this.$el.src; - }, - events: [ "scroll", "load", "resize" ] - } - }); - } - function Grid(UIkit) { - UIkit.component("grid", UIkit.components.margin.extend({ - mixins: [ Class ], - name: "grid", - defaults: { - margin: "uk-grid-margin", - clsStack: "uk-grid-stack" - }, - update: { - write: function write(ref) { - var stacks = ref.stacks; - toggleClass(this.$el, this.clsStack, stacks); - }, - events: [ "load", "resize" ] - } - })); - } - function HeightMatch(UIkit) { - UIkit.component("height-match", { - args: "target", - props: { - target: String, - row: Boolean - }, - defaults: { - target: "> *", - row: true - }, - computed: { - elements: function elements(ref, $el) { - var target = ref.target; - return $$(target, $el); - } - }, - update: { - read: function read() { - var this$1 = this; - var lastOffset = false; - css(this.elements, "minHeight", ""); - return { - rows: !this.row ? [ this.match(this.elements) ] : this.elements.reduce(function(rows, el) { - if (lastOffset !== el.offsetTop) { - rows.push([ el ]); - } else { - rows[rows.length - 1].push(el); - } - lastOffset = el.offsetTop; - return rows; - }, []).map(function(elements) { - return this$1.match(elements); - }) - }; - }, - write: function write(ref) { - var rows = ref.rows; - rows.forEach(function(ref) { - var height = ref.height; - var elements = ref.elements; - return css(elements, "minHeight", height); - }); - }, - events: [ "load", "resize" ] - }, - methods: { - match: function match(elements) { - if (elements.length < 2) { - return {}; - } - var heights = []; - var max = 0; - elements.forEach(function(el) { - var style, hidden; - if (!isVisible(el)) { - style = attr(el, "style"); - hidden = attr(el, "hidden"); - attr(el, { - style: (style || "") + ";display:block !important;", - hidden: null - }); - } - max = Math.max(max, el.offsetHeight); - heights.push(el.offsetHeight); - if (!isUndefined(style)) { - attr(el, { - style: style, - hidden: hidden - }); - } - }); - elements = elements.filter(function(el, i) { - return heights[i] < max; - }); - return { - height: max, - elements: elements - }; - } - } - }); - } - function HeightViewport(UIkit) { - UIkit.component("height-viewport", { - props: { - expand: Boolean, - offsetTop: Boolean, - offsetBottom: Boolean, - minHeight: Number - }, - defaults: { - expand: false, - offsetTop: false, - offsetBottom: false, - minHeight: 0 - }, - update: { - write: function write() { - css(this.$el, "boxSizing", "border-box"); - var viewport = height(window); - var minHeight, offsetTop = 0; - if (this.expand) { - css(this.$el, { - height: "", - minHeight: "" - }); - var diff = viewport - offsetHeight(document.documentElement); - if (diff > 0) { - minHeight = offsetHeight(this.$el) + diff; - } - } else { - var ref = offset(this.$el); - var top = ref.top; - if (top < viewport / 2 && this.offsetTop) { - offsetTop += top; - } - if (this.offsetBottom === true) { - offsetTop += offsetHeight(this.$el.nextElementSibling); - } else if (isNumeric(this.offsetBottom)) { - offsetTop += viewport / 100 * this.offsetBottom; - } else if (this.offsetBottom && endsWith(this.offsetBottom, "px")) { - offsetTop += toFloat(this.offsetBottom); - } else if (isString(this.offsetBottom)) { - offsetTop += offsetHeight(query(this.offsetBottom, this.$el)); - } - minHeight = offsetTop ? "calc(100vh - " + offsetTop + "px)" : "100vh"; - } - if (!minHeight) { - return; - } - css(this.$el, { - height: "", - minHeight: minHeight - }); - var elHeight = this.$el.offsetHeight; - if (this.minHeight && this.minHeight > elHeight) { - css(this.$el, "minHeight", this.minHeight); - } - if (viewport - offsetTop >= elHeight) { - css(this.$el, "height", minHeight); - } - }, - events: [ "load", "resize" ] - } - }); - function offsetHeight(el) { - return el && el.offsetHeight || 0; - } - } - var closeIcon = ''; - var closeLarge = ''; - var marker = ''; - var navbarToggleIcon = ''; - var overlayIcon = ''; - var paginationNext = ''; - var paginationPrevious = ''; - var searchIcon = ''; - var searchLarge = ''; - var searchNavbar = ''; - var slidenavNext = ''; - var slidenavNextLarge = ''; - var slidenavPrevious = ''; - var slidenavPreviousLarge = ''; - var spinner = ''; - var totop = ''; - function Icon(UIkit) { - var parsed = {}; - var icons = { - spinner: spinner, - totop: totop, - marker: marker, - "close-icon": closeIcon, - "close-large": closeLarge, - "navbar-toggle-icon": navbarToggleIcon, - "overlay-icon": overlayIcon, - "pagination-next": paginationNext, - "pagination-previous": paginationPrevious, - "search-icon": searchIcon, - "search-large": searchLarge, - "search-navbar": searchNavbar, - "slidenav-next": slidenavNext, - "slidenav-next-large": slidenavNextLarge, - "slidenav-previous": slidenavPrevious, - "slidenav-previous-large": slidenavPreviousLarge - }; - UIkit.component("icon", UIkit.components.svg.extend({ - attrs: [ "icon", "ratio" ], - mixins: [ Class ], - name: "icon", - args: "icon", - props: [ "icon" ], - defaults: { - exclude: [ "id", "style", "class", "src", "icon" ] - }, - init: function init() { - addClass(this.$el, "uk-icon"); - if (isRtl) { - this.icon = swap(swap(this.icon, "left", "right"), "previous", "next"); - } - }, - methods: { - getSvg: function getSvg() { - var icon = getIcon(this.icon); - if (!icon) { - return Promise.reject("Icon not found."); - } - return Promise.resolve(icon); - } - } - })); - [ "marker", "navbar-toggle-icon", "overlay-icon", "pagination-previous", "pagination-next", "totop" ].forEach(function(name) { - return registerComponent(name); - }); - [ "slidenav-previous", "slidenav-next" ].forEach(function(name) { - return registerComponent(name, { - init: function init() { - addClass(this.$el, "uk-slidenav"); - if (hasClass(this.$el, "uk-slidenav-large")) { - this.icon += "-large"; - } - } - }); - }); - registerComponent("search-icon", { - init: function init() { - if (hasClass(this.$el, "uk-search-icon") && parents(this.$el, ".uk-search-large").length) { - this.icon = "search-large"; - } else if (parents(this.$el, ".uk-search-navbar").length) { - this.icon = "search-navbar"; - } - } - }); - registerComponent("close", { - init: function init() { - this.icon = "close-" + (hasClass(this.$el, "uk-close-large") ? "large" : "icon"); - } - }); - registerComponent("spinner", { - connected: function connected() { - var this$1 = this; - this.svg.then(function(svg) { - return this$1.ratio !== 1 && css($("circle", svg), "stroke-width", 1 / this$1.ratio); - }, noop); - } - }); - UIkit.icon.add = function(added) { - Object.keys(added).forEach(function(name) { - icons[name] = added[name]; - delete parsed[name]; - }); - if (UIkit._initialized) { - apply(document.body, function(el) { - var icon = UIkit.getComponent(el, "icon"); - if (icon) { - icon.$reset(); - } - }); - } - }; - function registerComponent(name, mixin$$1) { - UIkit.component(name, UIkit.components.icon.extend({ - name: name, - mixins: mixin$$1 ? [ mixin$$1 ] : [], - defaults: { - icon: name - } - })); - } - function getIcon(icon) { - if (!icons[icon]) { - return null; - } - if (!parsed[icon]) { - parsed[icon] = $(icons[icon].trim()); - } - return parsed[icon]; - } - } - function Leader(UIkit) { - UIkit.component("leader", { - mixins: [ Class ], - props: { - fill: String, - media: "media" - }, - defaults: { - fill: "", - media: false, - clsWrapper: "uk-leader-fill", - clsHide: "uk-leader-hide", - attrFill: "data-fill" - }, - computed: { - fill: function fill(ref) { - var fill = ref.fill; - return fill || getCssVar("leader-fill"); - } - }, - connected: function connected() { - var assign; - assign = wrapInner(this.$el, ''), this.wrapper = assign[0]; - }, - disconnected: function disconnected() { - unwrap(this.wrapper.childNodes); - }, - update: [ { - read: function read(ref) { - var changed = ref.changed; - var width = ref.width; - var prev = width; - width = Math.floor(this.$el.offsetWidth / 2); - return { - width: width, - changed: changed || prev !== width, - hide: this.media && !window.matchMedia(this.media).matches - }; - }, - write: function write(data) { - toggleClass(this.wrapper, this.clsHide, data.hide); - if (data.changed) { - data.changed = false; - attr(this.wrapper, this.attrFill, new Array(data.width).join(this.fill)); - } - }, - events: [ "load", "resize" ] - } ] - }); - } - function Margin(UIkit) { - UIkit.component("margin", { - props: { - margin: String, - firstColumn: Boolean - }, - defaults: { - margin: "uk-margin-small-top", - firstColumn: "uk-first-column" - }, - update: { - read: function read(data) { - var items = this.$el.children; - if (!items.length || !isVisible(this.$el)) { - return data.rows = false; - } - data.stacks = true; - var rows = [ [] ]; - for (var i = 0; i < items.length; i++) { - var el = items[i]; - var dim = el.getBoundingClientRect(); - if (!dim.height) { - continue; - } - for (var j = rows.length - 1; j >= 0; j--) { - var row = rows[j]; - if (!row[0]) { - row.push(el); - break; - } - var leftDim = row[0].getBoundingClientRect(); - if (dim.top >= Math.floor(leftDim.bottom)) { - rows.push([ el ]); - break; - } - if (Math.floor(dim.bottom) > leftDim.top) { - data.stacks = false; - if (dim.left < leftDim.left && !isRtl) { - row.unshift(el); - break; - } - row.push(el); - break; - } - if (j === 0) { - rows.unshift([ el ]); - break; - } - } - } - data.rows = rows; - }, - write: function write(ref) { - var this$1 = this; - var rows = ref.rows; - rows.forEach(function(row, i) { - return row.forEach(function(el, j) { - toggleClass(el, this$1.margin, i !== 0); - toggleClass(el, this$1.firstColumn, j === 0); - }); - }); - }, - events: [ "load", "resize" ] - } - }); - } - function Modal$1(UIkit) { - UIkit.component("modal", { - mixins: [ Modal ], - defaults: { - clsPage: "uk-modal-page", - selPanel: ".uk-modal-dialog", - selClose: ".uk-modal-close, .uk-modal-close-default, .uk-modal-close-outside, .uk-modal-close-full" - }, - events: [ { - name: "show", - self: true, - handler: function handler() { - if (hasClass(this.panel, "uk-margin-auto-vertical")) { - addClass(this.$el, "uk-flex"); - } else { - css(this.$el, "display", "block"); - } - height(this.$el); - } - }, { - name: "hidden", - self: true, - handler: function handler() { - css(this.$el, "display", ""); - removeClass(this.$el, "uk-flex"); - } - } ] - }); - UIkit.component("overflow-auto", { - mixins: [ Class ], - computed: { - modal: function modal(_, $el) { - return closest($el, ".uk-modal"); - }, - panel: function panel(_, $el) { - return closest($el, ".uk-modal-dialog"); - } - }, - connected: function connected() { - css(this.$el, "minHeight", 150); - }, - update: { - write: function write() { - if (!this.panel || !this.modal) { - return; - } - var current = css(this.$el, "maxHeight"); - css(css(this.$el, "maxHeight", 150), "maxHeight", Math.max(150, 150 + height(this.modal) - this.panel.offsetHeight)); - if (current !== css(this.$el, "maxHeight")) { - trigger(this.$el, "resize"); - } - }, - events: [ "load", "resize" ] - } - }); - UIkit.modal.dialog = function(content, options) { - var dialog = UIkit.modal('
    ' + content + "
    ", options); - dialog.show(); - on(dialog.$el, "hidden", function(ref) { - var target = ref.target; - var currentTarget = ref.currentTarget; - if (target === currentTarget) { - dialog.$destroy(true); - } - }); - return dialog; - }; - UIkit.modal.alert = function(message, options) { - options = assign({ - bgClose: false, - escClose: false, - labels: UIkit.modal.labels - }, options); - return new Promise(function(resolve) { - return on(UIkit.modal.dialog('
    ' + (isString(message) ? message : html(message)) + '
    ", options).$el, "hide", resolve); - }); - }; - UIkit.modal.confirm = function(message, options) { - options = assign({ - bgClose: false, - escClose: true, - labels: UIkit.modal.labels - }, options); - return new Promise(function(resolve, reject) { - var confirm = UIkit.modal.dialog('
    ' + (isString(message) ? message : html(message)) + '
    ", options); - var resolved = false; - on(confirm.$el, "submit", "form", function(e) { - e.preventDefault(); - resolve(); - resolved = true; - confirm.hide(); - }); - on(confirm.$el, "hide", function() { - if (!resolved) { - reject(); - } - }); - }); - }; - UIkit.modal.prompt = function(message, value, options) { - options = assign({ - bgClose: false, - escClose: true, - labels: UIkit.modal.labels - }, options); - return new Promise(function(resolve) { - var prompt = UIkit.modal.dialog('
    ", options), input = $("input", prompt.$el); - input.value = value; - var resolved = false; - on(prompt.$el, "submit", "form", function(e) { - e.preventDefault(); - resolve(input.value); - resolved = true; - prompt.hide(); - }); - on(prompt.$el, "hide", function() { - if (!resolved) { - resolve(null); - } - }); - }); - }; - UIkit.modal.labels = { - ok: "Ok", - cancel: "Cancel" - }; - } - function Nav(UIkit) { - UIkit.component("nav", UIkit.components.accordion.extend({ - name: "nav", - defaults: { - targets: "> .uk-parent", - toggle: "> a", - content: "> ul" - } - })); - } - function Navbar(UIkit) { - UIkit.component("navbar", { - mixins: [ Class ], - props: { - dropdown: String, - mode: "list", - align: String, - offset: Number, - boundary: Boolean, - boundaryAlign: Boolean, - clsDrop: String, - delayShow: Number, - delayHide: Number, - dropbar: Boolean, - dropbarMode: String, - dropbarAnchor: "query", - duration: Number - }, - defaults: { - dropdown: ".uk-navbar-nav > li", - align: !isRtl ? "left" : "right", - clsDrop: "uk-navbar-dropdown", - mode: undefined, - offset: undefined, - delayShow: undefined, - delayHide: undefined, - boundaryAlign: undefined, - flip: "x", - boundary: true, - dropbar: false, - dropbarMode: "slide", - dropbarAnchor: false, - duration: 200 - }, - computed: { - boundary: function boundary(ref, $el) { - var boundary = ref.boundary; - var boundaryAlign = ref.boundaryAlign; - return boundary === true || boundaryAlign ? $el : boundary; - }, - pos: function pos(ref) { - var align = ref.align; - return "bottom-" + align; - } - }, - beforeConnect: function beforeConnect() { - var ref = this.$props; - var dropbar = ref.dropbar; - this.dropbar = dropbar && (isString(dropbar) && query(dropbar, this.$el) || $("
    ")); - if (this.dropbar) { - addClass(this.dropbar, "uk-navbar-dropbar"); - if (this.dropbarMode === "slide") { - addClass(this.dropbar, "uk-navbar-dropbar-slide"); - } - } - }, - disconnected: function disconnected() { - this.dropbar && remove(this.dropbar); - }, - update: function update() { - UIkit.drop($$(this.dropdown + " ." + this.clsDrop, this.$el).filter(function(el) { - return !UIkit.getComponent(el, "drop") && !UIkit.getComponent(el, "dropdown"); - }), assign({}, this.$props, { - boundary: this.boundary, - pos: this.pos, - offset: this.dropbar || this.offset - })); - }, - events: [ { - name: "mouseover", - delegate: function delegate() { - return this.dropdown; - }, - handler: function handler(ref) { - var current = ref.current; - var active = this.getActive(); - if (active && active.toggle && !within(active.toggle.$el, current) && !active.tracker.movesTo(active.$el)) { - active.hide(false); - } - } - }, { - name: "mouseleave", - el: function el() { - return this.dropbar; - }, - handler: function handler() { - var active = this.getActive(); - if (active && !matches(this.dropbar, ":hover")) { - active.hide(); - } - } - }, { - name: "beforeshow", - capture: true, - filter: function filter() { - return this.dropbar; - }, - handler: function handler() { - if (!this.dropbar.parentNode) { - after(this.dropbarAnchor || this.$el, this.dropbar); - } - } - }, { - name: "show", - capture: true, - filter: function filter() { - return this.dropbar; - }, - handler: function handler(_, drop) { - var $el = drop.$el; - var dir = drop.dir; - this.clsDrop && addClass($el, this.clsDrop + "-dropbar"); - if (dir === "bottom") { - this.transitionTo($el.offsetHeight + toFloat(css($el, "marginTop")) + toFloat(css($el, "marginBottom")), $el); - } - } - }, { - name: "beforehide", - filter: function filter() { - return this.dropbar; - }, - handler: function handler(e, ref) { - var $el = ref.$el; - var active = this.getActive(); - if (matches(this.dropbar, ":hover") && active && active.$el === $el) { - e.preventDefault(); - } - } - }, { - name: "hide", - filter: function filter() { - return this.dropbar; - }, - handler: function handler(_, ref) { - var $el = ref.$el; - var active = this.getActive(); - if (!active || active && active.$el === $el) { - this.transitionTo(0); - } - } - } ], - methods: { - getActive: function getActive() { - var active = UIkit.drop.getActive(); - return active && includes(active.mode, "hover") && within(active.toggle.$el, this.$el) && active; - }, - transitionTo: function transitionTo(newHeight, el) { - var ref = this; - var dropbar = ref.dropbar; - var oldHeight = isVisible(dropbar) ? height(dropbar) : 0; - el = oldHeight < newHeight && el; - css(el, { - height: oldHeight, - overflow: "hidden" - }); - height(dropbar, oldHeight); - Transition.cancel([ el, dropbar ]); - return Transition.start([ el, dropbar ], { - height: newHeight - }, this.duration).catch(noop).then(function() { - return css(el, { - height: "", - overflow: "" - }); - }); - } - } - }); - } - var scroll; - function Offcanvas(UIkit) { - UIkit.component("offcanvas", { - mixins: [ Modal ], - args: "mode", - props: { - content: String, - mode: String, - flip: Boolean, - overlay: Boolean - }, - defaults: { - content: ".uk-offcanvas-content", - mode: "slide", - flip: false, - overlay: false, - clsPage: "uk-offcanvas-page", - clsContainer: "uk-offcanvas-container", - selPanel: ".uk-offcanvas-bar", - clsFlip: "uk-offcanvas-flip", - clsContent: "uk-offcanvas-content", - clsContentAnimation: "uk-offcanvas-content-animation", - clsSidebarAnimation: "uk-offcanvas-bar-animation", - clsMode: "uk-offcanvas", - clsOverlay: "uk-offcanvas-overlay", - selClose: ".uk-offcanvas-close" - }, - computed: { - content: function content(ref) { - var content = ref.content; - return $(content) || document.body; - }, - clsFlip: function clsFlip(ref) { - var flip = ref.flip; - var clsFlip = ref.clsFlip; - return flip ? clsFlip : ""; - }, - clsOverlay: function clsOverlay(ref) { - var overlay = ref.overlay; - var clsOverlay = ref.clsOverlay; - return overlay ? clsOverlay : ""; - }, - clsMode: function clsMode(ref) { - var mode = ref.mode; - var clsMode = ref.clsMode; - return clsMode + "-" + mode; - }, - clsSidebarAnimation: function clsSidebarAnimation(ref) { - var mode = ref.mode; - var clsSidebarAnimation = ref.clsSidebarAnimation; - return mode === "none" || mode === "reveal" ? "" : clsSidebarAnimation; - }, - clsContentAnimation: function clsContentAnimation(ref) { - var mode = ref.mode; - var clsContentAnimation = ref.clsContentAnimation; - return mode !== "push" && mode !== "reveal" ? "" : clsContentAnimation; - }, - transitionElement: function transitionElement(ref) { - var mode = ref.mode; - return mode === "reveal" ? this.panel.parentNode : this.panel; - } - }, - update: { - write: function write() { - if (this.getActive() === this) { - if (this.overlay || this.clsContentAnimation) { - width(this.content, width(window) - this.scrollbarWidth); - } - if (this.overlay) { - height(this.content, height(window)); - if (scroll) { - this.content.scrollTop = scroll.y; - } - } - } - }, - events: [ "resize" ] - }, - events: [ { - name: "click", - delegate: function delegate() { - return 'a[href^="#"]'; - }, - handler: function handler(ref) { - var current = ref.current; - if (current.hash && $(current.hash, this.content)) { - scroll = null; - this.hide(); - } - } - }, { - name: "beforescroll", - filter: function filter() { - return this.overlay; - }, - handler: function handler(e, scroll, target) { - if (scroll && target && this.isToggled() && $(target, this.content)) { - once(this.$el, "hidden", function() { - return scroll.scrollTo(target); - }); - e.preventDefault(); - } - } - }, { - name: "show", - self: true, - handler: function handler() { - scroll = scroll || { - x: window.pageXOffset, - y: window.pageYOffset - }; - if (this.mode === "reveal" && !hasClass(this.panel, this.clsMode)) { - wrapAll(this.panel, "
    "); - addClass(this.panel.parentNode, this.clsMode); - } - css(document.documentElement, "overflowY", (!this.clsContentAnimation || this.flip) && this.scrollbarWidth && this.overlay ? "scroll" : ""); - addClass(document.body, this.clsContainer, this.clsFlip, this.clsOverlay); - height(document.body); - addClass(this.content, this.clsContentAnimation); - addClass(this.panel, this.clsSidebarAnimation, this.mode !== "reveal" ? this.clsMode : ""); - addClass(this.$el, this.clsOverlay); - css(this.$el, "display", "block"); - height(this.$el); - } - }, { - name: "hide", - self: true, - handler: function handler() { - removeClass(this.content, this.clsContentAnimation); - var active = this.getActive(); - if (this.mode === "none" || active && active !== this && active !== this.prev) { - trigger(this.panel, "transitionend"); - } - } - }, { - name: "hidden", - self: true, - handler: function handler() { - if (this.mode === "reveal") { - unwrap(this.panel); - } - if (!this.overlay) { - scroll = { - x: window.pageXOffset, - y: window.pageYOffset - }; - } else if (!scroll) { - var ref = this.content; - var x = ref.scrollLeft; - var y = ref.scrollTop; - scroll = { - x: x, - y: y - }; - } - removeClass(this.panel, this.clsSidebarAnimation, this.clsMode); - removeClass(this.$el, this.clsOverlay); - css(this.$el, "display", ""); - removeClass(document.body, this.clsContainer, this.clsFlip, this.clsOverlay); - document.body.scrollTop = scroll.y; - css(document.documentElement, "overflowY", ""); - width(this.content, ""); - height(this.content, ""); - window.scrollTo(scroll.x, scroll.y); - scroll = null; - } - }, { - name: "swipeLeft swipeRight", - handler: function handler(e) { - if (this.isToggled() && isTouch(e) && (e.type === "swipeLeft" && !this.flip || e.type === "swipeRight" && this.flip)) { - this.hide(); - } - } - } ] - }); - } - function Responsive(UIkit) { - UIkit.component("responsive", { - props: [ "width", "height" ], - init: function init() { - addClass(this.$el, "uk-responsive-width"); - }, - update: { - read: function read() { - return isVisible(this.$el) && this.width && this.height ? { - width: width(this.$el.parentNode), - height: this.height - } : false; - }, - write: function write(dim) { - height(this.$el, Dimensions.contain({ - height: this.height, - width: this.width - }, dim).height); - }, - events: [ "load", "resize" ] - } - }); - } - function Scroll(UIkit) { - UIkit.component("scroll", { - props: { - duration: Number, - offset: Number - }, - defaults: { - duration: 1e3, - offset: 0 - }, - methods: { - scrollTo: function scrollTo(el) { - var this$1 = this; - el = el && $(el) || document.body; - var docHeight = height(document); - var winHeight = height(window); - var target = offset(el).top - this.offset; - if (target + winHeight > docHeight) { - target = docHeight - winHeight; - } - if (!trigger(this.$el, "beforescroll", [ this, el ])) { - return; - } - var start = Date.now(); - var startY = window.pageYOffset; - var step = function() { - var currentY = startY + (target - startY) * ease(clamp((Date.now() - start) / this$1.duration)); - window.scrollTo(window.pageXOffset, currentY); - if (currentY !== target) { - requestAnimationFrame(step); - } else { - trigger(this$1.$el, "scrolled", [ this$1, el ]); - } - }; - step(); - } - }, - events: { - click: function click(e) { - if (e.defaultPrevented) { - return; - } - e.preventDefault(); - this.scrollTo(escape(this.$el.hash).substr(1)); - } - } - }); - function ease(k) { - return .5 * (1 - Math.cos(Math.PI * k)); - } - } - function Scrollspy(UIkit) { - UIkit.component("scrollspy", { - args: "cls", - props: { - cls: "list", - target: String, - hidden: Boolean, - offsetTop: Number, - offsetLeft: Number, - repeat: Boolean, - delay: Number - }, - defaults: { - cls: [], - target: false, - hidden: true, - offsetTop: 0, - offsetLeft: 0, - repeat: false, - delay: 0, - inViewClass: "uk-scrollspy-inview" - }, - computed: { - elements: function elements(ref, $el) { - var target = ref.target; - return target ? $$(target, $el) : [ $el ]; - } - }, - update: [ { - write: function write() { - if (this.hidden) { - css(filter(this.elements, ":not(." + this.inViewClass + ")"), "visibility", "hidden"); - } - } - }, { - read: function read(els) { - var this$1 = this; - if (!UIkit._initialized) { - if (document.readyState === "complete") { - requestAnimationFrame(function() { - return this$1.$emit(); - }); - } - return false; - } - this.elements.forEach(function(el, i) { - var elData = els[i]; - if (!elData || elData.el !== el) { - var cls = data(el, "uk-scrollspy-class"); - elData = { - el: el, - toggles: cls && cls.split(",") || this$1.cls - }; - } - elData.show = isInView(el, this$1.offsetTop, this$1.offsetLeft); - els[i] = elData; - }); - }, - write: function write(els) { - var this$1 = this; - var index = this.elements.length === 1 ? 1 : 0; - this.elements.forEach(function(el, i) { - var elData = els[i]; - var cls = elData.toggles[i] || elData.toggles[0]; - if (elData.show && !elData.inview && !elData.timer) { - var show = function() { - css(el, "visibility", ""); - addClass(el, this$1.inViewClass); - toggleClass(el, cls); - trigger(el, "inview"); - UIkit.update(el); - elData.inview = true; - delete elData.timer; - }; - if (this$1.delay && index) { - elData.timer = setTimeout(show, this$1.delay * index); - } else { - show(); - } - index++; - } else if (!elData.show && elData.inview && this$1.repeat) { - if (elData.timer) { - clearTimeout(elData.timer); - delete elData.timer; - } - css(el, "visibility", this$1.hidden ? "hidden" : ""); - removeClass(el, this$1.inViewClass); - toggleClass(el, cls); - trigger(el, "outview"); - UIkit.update(el); - elData.inview = false; - } - }); - }, - events: [ "scroll", "load", "resize" ] - } ] - }); - } - function ScrollspyNav(UIkit) { - UIkit.component("scrollspy-nav", { - props: { - cls: String, - closest: String, - scroll: Boolean, - overflow: Boolean, - offset: Number - }, - defaults: { - cls: "uk-active", - closest: false, - scroll: false, - overflow: true, - offset: 0 - }, - computed: { - links: function links(_, $el) { - return $$('a[href^="#"]', $el).filter(function(el) { - return el.hash; - }); - }, - elements: function elements() { - return this.closest ? closest(this.links, this.closest) : this.links; - }, - targets: function targets() { - return $$(this.links.map(function(el) { - return el.hash; - }).join(",")); - } - }, - update: [ { - read: function read() { - if (this.scroll) { - UIkit.scroll(this.links, { - offset: this.offset || 0 - }); - } - } - }, { - read: function read(data) { - var this$1 = this; - var scroll = window.pageYOffset + this.offset + 1; - var max = height(document) - height(window) + this.offset; - data.active = false; - this.targets.every(function(el, i) { - var ref = offset(el); - var top = ref.top; - var last = i + 1 === this$1.targets.length; - if (!this$1.overflow && (i === 0 && top > scroll || last && top + el.offsetTop < scroll)) { - return false; - } - if (!last && offset(this$1.targets[i + 1]).top <= scroll) { - return true; - } - if (scroll >= max) { - for (var j = this$1.targets.length - 1; j > i; j--) { - if (isInView(this$1.targets[j])) { - el = this$1.targets[j]; - break; - } - } - } - return !(data.active = $(filter(this$1.links, '[href="#' + el.id + '"]'))); - }); - }, - write: function write(ref) { - var active = ref.active; - this.links.forEach(function(el) { - return el.blur(); - }); - removeClass(this.elements, this.cls); - if (active) { - trigger(this.$el, "active", [ active, addClass(this.closest ? closest(active, this.closest) : active, this.cls) ]); - } - }, - events: [ "scroll", "load", "resize" ] - } ] - }); - } - function Sticky(UIkit) { - UIkit.component("sticky", { - mixins: [ Class ], - attrs: true, - props: { - top: null, - bottom: Boolean, - offset: Number, - animation: String, - clsActive: String, - clsInactive: String, - clsFixed: String, - clsBelow: String, - selTarget: String, - widthElement: "query", - showOnUp: Boolean, - media: "media", - target: Number - }, - defaults: { - top: 0, - bottom: false, - offset: 0, - animation: "", - clsActive: "uk-active", - clsInactive: "", - clsFixed: "uk-sticky-fixed", - clsBelow: "uk-sticky-below", - selTarget: "", - widthElement: false, - showOnUp: false, - media: false, - target: false - }, - computed: { - selTarget: function selTarget(ref, $el) { - var selTarget = ref.selTarget; - return selTarget && $(selTarget, $el) || $el; - } - }, - connected: function connected() { - this.placeholder = $('
    '); - this.widthElement = this.$props.widthElement || this.placeholder; - if (!this.isActive) { - this.hide(); - } - }, - disconnected: function disconnected() { - if (this.isActive) { - this.isActive = false; - this.hide(); - removeClass(this.selTarget, this.clsInactive); - } - remove(this.placeholder); - this.placeholder = null; - this.widthElement = null; - }, - ready: function ready() { - var this$1 = this; - if (!(this.target && location.hash && window.pageYOffset > 0)) { - return; - } - var target = $(location.hash); - if (target) { - fastdom.read(function() { - var ref = offset(target); - var top = ref.top; - var elTop = offset(this$1.$el).top; - var elHeight = this$1.$el.offsetHeight; - if (elTop + elHeight >= top && elTop <= top + target.offsetHeight) { - window.scrollTo(0, top - elHeight - this$1.target - this$1.offset); - } - }); - } - }, - events: [ { - name: "active", - self: true, - handler: function handler() { - replaceClass(this.selTarget, this.clsInactive, this.clsActive); - } - }, { - name: "inactive", - self: true, - handler: function handler() { - replaceClass(this.selTarget, this.clsActive, this.clsInactive); - } - } ], - update: [ { - write: function write() { - var ref = this; - var placeholder = ref.placeholder; - var outerHeight = (this.isActive ? placeholder : this.$el).offsetHeight; - css(placeholder, assign({ - height: css(this.$el, "position") !== "absolute" ? outerHeight : "" - }, css(this.$el, [ "marginTop", "marginBottom", "marginLeft", "marginRight" ]))); - if (!within(placeholder, document)) { - after(this.$el, placeholder); - attr(placeholder, "hidden", ""); - } - attr(this.widthElement, "hidden", null); - this.width = this.widthElement.offsetWidth; - attr(this.widthElement, "hidden", this.isActive ? null : ""); - this.topOffset = offset(this.isActive ? placeholder : this.$el).top; - this.bottomOffset = this.topOffset + outerHeight; - var bottom = parseProp("bottom", this); - this.top = Math.max(toFloat(parseProp("top", this)), this.topOffset) - this.offset; - this.bottom = bottom && bottom - outerHeight; - this.inactive = this.media && !window.matchMedia(this.media).matches; - if (this.isActive) { - this.update(); - } - }, - events: [ "load", "resize" ] - }, { - read: function read(_, ref) { - var scrollY = ref.scrollY; - if (scrollY === void 0) scrollY = window.pageYOffset; - return { - scroll: this.scroll = scrollY, - visible: isVisible(this.$el) - }; - }, - write: function write(ref, ref$1) { - var this$1 = this; - var visible = ref.visible; - var scroll = ref.scroll; - if (ref$1 === void 0) ref$1 = {}; - var dir = ref$1.dir; - if (scroll < 0 || !visible || this.disabled || this.showOnUp && !dir) { - return; - } - if (this.inactive || scroll < this.top || this.showOnUp && (scroll <= this.top || dir === "down" || dir === "up" && !this.isActive && scroll <= this.bottomOffset)) { - if (!this.isActive) { - return; - } - this.isActive = false; - if (this.animation && scroll > this.topOffset) { - Animation.cancel(this.$el); - Animation.out(this.$el, this.animation).then(function() { - return this$1.hide(); - }, noop); - } else { - this.hide(); - } - } else if (this.isActive) { - this.update(); - } else if (this.animation) { - Animation.cancel(this.$el); - this.show(); - Animation.in(this.$el, this.animation).catch(noop); - } else { - this.show(); - } - }, - events: [ "scroll" ] - } ], - methods: { - show: function show() { - this.isActive = true; - this.update(); - attr(this.placeholder, "hidden", null); - }, - hide: function hide() { - if (!this.isActive || hasClass(this.selTarget, this.clsActive)) { - trigger(this.$el, "inactive"); - } - removeClass(this.$el, this.clsFixed, this.clsBelow); - css(this.$el, { - position: "", - top: "", - width: "" - }); - attr(this.placeholder, "hidden", ""); - }, - update: function update() { - var active = this.top !== 0 || this.scroll > this.top; - var top = Math.max(0, this.offset); - if (this.bottom && this.scroll > this.bottom - this.offset) { - top = this.bottom - this.scroll; - } - css(this.$el, { - position: "fixed", - top: top + "px", - width: this.width - }); - if (hasClass(this.selTarget, this.clsActive)) { - if (!active) { - trigger(this.$el, "inactive"); - } - } else if (active) { - trigger(this.$el, "active"); - } - toggleClass(this.$el, this.clsBelow, this.scroll > this.bottomOffset); - addClass(this.$el, this.clsFixed); - } - } - }); - function parseProp(prop, ref) { - var $props = ref.$props; - var $el = ref.$el; - var propOffset = ref[prop + "Offset"]; - var value = $props[prop]; - if (!value) { - return; - } - if (isNumeric(value)) { - return propOffset + toFloat(value); - } else if (isString(value) && value.match(/^-?\d+vh$/)) { - return height(window) * toFloat(value) / 100; - } else { - var el = value === true ? $el.parentNode : query(value, $el); - if (el) { - return offset(el).top + el.offsetHeight; - } - } - } - } - var svgs = {}; - function Svg(UIkit) { - UIkit.component("svg", { - attrs: true, - props: { - id: String, - icon: String, - src: String, - style: String, - width: Number, - height: Number, - ratio: Number, - class: String - }, - defaults: { - ratio: 1, - id: false, - exclude: [ "src" ], - class: "" - }, - init: function init() { - this.class += " uk-svg"; - }, - connected: function connected() { - var this$1 = this; - if (!this.icon && includes(this.src, "#")) { - var parts = this.src.split("#"); - if (parts.length > 1) { - var assign; - assign = parts, this.src = assign[0], this.icon = assign[1]; - } - } - this.svg = this.getSvg().then(function(svg) { - var el; - if (isString(svg)) { - if (this$1.icon && includes(svg, "/g; - var symbols = {}; - function parseSymbols(svg, icon) { - if (!symbols[svg]) { - symbols[svg] = {}; - var match; - while (match = symbolRe.exec(svg)) { - symbols[svg][match[3]] = '"; - } - } - return symbols[svg][icon]; - } - } - function Switcher(UIkit) { - UIkit.component("switcher", { - mixins: [ Togglable ], - args: "connect", - props: { - connect: String, - toggle: String, - active: Number, - swiping: Boolean - }, - defaults: { - connect: "~.uk-switcher", - toggle: "> *", - active: 0, - swiping: true, - cls: "uk-active", - clsContainer: "uk-switcher", - attrItem: "uk-switcher-item", - queued: true - }, - computed: { - connects: function connects(ref, $el) { - var connect = ref.connect; - return queryAll(connect, $el); - }, - toggles: function toggles(ref, $el) { - var toggle = ref.toggle; - return $$(toggle, $el); - } - }, - events: [ { - name: "click", - delegate: function delegate() { - return this.toggle + ":not(.uk-disabled)"; - }, - handler: function handler(e) { - e.preventDefault(); - this.show(e.current); - } - }, { - name: "click", - el: function el() { - return this.connects; - }, - delegate: function delegate() { - return "[" + this.attrItem + "],[data-" + this.attrItem + "]"; - }, - handler: function handler(e) { - e.preventDefault(); - this.show(data(e.current, this.attrItem)); - } - }, { - name: "swipeRight swipeLeft", - filter: function filter() { - return this.swiping; - }, - el: function el() { - return this.connects; - }, - handler: function handler(e) { - if (!isTouch(e)) { - return; - } - e.preventDefault(); - if (!window.getSelection().toString()) { - this.show(e.type === "swipeLeft" ? "next" : "previous"); - } - } - } ], - update: function update() { - var this$1 = this; - this.connects.forEach(function(list) { - return this$1.updateAria(list.children); - }); - this.show(filter(this.toggles, "." + this.cls)[0] || this.toggles[this.active] || this.toggles[0]); - }, - methods: { - show: function show(item) { - var this$1 = this; - var ref = this.toggles; - var length = ref.length; - var prev = !!this.connects.length && index(filter(this.connects[0].children, "." + this.cls)[0]); - var hasPrev = prev >= 0; - var dir = item === "previous" ? -1 : 1; - var toggle, next = getIndex(item, this.toggles, prev); - for (var i = 0; i < length; i++, next = (next + dir + length) % length) { - if (!matches(this$1.toggles[next], ".uk-disabled, [disabled]")) { - toggle = this$1.toggles[next]; - break; - } - } - if (!toggle || prev >= 0 && hasClass(toggle, this.cls) || prev === next) { - return; - } - removeClass(this.toggles, this.cls); - attr(this.toggles, "aria-expanded", false); - addClass(toggle, this.cls); - attr(toggle, "aria-expanded", true); - this.connects.forEach(function(list) { - if (!hasPrev) { - this$1.toggleNow(list.children[next]); - } else { - this$1.toggleElement([ list.children[prev], list.children[next] ]); - } - }); - } - } - }); - } - function Tab(UIkit) { - UIkit.component("tab", UIkit.components.switcher.extend({ - mixins: [ Class ], - name: "tab", - props: { - media: "media" - }, - defaults: { - media: 960, - attrItem: "uk-tab-item" - }, - init: function init() { - var cls = hasClass(this.$el, "uk-tab-left") ? "uk-tab-left" : hasClass(this.$el, "uk-tab-right") ? "uk-tab-right" : false; - if (cls) { - UIkit.toggle(this.$el, { - cls: cls, - mode: "media", - media: this.media - }); - } - } - })); - } - function Toggle(UIkit) { - UIkit.component("toggle", { - mixins: [ UIkit.mixin.togglable ], - args: "target", - props: { - href: String, - target: null, - mode: "list", - media: "media" - }, - defaults: { - href: false, - target: false, - mode: "click", - queued: true, - media: false - }, - computed: { - target: function target(ref, $el) { - var href = ref.href; - var target = ref.target; - target = queryAll(target || href, $el); - return target.length && target || [ $el ]; - } - }, - events: [ { - name: pointerEnter + " " + pointerLeave, - filter: function filter() { - return includes(this.mode, "hover"); - }, - handler: function handler(e) { - if (!isTouch(e)) { - this.toggle("toggle" + (e.type === pointerEnter ? "show" : "hide")); - } - } - }, { - name: "click", - filter: function filter() { - return includes(this.mode, "click") || hasTouch; - }, - handler: function handler(e) { - if (!isTouch(e) && !includes(this.mode, "click")) { - return; - } - var link; - if (closest(e.target, 'a[href="#"], button') || (link = closest(e.target, "a[href]")) && (this.cls || !isVisible(this.target) || link.hash && matches(this.target, link.hash))) { - once(document, "click", function(e) { - return e.preventDefault(); - }); - } - this.toggle(); - } - } ], - update: { - write: function write() { - if (!includes(this.mode, "media") || !this.media) { - return; - } - var toggled = this.isToggled(this.target); - if (window.matchMedia(this.media).matches ? !toggled : toggled) { - this.toggle(); - } - }, - events: [ "load", "resize" ] - }, - methods: { - toggle: function toggle(type) { - if (trigger(this.target, type || "toggle", [ this ])) { - this.toggleElement(this.target); - } - } - } - }); - } - function Video(UIkit) { - UIkit.component("video", { - args: "autoplay", - props: { - automute: Boolean, - autoplay: Boolean - }, - defaults: { - automute: false, - autoplay: true - }, - computed: { - inView: function inView(ref) { - var autoplay = ref.autoplay; - return autoplay === "inview"; - } - }, - connected: function connected() { - if (this.inView && !hasAttr(this.$el, "preload")) { - this.$el.preload = "none"; - } - }, - ready: function ready() { - this.player = new Player(this.$el); - if (this.automute) { - this.player.mute(); - } - }, - update: [ { - read: function read(_, ref) { - var type = ref.type; - return !this.player || (type === "scroll" || type === "resize") && !this.inView ? false : { - visible: isVisible(this.$el) && css(this.$el, "visibility") !== "hidden", - inView: this.inView && isInView(this.$el) - }; - }, - write: function write(ref) { - var visible = ref.visible; - var inView = ref.inView; - if (!visible || this.inView && !inView) { - this.player.pause(); - } else if (this.autoplay === true || this.inView && inView) { - this.player.play(); - } - }, - events: [ "load", "resize", "scroll" ] - } ] - }); - } - function core(UIkit) { - UIkit.use(Toggle); - UIkit.use(Accordion); - UIkit.use(Alert); - UIkit.use(Video); - UIkit.use(Cover); - UIkit.use(Drop); - UIkit.use(Dropdown); - UIkit.use(FormCustom); - UIkit.use(HeightMatch); - UIkit.use(HeightViewport); - UIkit.use(Margin); - UIkit.use(Gif); - UIkit.use(Grid); - UIkit.use(Leader); - UIkit.use(Modal$1); - UIkit.use(Nav); - UIkit.use(Navbar); - UIkit.use(Offcanvas); - UIkit.use(Responsive); - UIkit.use(Scroll); - UIkit.use(Scrollspy); - UIkit.use(ScrollspyNav); - UIkit.use(Sticky); - UIkit.use(Svg); - UIkit.use(Icon); - UIkit.use(Switcher); - UIkit.use(Tab); - UIkit.use(Core); - } - UIkit$2.version = "3.0.0-beta.42"; - mixin(UIkit$2); - core(UIkit$2); - function plugin(UIkit) { - if (plugin.installed) { - return; - } - var ref = UIkit.util; - var $ = ref.$; - var empty = ref.empty; - var html = ref.html; - UIkit.component("countdown", { - mixins: [ UIkit.mixin.class ], - attrs: true, - props: { - date: String, - clsWrapper: String - }, - defaults: { - date: "", - clsWrapper: ".uk-countdown-%unit%" - }, - computed: { - date: function date(ref) { - var date = ref.date; - return Date.parse(date); - }, - days: function days(ref, $el) { - var clsWrapper = ref.clsWrapper; - return $(clsWrapper.replace("%unit%", "days"), $el); - }, - hours: function hours(ref, $el) { - var clsWrapper = ref.clsWrapper; - return $(clsWrapper.replace("%unit%", "hours"), $el); - }, - minutes: function minutes(ref, $el) { - var clsWrapper = ref.clsWrapper; - return $(clsWrapper.replace("%unit%", "minutes"), $el); - }, - seconds: function seconds(ref, $el) { - var clsWrapper = ref.clsWrapper; - return $(clsWrapper.replace("%unit%", "seconds"), $el); - }, - units: function units() { - var this$1 = this; - return [ "days", "hours", "minutes", "seconds" ].filter(function(unit) { - return this$1[unit]; - }); - } - }, - connected: function connected() { - this.start(); - }, - disconnected: function disconnected() { - var this$1 = this; - this.stop(); - this.units.forEach(function(unit) { - return empty(this$1[unit]); - }); - }, - events: [ { - name: "visibilitychange", - el: document, - handler: function handler() { - if (document.hidden) { - this.stop(); - } else { - this.start(); - } - } - } ], - update: { - write: function write() { - var this$1 = this; - var timespan = getTimeSpan(this.date); - if (timespan.total <= 0) { - this.stop(); - timespan.days = timespan.hours = timespan.minutes = timespan.seconds = 0; - } - this.units.forEach(function(unit) { - var digits = String(Math.floor(timespan[unit])); - digits = digits.length < 2 ? "0" + digits : digits; - var el = this$1[unit]; - if (el.textContent !== digits) { - digits = digits.split(""); - if (digits.length !== el.children.length) { - html(el, digits.map(function() { - return ""; - }).join("")); - } - digits.forEach(function(digit, i) { - return el.children[i].textContent = digit; - }); - } - }); - } - }, - methods: { - start: function start() { - var this$1 = this; - this.stop(); - if (this.date && this.units.length) { - this.$emit(); - this.timer = setInterval(function() { - return this$1.$emit(); - }, 1e3); - } - }, - stop: function stop() { - if (this.timer) { - clearInterval(this.timer); - this.timer = null; - } - } - } - }); - function getTimeSpan(date) { - var total = date - Date.now(); - return { - total: total, - seconds: total / 1e3 % 60, - minutes: total / 1e3 / 60 % 60, - hours: total / 1e3 / 60 / 60 % 24, - days: total / 1e3 / 60 / 60 / 24 - }; - } - } - function plugin$1(UIkit) { - if (plugin$1.installed) { - return; - } - var ref = UIkit.util; - var addClass = ref.addClass; - var css = ref.css; - var scrolledOver = ref.scrolledOver; - var sortBy = ref.sortBy; - var toFloat = ref.toFloat; - UIkit.component("grid-parallax", UIkit.components.grid.extend({ - props: { - target: String, - translate: Number - }, - defaults: { - target: false, - translate: 150 - }, - computed: { - translate: function translate(ref) { - var translate = ref.translate; - return Math.abs(translate); - } - }, - init: function init() { - addClass(this.$el, "uk-grid"); - }, - disconnected: function disconnected() { - this.reset(); - css(this.$el, "marginBottom", ""); - }, - update: [ { - read: function read(ref) { - var rows = ref.rows; - return { - columns: rows && rows[0] && rows[0].length || 0, - rows: rows && rows.map(function(elements) { - return sortBy(elements, "offsetLeft"); - }) - }; - }, - write: function write(ref) { - var columns = ref.columns; - css(this.$el, "marginBottom", columns > 1 ? this.translate + toFloat(css(css(this.$el, "marginBottom", ""), "marginBottom")) : ""); - }, - events: [ "load", "resize" ] - }, { - read: function read() { - return { - scrolled: scrolledOver(this.$el) * this.translate - }; - }, - write: function write(ref) { - var rows = ref.rows; - var columns = ref.columns; - var scrolled = ref.scrolled; - if (!rows || columns === 1 || !scrolled) { - return this.reset(); - } - rows.forEach(function(row) { - return row.forEach(function(el, i) { - return css(el, "transform", "translateY(" + (i % 2 ? scrolled : scrolled / 8) + "px)"); - }); - }); - }, - events: [ "scroll", "load", "resize" ] - } ], - methods: { - reset: function reset() { - css(this.$el.children, "transform", ""); - } - } - })); - UIkit.components.gridParallax.options.update.unshift({ - read: function read() { - this.reset(); - }, - events: [ "load", "resize" ] - }); - } - function AnimationsPlugin(UIkit) { - var ref = UIkit.util; - var css = ref.css; - var Animations = { - slide: { - show: function show(dir) { - return [ { - transform: translate(dir * -100) - }, { - transform: translate() - } ]; - }, - percent: function percent(current) { - return Animations.translated(current); - }, - translate: function translate$1(percent, dir) { - return [ { - transform: translate(dir * -100 * percent) - }, { - transform: translate(dir * 100 * (1 - percent)) - } ]; - } - }, - translated: function translated(el) { - return Math.abs(css(el, "transform").split(",")[4] / el.offsetWidth) || 0; - } - }; - return Animations; - } - function translate(value, unit) { - if (value === void 0) value = 0; - if (unit === void 0) unit = "%"; - return "translateX(" + value + (value ? unit : "") + ")"; - } - function scale3d(value) { - return "scale3d(" + value + ", " + value + ", 1)"; - } - function TransitionerPlugin(UIkit) { - var ref = UIkit.util; - var createEvent = ref.createEvent; - var clamp = ref.clamp; - var css = ref.css; - var Deferred = ref.Deferred; - var noop = ref.noop; - var Promise = ref.Promise; - var Transition = ref.Transition; - var trigger = ref.trigger; - function Transitioner(prev, next, dir, ref) { - var animation = ref.animation; - var easing = ref.easing; - var percent = animation.percent; - var translate = animation.translate; - var show = animation.show; - if (show === void 0) show = noop; - var props = show(dir); - var deferred = new Deferred(); - return { - dir: dir, - show: function show(duration, percent, linear) { - var this$1 = this; - if (percent === void 0) percent = 0; - var timing = linear ? "linear" : easing; - duration -= Math.round(duration * clamp(percent, -1, 1)); - this.translate(percent); - triggerUpdate(next, "itemin", { - percent: percent, - duration: duration, - timing: timing, - dir: dir - }); - triggerUpdate(prev, "itemout", { - percent: 1 - percent, - duration: duration, - timing: timing, - dir: dir - }); - Promise.all([ Transition.start(next, props[1], duration, timing), Transition.start(prev, props[0], duration, timing) ]).then(function() { - this$1.reset(); - deferred.resolve(); - }, noop); - return deferred.promise; - }, - stop: function stop() { - return Transition.stop([ next, prev ]); - }, - cancel: function cancel() { - Transition.cancel([ next, prev ]); - }, - reset: function reset() { - for (var prop in props[0]) { - css([ next, prev ], prop, ""); - } - }, - forward: function forward(duration, percent) { - if (percent === void 0) percent = this.percent(); - Transition.cancel([ next, prev ]); - return this.show(duration, percent, true); - }, - translate: function translate$1(percent) { - this.reset(); - var props = translate(percent, dir); - css(next, props[1]); - css(prev, props[0]); - triggerUpdate(next, "itemtranslatein", { - percent: percent, - dir: dir - }); - triggerUpdate(prev, "itemtranslateout", { - percent: 1 - percent, - dir: dir - }); - }, - percent: function percent$1() { - return percent(prev || next, next, dir); - }, - getDistance: function getDistance() { - return prev.offsetWidth; - } - }; - } - function triggerUpdate(el, type, data) { - trigger(el, createEvent(type, false, false, data)); - } - return Transitioner; - } - function AutoplayMixin(UIkit) { - var ref = UIkit.util; - var pointerDown = ref.pointerDown; - return { - props: { - autoplay: Boolean, - autoplayInterval: Number, - pauseOnHover: Boolean - }, - defaults: { - autoplay: false, - autoplayInterval: 7e3, - pauseOnHover: true - }, - connected: function connected() { - this.startAutoplay(); - }, - disconnected: function disconnected() { - this.stopAutoplay(); - }, - events: [ { - name: "visibilitychange", - el: document, - handler: function handler() { - if (document.hidden) { - this.stopAutoplay(); - } else { - this.startAutoplay(); - } - } - }, { - name: pointerDown, - handler: "stopAutoplay" - }, { - name: "mouseenter", - filter: function filter() { - return this.autoplay; - }, - handler: function handler() { - this.isHovering = true; - } - }, { - name: "mouseleave", - filter: function filter() { - return this.autoplay; - }, - handler: function handler() { - this.isHovering = false; - } - } ], - methods: { - startAutoplay: function startAutoplay() { - var this$1 = this; - this.stopAutoplay(); - if (this.autoplay) { - this.interval = setInterval(function() { - return !(this$1.isHovering && this$1.pauseOnHover) && !this$1.stack.length && this$1.show("next"); - }, this.autoplayInterval); - } - }, - stopAutoplay: function stopAutoplay() { - if (this.interval) { - clearInterval(this.interval); - } - } - } - }; - } - function DragMixin(UIkit) { - var ref = UIkit.util; - var getPos = ref.getPos; - var includes = ref.includes; - var isRtl = ref.isRtl; - var isTouch = ref.isTouch; - var off = ref.off; - var on = ref.on; - var pointerDown = ref.pointerDown; - var pointerMove = ref.pointerMove; - var pointerUp = ref.pointerUp; - var preventClick = ref.preventClick; - var trigger = ref.trigger; - return { - defaults: { - threshold: 10, - preventCatch: false - }, - init: function init() { - var this$1 = this; - [ "start", "move", "end" ].forEach(function(key) { - var fn = this$1[key]; - this$1[key] = function(e) { - var pos = getPos(e).x * (isRtl ? -1 : 1); - this$1.prevPos = pos !== this$1.pos ? this$1.pos : this$1.prevPos; - this$1.pos = pos; - fn(e); - }; - }); - }, - events: [ { - name: pointerDown, - delegate: function delegate() { - return this.slidesSelector; - }, - handler: function handler(e) { - if (!isTouch(e) && hasTextNodesOnly(e.target) || e.button > 0 || this.length < 2 || this.preventCatch) { - return; - } - this.start(e); - } - }, { - name: "dragstart", - handler: function handler(e) { - e.preventDefault(); - } - } ], - methods: { - start: function start() { - this.drag = this.pos; - if (this._transitioner) { - this.percent = this._transitioner.percent(); - this.drag += this._transitioner.getDistance() * this.percent * this.dir; - this._transitioner.translate(this.percent); - this._transitioner.cancel(); - this.dragging = true; - this.stack = []; - } else { - this.prevIndex = this.index; - } - this.unbindMove = on(document, pointerMove, this.move, { - capture: true, - passive: false - }); - on(window, "scroll", this.unbindMove); - on(document, pointerUp, this.end, true); - }, - move: function move(e) { - var this$1 = this; - var distance = this.pos - this.drag; - if (distance === 0 || this.prevPos === this.pos || !this.dragging && Math.abs(distance) < this.threshold) { - return; - } - e.cancelable && e.preventDefault(); - this.dragging = true; - this.dir = distance < 0 ? 1 : -1; - var ref = this; - var slides = ref.slides; - var ref$1 = this; - var prevIndex = ref$1.prevIndex; - var dis = Math.abs(distance); - var nextIndex = this.getIndex(prevIndex + this.dir, prevIndex); - var width = this._getDistance(prevIndex, nextIndex) || slides[prevIndex].offsetWidth; - while (nextIndex !== prevIndex && dis > width) { - this$1.drag -= width * this$1.dir; - prevIndex = nextIndex; - dis -= width; - nextIndex = this$1.getIndex(prevIndex + this$1.dir, prevIndex); - width = this$1._getDistance(prevIndex, nextIndex) || slides[prevIndex].offsetWidth; - } - this.percent = dis / width; - var prev = slides[prevIndex]; - var next = slides[nextIndex]; - var changed = this.index !== nextIndex; - var edge = prevIndex === nextIndex; - var itemShown; - [ this.index, this.prevIndex ].filter(function(i) { - return !includes([ nextIndex, prevIndex ], i); - }).forEach(function(i) { - trigger(slides[i], "itemhidden", [ this$1 ]); - if (edge) { - itemShown = true; - this$1.prevIndex = prevIndex; - } - }); - if (this.index === prevIndex && this.prevIndex !== prevIndex || itemShown) { - trigger(slides[this.index], "itemshown", [ this ]); - } - if (changed) { - this.prevIndex = prevIndex; - this.index = nextIndex; - !edge && trigger(prev, "beforeitemhide", [ this ]); - trigger(next, "beforeitemshow", [ this ]); - } - this._transitioner = this._translate(Math.abs(this.percent), prev, !edge && next); - if (changed) { - !edge && trigger(prev, "itemhide", [ this ]); - trigger(next, "itemshow", [ this ]); - } - }, - end: function end() { - off(window, "scroll", this.unbindMove); - this.unbindMove(); - off(document, pointerUp, this.end, true); - if (this.dragging) { - this.dragging = null; - if (this.index === this.prevIndex) { - this.percent = 1 - this.percent; - this.dir *= -1; - this._show(false, this.index, true); - this._transitioner = null; - } else { - var dirChange = (isRtl ? this.dir * (isRtl ? 1 : -1) : this.dir) < 0 === this.prevPos > this.pos; - this.index = dirChange ? this.index : this.prevIndex; - if (dirChange) { - this.percent = 1 - this.percent; - } - this.show(this.dir > 0 && !dirChange || this.dir < 0 && dirChange ? "next" : "previous", true); - } - preventClick(); - } - this.drag = this.percent = null; - } - } - }; - function hasTextNodesOnly(el) { - return !el.children.length && el.childNodes.length; - } - } - function NavMixin(UIkit) { - var ref = UIkit.util; - var $ = ref.$; - var $$ = ref.$$; - var data = ref.data; - var html = ref.html; - var toggleClass = ref.toggleClass; - var toNumber = ref.toNumber; - return { - defaults: { - selNav: false - }, - computed: { - nav: function nav(ref, $el) { - var selNav = ref.selNav; - return $(selNav, $el); - }, - navItemSelector: function navItemSelector(ref) { - var attrItem = ref.attrItem; - return "[" + attrItem + "],[data-" + attrItem + "]"; - }, - navItems: function navItems(_, $el) { - return $$(this.navItemSelector, $el); - } - }, - update: [ { - write: function write() { - var this$1 = this; - if (this.nav && this.length !== this.nav.children.length) { - html(this.nav, this.slides.map(function(_, i) { - return "
  • '; - }).join("")); - } - toggleClass($$(this.navItemSelector, this.$el).concat(this.nav), "uk-hidden", !this.maxIndex); - this.updateNav(); - }, - events: [ "load", "resize" ] - } ], - events: [ { - name: "click", - delegate: function delegate() { - return this.navItemSelector; - }, - handler: function handler(e) { - e.preventDefault(); - e.current.blur(); - this.show(data(e.current, this.attrItem)); - } - }, { - name: "itemshow", - handler: "updateNav" - } ], - methods: { - updateNav: function updateNav() { - var this$1 = this; - var i = this.getValidIndex(); - this.navItems.forEach(function(el) { - var cmd = data(el, this$1.attrItem); - toggleClass(el, this$1.clsActive, toNumber(cmd) === i); - toggleClass(el, "uk-invisible", this$1.finite && (cmd === "previous" && i === 0 || cmd === "next" && i >= this$1.maxIndex)); - }); - } - } - }; - } - function plugin$5(UIkit) { - if (plugin$5.installed) { - return; - } - var ref = UIkit.util; - var $ = ref.$; - var assign = ref.assign; - var clamp = ref.clamp; - var fastdom = ref.fastdom; - var getIndex = ref.getIndex; - var hasClass = ref.hasClass; - var isNumber = ref.isNumber; - var isRtl = ref.isRtl; - var Promise = ref.Promise; - var toNodes = ref.toNodes; - var trigger = ref.trigger; - UIkit.mixin.slider = { - attrs: true, - mixins: [ AutoplayMixin(UIkit), DragMixin(UIkit), NavMixin(UIkit) ], - props: { - clsActivated: Boolean, - easing: String, - index: Number, - finite: Boolean, - velocity: Number - }, - defaults: { - easing: "ease", - finite: false, - velocity: 1, - index: 0, - stack: [], - percent: 0, - clsActive: "uk-active", - clsActivated: false, - Transitioner: false, - transitionOptions: {} - }, - computed: { - duration: function duration(ref, $el) { - var velocity = ref.velocity; - return speedUp($el.offsetWidth / velocity); - }, - length: function length() { - return this.slides.length; - }, - list: function list(ref, $el) { - var selList = ref.selList; - return $(selList, $el); - }, - maxIndex: function maxIndex() { - return this.length - 1; - }, - slidesSelector: function slidesSelector(ref) { - var selList = ref.selList; - return selList + " > *"; - }, - slides: function slides() { - return toNodes(this.list.children); - } - }, - methods: { - show: function show(index, force) { - var this$1 = this; - if (force === void 0) force = false; - if (this.dragging || !this.length) { - return; - } - var ref = this; - var stack = ref.stack; - var queueIndex = force ? 0 : stack.length; - var reset = function() { - stack.splice(queueIndex, 1); - if (stack.length) { - this$1.show(stack.shift(), true); - } - }; - stack[force ? "unshift" : "push"](index); - if (!force && stack.length > 1) { - if (stack.length === 2) { - this._transitioner.forward(Math.min(this.duration, 200)); - } - return; - } - var prevIndex = this.index; - var prev = hasClass(this.slides, this.clsActive) && this.slides[prevIndex]; - var nextIndex = this.getIndex(index, this.index); - var next = this.slides[nextIndex]; - if (prev === next) { - reset(); - return; - } - this.dir = getDirection(index, prevIndex); - this.prevIndex = prevIndex; - this.index = nextIndex; - prev && trigger(prev, "beforeitemhide", [ this ]); - if (!trigger(next, "beforeitemshow", [ this, prev ])) { - this.index = this.prevIndex; - reset(); - return; - } - var promise = this._show(prev, next, force).then(function() { - prev && trigger(prev, "itemhidden", [ this$1 ]); - trigger(next, "itemshown", [ this$1 ]); - return new Promise(function(resolve) { - fastdom.write(function() { - stack.shift(); - if (stack.length) { - this$1.show(stack.shift(), true); - } else { - this$1._transitioner = null; - } - resolve(); - }); - }); - }); - prev && trigger(prev, "itemhide", [ this ]); - trigger(next, "itemshow", [ this ]); - return promise; - }, - getIndex: function getIndex$1(index, prev) { - if (index === void 0) index = this.index; - if (prev === void 0) prev = this.index; - return clamp(getIndex(index, this.slides, prev, this.finite), 0, this.maxIndex); - }, - getValidIndex: function getValidIndex(index, prevIndex) { - if (index === void 0) index = this.index; - if (prevIndex === void 0) prevIndex = this.prevIndex; - return this.getIndex(index, prevIndex); - }, - _show: function _show(prev, next, force) { - this._transitioner = this._getTransitioner(prev, next, this.dir, assign({ - easing: force ? next.offsetWidth < 600 ? "cubic-bezier(0.25, 0.46, 0.45, 0.94)" : "cubic-bezier(0.165, 0.84, 0.44, 1)" : this.easing - }, this.transitionOptions)); - if (!force && !prev) { - this._transitioner.translate(1); - return Promise.resolve(); - } - var ref = this.stack; - var length = ref.length; - return this._transitioner[length > 1 ? "forward" : "show"](length > 1 ? Math.min(this.duration, 75 + 75 / (length - 1)) : this.duration, this.percent); - }, - _getDistance: function _getDistance(prev, next) { - return new this._getTransitioner(prev, prev !== next && next).getDistance(); - }, - _translate: function _translate(percent, prev, next) { - if (prev === void 0) prev = this.prevIndex; - if (next === void 0) next = this.index; - var transitioner = this._getTransitioner(prev !== next ? prev : false, next); - transitioner.translate(percent); - return transitioner; - }, - _getTransitioner: function _getTransitioner(prev, next, dir, options) { - if (prev === void 0) prev = this.prevIndex; - if (next === void 0) next = this.index; - if (dir === void 0) dir = this.dir || 1; - if (options === void 0) options = this.transitionOptions; - return new this.Transitioner(isNumber(prev) ? this.slides[prev] : prev, isNumber(next) ? this.slides[next] : next, dir * (isRtl ? -1 : 1), options); - } - } - }; - function getDirection(index, prevIndex) { - return index === "next" ? 1 : index === "previous" ? -1 : index < prevIndex ? -1 : 1; - } - } - function speedUp(x) { - return .5 * x + 300; - } - function plugin$4(UIkit) { - if (plugin$4.installed) { - return; - } - UIkit.use(plugin$5); - var mixin = UIkit.mixin; - var UIkit_util = UIkit.util; - var addClass = UIkit_util.addClass; - var assign = UIkit_util.assign; - var fastdom = UIkit_util.fastdom; - var isNumber = UIkit_util.isNumber; - var removeClass = UIkit_util.removeClass; - var Animations = AnimationsPlugin(UIkit); - var Transitioner = TransitionerPlugin(UIkit); - UIkit.mixin.slideshow = { - mixins: [ mixin.slider ], - props: { - animation: String - }, - defaults: { - animation: "slide", - clsActivated: "uk-transition-active", - Animations: Animations, - Transitioner: Transitioner - }, - computed: { - animation: function animation(ref) { - var animation = ref.animation; - var Animations = ref.Animations; - return assign(animation in Animations ? Animations[animation] : Animations.slide, { - name: animation - }); - }, - transitionOptions: function transitionOptions() { - return { - animation: this.animation - }; - } - }, - events: { - "itemshow itemhide itemshown itemhidden": function itemshowitemhideitemshownitemhidden(ref) { - var target = ref.target; - UIkit.update(target); - }, - itemshow: function itemshow() { - isNumber(this.prevIndex) && fastdom.flush(); - }, - beforeitemshow: function beforeitemshow(ref) { - var target = ref.target; - addClass(target, this.clsActive); - }, - itemshown: function itemshown(ref) { - var target = ref.target; - addClass(target, this.clsActivated); - }, - itemhidden: function itemhidden(ref) { - var target = ref.target; - removeClass(target, this.clsActive, this.clsActivated); - } - } - }; - } - function AnimationsPlugin$1(UIkit) { - var mixin = UIkit.mixin; - var UIkit_util = UIkit.util; - var assign = UIkit_util.assign; - var css = UIkit_util.css; - return assign({}, mixin.slideshow.defaults.Animations, { - fade: { - show: function show() { - return [ { - opacity: 0 - }, { - opacity: 1 - } ]; - }, - percent: function percent(current) { - return 1 - css(current, "opacity"); - }, - translate: function translate$$1(percent) { - return [ { - opacity: 1 - percent - }, { - opacity: percent - } ]; - } - }, - scale: { - show: function show() { - return [ { - opacity: 0, - transform: scale3d(1 - .2) - }, { - opacity: 1, - transform: scale3d(1) - } ]; - }, - percent: function percent(current) { - return 1 - css(current, "opacity"); - }, - translate: function translate$$1(percent) { - return [ { - opacity: 1 - percent, - transform: scale3d(1 - .2 * percent) - }, { - opacity: percent, - transform: scale3d(1 - .2 + .2 * percent) - } ]; - } - } - }); - } - function plugin$3(UIkit) { - if (plugin$3.installed) { - return; - } - UIkit.use(plugin$4); - var mixin = UIkit.mixin; - var util = UIkit.util; - var $ = util.$; - var addClass = util.addClass; - var ajax = util.ajax; - var append = util.append; - var assign = util.assign; - var attr = util.attr; - var css = util.css; - var getImage = util.getImage; - var html = util.html; - var index = util.index; - var on = util.on; - var pointerDown = util.pointerDown; - var pointerMove = util.pointerMove; - var removeClass = util.removeClass; - var Transition = util.Transition; - var trigger = util.trigger; - var Animations = AnimationsPlugin$1(UIkit); - UIkit.component("lightbox-panel", { - mixins: [ mixin.container, mixin.modal, mixin.togglable, mixin.slideshow ], - functional: true, - props: { - delayControls: Number, - preload: Number, - videoAutoplay: Boolean, - template: String - }, - defaults: { - preload: 1, - videoAutoplay: false, - delayControls: 3e3, - items: [], - cls: "uk-open", - clsPage: "uk-lightbox-page", - selList: ".uk-lightbox-items", - attrItem: "uk-lightbox-item", - selClose: ".uk-close-large", - pauseOnHover: false, - velocity: 2, - Animations: Animations, - template: '
      ' - }, - created: function created() { - var this$1 = this; - this.$mount(append(this.container, this.template)); - this.caption = $(".uk-lightbox-caption", this.$el); - this.items.forEach(function() { - return append(this$1.list, "
    • "); - }); - }, - events: [ { - name: pointerMove + " " + pointerDown + " keydown", - handler: "showControls" - }, { - name: "click", - self: true, - delegate: function delegate() { - return this.slidesSelector; - }, - handler: function handler(e) { - e.preventDefault(); - this.hide(); - } - }, { - name: "shown", - self: true, - handler: "showControls" - }, { - name: "hide", - self: true, - handler: function handler() { - this.hideControls(); - removeClass(this.slides, this.clsActive); - Transition.stop(this.slides); - } - }, { - name: "keyup", - el: document, - handler: function handler(e) { - if (!this.isToggled(this.$el)) { - return; - } - switch (e.keyCode) { - case 37: - this.show("previous"); - break; - - case 39: - this.show("next"); - break; - } - } - }, { - name: "beforeitemshow", - handler: function handler(e) { - if (this.isToggled()) { - return; - } - this.preventCatch = true; - e.preventDefault(); - this.toggleNow(this.$el, true); - this.animation = Animations["scale"]; - removeClass(e.target, this.clsActive); - this.stack.splice(1, 0, this.index); - } - }, { - name: "itemshow", - handler: function handler(ref) { - var this$1 = this; - var target = ref.target; - var i = index(target); - var ref$1 = this.getItem(i); - var caption = ref$1.caption; - css(this.caption, "display", caption ? "" : "none"); - html(this.caption, caption); - for (var j = 0; j <= this.preload; j++) { - this$1.loadItem(this$1.getIndex(i + j)); - this$1.loadItem(this$1.getIndex(i - j)); - } - } - }, { - name: "itemshown", - handler: function handler() { - this.preventCatch = false; - } - }, { - name: "itemload", - handler: function handler(_, item) { - var this$1 = this; - var source = item.source; - var type = item.type; - var alt = item.alt; - this.setItem(item, ""); - if (!source) { - return; - } - var matches; - if (type === "image" || source.match(/\.(jp(e)?g|png|gif|svg)$/i)) { - getImage(source).then(function(img) { - return this$1.setItem(item, '' + (alt ? alt : '); - }, function() { - return this$1.setError(item); - }); - } else if (type === "video" || source.match(/\.(mp4|webm|ogv)$/i)) { - var video = $("'); - attr(video, "src", source); - on(video, "error", function() { - return this$1.setError(item); - }); - on(video, "loadedmetadata", function() { - attr(video, { - width: video.videoWidth, - height: video.videoHeight - }); - this$1.setItem(item, video); - }); - } else if (type === "iframe") { - this.setItem(item, ''); - } else if (matches = source.match(/\/\/.*?youtube(-nocookie)?\.[a-z]+\/watch\?v=([^&\s]+)/) || source.match(/()youtu\.be\/(.*)/)) { - var id = matches[2]; - var setIframe = function(width, height) { - if (width === void 0) width = 640; - if (height === void 0) height = 450; - return this$1.setItem(item, getIframe("//www.youtube" + (matches[1] || "") + ".com/embed/" + id, width, height, this$1.videoAutoplay)); - }; - getImage("//img.youtube.com/vi/" + id + "/maxresdefault.jpg").then(function(ref) { - var width = ref.width; - var height = ref.height; - if (width === 120 && height === 90) { - getImage("//img.youtube.com/vi/" + id + "/0.jpg").then(function(ref) { - var width = ref.width; - var height = ref.height; - return setIframe(width, height); - }, setIframe); - } else { - setIframe(width, height); - } - }, setIframe); - } else if (matches = source.match(/(\/\/.*?)vimeo\.[a-z]+\/([0-9]+).*?/)) { - ajax("//vimeo.com/api/oembed.json?maxwidth=1920&url=" + encodeURI(source), { - responseType: "json" - }).then(function(ref) { - var ref_response = ref.response; - var height = ref_response.height; - var width = ref_response.width; - return this$1.setItem(item, getIframe("//player.vimeo.com/video/" + matches[2], width, height, this$1.videoAutoplay)); - }); - } - } - } ], - methods: { - loadItem: function loadItem(index) { - if (index === void 0) index = this.index; - var item = this.getItem(index); - if (item.content) { - return; - } - trigger(this.$el, "itemload", [ item ]); - }, - getItem: function getItem(index) { - if (index === void 0) index = this.index; - return this.items[index] || {}; - }, - setItem: function setItem(item, content) { - assign(item, { - content: content - }); - var el = html(this.slides[this.items.indexOf(item)], content); - trigger(this.$el, "itemloaded", [ this, el ]); - UIkit.update(el); - }, - setError: function setError(item) { - this.setItem(item, ''); - }, - showControls: function showControls() { - clearTimeout(this.controlsTimer); - this.controlsTimer = setTimeout(this.hideControls, this.delayControls); - addClass(this.$el, "uk-active", "uk-transition-active"); - }, - hideControls: function hideControls() { - removeClass(this.$el, "uk-active", "uk-transition-active"); - } - } - }); - function getIframe(src, width, height, autoplay) { - return ''; - } - } - function plugin$2(UIkit) { - if (plugin$2.installed) { - return; - } - UIkit.use(plugin$3); - var util = UIkit.util; - var $$ = util.$$; - var assign = util.assign; - var data = util.data; - var index = util.index; - var ref = UIkit.components.lightboxPanel; - var options = ref.options; - UIkit.component("lightbox", { - attrs: true, - props: assign({ - toggle: String - }, options.props), - defaults: assign({ - toggle: "a" - }, Object.keys(options.props).reduce(function(defaults, key) { - defaults[key] = options.defaults[key]; - return defaults; - }, {})), - computed: { - toggles: function toggles(ref, $el) { - var toggle = ref.toggle; - return $$(toggle, $el); - } - }, - disconnected: function disconnected() { - this._destroy(); - }, - events: [ { - name: "click", - delegate: function delegate() { - return this.toggle + ":not(.uk-disabled)"; - }, - handler: function handler(e) { - e.preventDefault(); - e.current.blur(); - this.show(index(this.toggles, e.current)); - } - } ], - update: function update(data) { - data.toggles = data.toggles || this.toggles; - if (this.panel && this.animation) { - this.panel.$props.animation = this.animation; - this.panel.$emit(); - } - if (!this.panel || isEqualList(data.toggles, this.toggles)) { - return; - } - data.toggles = this.toggles; - this._destroy(); - this._init(); - }, - methods: { - _init: function _init() { - return this.panel = this.panel || UIkit.lightboxPanel(assign({}, this.$props, { - items: this.toggles.reduce(function(items, el) { - items.push([ "href", "caption", "type", "poster", "alt" ].reduce(function(obj, attr) { - obj[attr === "href" ? "source" : attr] = data(el, attr); - return obj; - }, {})); - return items; - }, []) - })); - }, - _destroy: function _destroy() { - if (this.panel) { - this.panel.$destroy(true); - this.panel = null; - } - }, - show: function show(index) { - if (!this.panel) { - this._init(); - } - return this.panel.show(index); - }, - hide: function hide() { - return this.panel && this.panel.hide(); - } - } - }); - function isEqualList(listA, listB) { - return listA.length === listB.length && listA.every(function(el, i) { - return el === listB[i]; - }); - } - } - function plugin$6(UIkit) { - var obj; - if (plugin$6.installed) { - return; - } - var ref = UIkit.util; - var append = ref.append; - var apply = ref.apply; - var closest = ref.closest; - var css = ref.css; - var pointerEnter = ref.pointerEnter; - var pointerLeave = ref.pointerLeave; - var remove = ref.remove; - var toFloat = ref.toFloat; - var Transition = ref.Transition; - var trigger = ref.trigger; - var containers = {}; - UIkit.component("notification", { - functional: true, - args: [ "message", "status" ], - defaults: { - message: "", - status: "", - timeout: 5e3, - group: null, - pos: "top-center", - clsClose: "uk-notification-close", - clsMsg: "uk-notification-message" - }, - created: function created() { - if (!containers[this.pos]) { - containers[this.pos] = append(UIkit.container, '
      '); - } - var container = css(containers[this.pos], "display", "block"); - this.$mount(append(container, '
      ' + this.message + "
      ")); - }, - ready: function ready() { - var this$1 = this; - var marginBottom = toFloat(css(this.$el, "marginBottom")); - Transition.start(css(this.$el, { - opacity: 0, - marginTop: -this.$el.offsetHeight, - marginBottom: 0 - }), { - opacity: 1, - marginTop: 0, - marginBottom: marginBottom - }).then(function() { - if (this$1.timeout) { - this$1.timer = setTimeout(this$1.close, this$1.timeout); - } - }); - }, - events: (obj = { - click: function click(e) { - if (closest(e.target, 'a[href="#"]')) { - e.preventDefault(); - } - this.close(); - } - }, obj[pointerEnter] = function() { - if (this.timer) { - clearTimeout(this.timer); - } - }, obj[pointerLeave] = function() { - if (this.timeout) { - this.timer = setTimeout(this.close, this.timeout); - } - }, obj), - methods: { - close: function close(immediate) { - var this$1 = this; - var removeFn = function() { - trigger(this$1.$el, "close", [ this$1 ]); - remove(this$1.$el); - if (!containers[this$1.pos].children.length) { - css(containers[this$1.pos], "display", "none"); - } - }; - if (this.timer) { - clearTimeout(this.timer); - } - if (immediate) { - removeFn(); - } else { - Transition.start(this.$el, { - opacity: 0, - marginTop: -this.$el.offsetHeight, - marginBottom: 0 - }).then(removeFn); - } - } - } - }); - UIkit.notification.closeAll = function(group, immediate) { - apply(document.body, function(el) { - var notification = UIkit.getComponent(el, "notification"); - if (notification && (!group || group === notification.group)) { - notification.close(immediate); - } - }); - }; - } - function plugin$8(UIkit) { - if (plugin$8.installed) { - return; - } - var mixin = UIkit.mixin; - var util = UIkit.util; - var css = util.css; - var Dimensions = util.Dimensions; - var each = util.each; - var getImage = util.getImage; - var includes = util.includes; - var isNumber = util.isNumber; - var isUndefined = util.isUndefined; - var toFloat = util.toFloat; - var props = [ "x", "y", "bgx", "bgy", "rotate", "scale", "color", "backgroundColor", "borderColor", "opacity", "blur", "hue", "grayscale", "invert", "saturate", "sepia", "fopacity" ]; - mixin.parallax = { - props: props.reduce(function(props, prop) { - props[prop] = "list"; - return props; - }, { - media: "media" - }), - defaults: props.reduce(function(defaults, prop) { - defaults[prop] = undefined; - return defaults; - }, { - media: false - }), - computed: { - props: function props$1(properties, $el) { - var this$1 = this; - return props.reduce(function(props, prop) { - if (isUndefined(properties[prop])) { - return props; - } - var isColor = prop.match(/color/i); - var isCssProp = isColor || prop === "opacity"; - var pos, bgPos, diff; - var steps = properties[prop].slice(0); - if (isCssProp) { - css($el, prop, ""); - } - if (steps.length < 2) { - steps.unshift((prop === "scale" ? 1 : isCssProp ? css($el, prop) : 0) || 0); - } - var unit = includes(steps.join(""), "%") ? "%" : "px"; - if (isColor) { - var ref = $el.style; - var color = ref.color; - steps = steps.map(function(step) { - return parseColor($el, step); - }); - $el.style.color = color; - } else { - steps = steps.map(toFloat); - } - if (prop.match(/^bg/)) { - css($el, "background-position-" + prop[2], ""); - bgPos = css($el, "backgroundPosition").split(" ")[prop[2] === "x" ? 0 : 1]; - if (this$1.covers) { - var min = Math.min.apply(Math, steps); - var max = Math.max.apply(Math, steps); - var down = steps.indexOf(min) < steps.indexOf(max); - diff = max - min; - steps = steps.map(function(step) { - return step - (down ? min : max); - }); - pos = (down ? -diff : 0) + "px"; - } else { - pos = bgPos; - } - } - props[prop] = { - steps: steps, - unit: unit, - pos: pos, - bgPos: bgPos, - diff: diff - }; - return props; - }, {}); - }, - bgProps: function bgProps() { - var this$1 = this; - return [ "bgx", "bgy" ].filter(function(bg) { - return bg in this$1.props; - }); - }, - covers: function covers$1(_, $el) { - return covers($el); - } - }, - disconnected: function disconnected() { - delete this._image; - }, - update: [ { - read: function read(data) { - var this$1 = this; - data.active = !this.media || window.matchMedia(this.media).matches; - if (data.image) { - data.image.dimEl = { - width: this.$el.offsetWidth, - height: this.$el.offsetHeight - }; - } - if ("image" in data || !this.covers || !this.bgProps.length) { - return; - } - var src = css(this.$el, "backgroundImage").replace(/^none|url\(["']?(.+?)["']?\)$/, "$1"); - if (!src) { - return; - } - data.image = false; - getImage(src).then(function(img) { - data.image = { - width: img.naturalWidth, - height: img.naturalHeight - }; - this$1.$emit(); - }); - }, - write: function write(ref) { - var this$1 = this; - var image = ref.image; - var active = ref.active; - if (!image) { - return; - } - if (!active) { - css(this.$el, { - backgroundSize: "", - backgroundRepeat: "" - }); - return; - } - var dimEl = image.dimEl; - var dim = Dimensions.cover(image, dimEl); - this.bgProps.forEach(function(prop) { - var ref = this$1.props[prop]; - var diff = ref.diff; - var bgPos = ref.bgPos; - var steps = ref.steps; - var attr = prop === "bgy" ? "height" : "width"; - var span = dim[attr] - dimEl[attr]; - if (!bgPos.match(/%$|0px/)) { - return; - } - if (span < diff) { - dimEl[attr] = dim[attr] + diff - span; - } else if (span > diff) { - var bgPosFloat = parseFloat(bgPos); - if (bgPosFloat) { - this$1.props[prop].steps = steps.map(function(step) { - return step - (span - diff) / (100 / bgPosFloat); - }); - } - } - dim = Dimensions.cover(image, dimEl); - }); - css(this.$el, { - backgroundSize: dim.width + "px " + dim.height + "px", - backgroundRepeat: "no-repeat" - }); - }, - events: [ "load", "resize" ] - } ], - methods: { - reset: function reset() { - var this$1 = this; - each(this.getCss(0), function(_, prop) { - return css(this$1.$el, prop, ""); - }); - }, - getCss: function getCss(percent) { - var ref = this; - var props = ref.props; - var translated = false; - return Object.keys(props).reduce(function(css, prop) { - var ref = props[prop]; - var steps = ref.steps; - var unit = ref.unit; - var pos = ref.pos; - var value = getValue(steps, percent); - switch (prop) { - case "x": - case "y": - if (translated) { - break; - } - var ref$1 = [ "x", "y" ].map(function(dir) { - return prop === dir ? value + unit : props[dir] ? getValue(props[dir].steps, percent) + props[dir].unit : 0; - }); - var x = ref$1[0]; - var y = ref$1[1]; - translated = css.transform += " translate3d(" + x + ", " + y + ", 0)"; - break; - - case "rotate": - css.transform += " rotate(" + value + "deg)"; - break; - - case "scale": - css.transform += " scale(" + value + ")"; - break; - - case "bgy": - case "bgx": - css["background-position-" + prop[2]] = "calc(" + pos + " + " + (value + unit) + ")"; - break; - - case "color": - case "backgroundColor": - case "borderColor": - var ref$2 = getStep(steps, percent); - var start = ref$2[0]; - var end = ref$2[1]; - var p = ref$2[2]; - css[prop] = "rgba(" + start.map(function(value, i) { - value = value + p * (end[i] - value); - return i === 3 ? toFloat(value) : parseInt(value, 10); - }).join(",") + ")"; - break; - - case "blur": - css.filter += " blur(" + value + "px)"; - break; - - case "hue": - css.filter += " hue-rotate(" + value + "deg)"; - break; - - case "fopacity": - css.filter += " opacity(" + value + "%)"; - break; - - case "grayscale": - case "invert": - case "saturate": - case "sepia": - css.filter += " " + prop + "(" + value + "%)"; - break; - - default: - css[prop] = value; - } - return css; - }, { - transform: "", - filter: "" - }); - } - } - }; - function parseColor(el, color) { - return css(css(el, "color", color), "color").split(/[(),]/g).slice(1, -1).concat(1).slice(0, 4).map(function(n) { - return toFloat(n); - }); - } - function getStep(steps, percent) { - var count = steps.length - 1; - var index = Math.min(Math.floor(count * percent), count - 1); - var step = steps.slice(index, index + 2); - step.push(percent === 1 ? 1 : percent % (1 / count) * count); - return step; - } - function getValue(steps, percent) { - var ref = getStep(steps, percent); - var start = ref[0]; - var end = ref[1]; - var p = ref[2]; - return (isNumber(start) ? start + Math.abs(start - end) * p * (start < end ? 1 : -1) : +end).toFixed(2); - } - function covers(el) { - var ref = el.style; - var backgroundSize = ref.backgroundSize; - var covers = css(css(el, "backgroundSize", ""), "backgroundSize") === "cover"; - el.style.backgroundSize = backgroundSize; - return covers; - } - } - function plugin$7(UIkit) { - if (plugin$7.installed) { - return; - } - UIkit.use(plugin$8); - var mixin = UIkit.mixin; - var util = UIkit.util; - var clamp = util.clamp; - var css = util.css; - var scrolledOver = util.scrolledOver; - var query = util.query; - UIkit.component("parallax", { - mixins: [ mixin.parallax ], - props: { - target: String, - viewport: Number, - easing: Number - }, - defaults: { - target: false, - viewport: 1, - easing: 1 - }, - computed: { - target: function target(ref, $el) { - var target = ref.target; - return target && query(target, $el) || $el; - } - }, - update: [ { - read: function read(ref) { - var percent = ref.percent; - return { - prev: percent, - percent: ease(scrolledOver(this.target) / (this.viewport || 1), this.easing) - }; - }, - write: function write(ref, ref$1) { - var prev = ref.prev; - var percent = ref.percent; - var active = ref.active; - var type = ref$1.type; - if (type !== "scroll") { - prev = false; - } - if (!active) { - this.reset(); - return; - } - if (prev !== percent) { - css(this.$el, this.getCss(percent)); - } - }, - events: [ "scroll", "load", "resize" ] - } ] - }); - function ease(percent, easing) { - return clamp(percent * (1 - (easing - easing * percent))); - } - } - function SliderReactive(UIkit) { - var ref = UIkit.util; - var removeClass = ref.removeClass; - return { - update: [ { - write: function write() { - if (this.stack.length || this.dragging) { - return; - } - var index = this.getValidIndex(); - delete this.index; - removeClass(this.slides, this.clsActive, this.clsActivated); - this.show(index); - }, - events: [ "load", "resize" ] - } ] - }; - } - function TransitionerPlugin$1(UIkit) { - var ref = UIkit.util; - var assign = ref.assign; - var clamp = ref.clamp; - var createEvent = ref.createEvent; - var css = ref.css; - var Deferred = ref.Deferred; - var includes = ref.includes; - var index = ref.index; - var isRtl = ref.isRtl; - var noop = ref.noop; - var sortBy = ref.sortBy; - var toNodes = ref.toNodes; - var Transition = ref.Transition; - var trigger = ref.trigger; - var Transitioner = assign(function(prev, next, dir, ref) { - var center = ref.center; - var easing = ref.easing; - var list = ref.list; - var deferred = new Deferred(); - var from = prev ? Transitioner.getLeft(prev, list, center) : Transitioner.getLeft(next, list, center) + next.offsetWidth * dir, to = next ? Transitioner.getLeft(next, list, center) : from + prev.offsetWidth * dir * (isRtl ? -1 : 1); - return { - dir: dir, - show: function show(duration, percent, linear) { - if (percent === void 0) percent = 0; - var timing = linear ? "linear" : easing; - duration -= Math.round(duration * clamp(percent, -1, 1)); - this.translate(percent); - prev && this.updateTranslates(); - percent = prev ? percent : clamp(percent, 0, 1); - triggerUpdate(this.getItemIn(), "itemin", { - percent: percent, - duration: duration, - timing: timing, - dir: dir - }); - prev && triggerUpdate(this.getItemIn(true), "itemout", { - percent: 1 - percent, - duration: duration, - timing: timing, - dir: dir - }); - Transition.start(list, { - transform: translate(-to * (isRtl ? -1 : 1), "px") - }, duration, timing).then(deferred.resolve, noop); - return deferred.promise; - }, - stop: function stop() { - return Transition.stop(list); - }, - cancel: function cancel() { - Transition.cancel(list); - }, - reset: function reset() { - css(list, "transform", ""); - }, - forward: function forward(duration, percent) { - if (percent === void 0) percent = this.percent(); - Transition.cancel(list); - return this.show(duration, percent, true); - }, - translate: function translate$1(percent) { - var distance = this.getDistance() * dir * (isRtl ? -1 : 1); - css(list, "transform", translate(clamp(-to + (distance - distance * percent), -Transitioner.getWidth(list), list.offsetWidth) * (isRtl ? -1 : 1), "px")); - this.updateTranslates(); - if (prev) { - percent = clamp(percent, -1, 1); - triggerUpdate(this.getItemIn(), "itemtranslatein", { - percent: percent, - dir: dir - }); - triggerUpdate(this.getItemIn(true), "itemtranslateout", { - percent: 1 - percent, - dir: dir - }); - } - }, - percent: function percent() { - return Math.abs((css(list, "transform").split(",")[4] * (isRtl ? -1 : 1) + from) / (to - from)); - }, - getDistance: function getDistance() { - return Math.abs(to - from); - }, - getItemIn: function getItemIn(out) { - if (out === void 0) out = false; - var actives = this.getActives(); - var all = sortBy(slides(list), "offsetLeft"); - var i = index(all, actives[dir * (out ? -1 : 1) > 0 ? actives.length - 1 : 0]); - return ~i && all[i + (prev && !out ? dir : 0)]; - }, - getActives: function getActives() { - var left = Transitioner.getLeft(prev || next, list, center); - return sortBy(slides(list).filter(function(slide) { - var slideLeft = Transitioner.getElLeft(slide, list); - return slideLeft >= left && slideLeft + slide.offsetWidth <= list.offsetWidth + left; - }), "offsetLeft"); - }, - updateTranslates: function updateTranslates() { - var actives = this.getActives(); - slides(list).forEach(function(slide) { - var isActive = includes(actives, slide); - triggerUpdate(slide, "itemtranslate" + (isActive ? "in" : "out"), { - percent: isActive ? 1 : 0, - dir: slide.offsetLeft <= next.offsetLeft ? 1 : -1 - }); - }); - } - }; - }, { - getLeft: function getLeft(el, list, center) { - var left = this.getElLeft(el, list); - return center ? left - this.center(el, list) : Math.min(left, this.getMax(list)); - }, - getMax: function getMax(list) { - return Math.max(0, this.getWidth(list) - list.offsetWidth); - }, - getWidth: function getWidth(list) { - return slides(list).reduce(function(right, el) { - return el.offsetWidth + right; - }, 0); - }, - getMaxWidth: function getMaxWidth(list) { - return slides(list).reduce(function(right, el) { - return Math.max(right, el.offsetWidth); - }, 0); - }, - center: function center(el, list) { - return list.offsetWidth / 2 - el.offsetWidth / 2; - }, - getElLeft: function getElLeft(el, list) { - return (el.offsetLeft + (isRtl ? el.offsetWidth - list.offsetWidth : 0)) * (isRtl ? -1 : 1); - } - }); - function triggerUpdate(el, type, data) { - trigger(el, createEvent(type, false, false, data)); - } - function slides(list) { - return toNodes(list.children); - } - return Transitioner; - } - function ParallaxPlugin(UIkit, parent) { - UIkit.use(plugin$8); - var mixin = UIkit.mixin; - var UIkit_util = UIkit.util; - var closest = UIkit_util.closest; - var css = UIkit_util.css; - var endsWith = UIkit_util.endsWith; - var noop = UIkit_util.noop; - var Transition = UIkit_util.Transition; - return { - mixins: [ mixin.parallax ], - computed: { - item: function item() { - var slider = UIkit.getComponent(closest(this.$el, ".uk-" + parent), parent); - return slider && closest(this.$el, slider.slidesSelector); - } - }, - events: [ { - name: "itemshown", - self: true, - el: function el() { - return this.item; - }, - handler: function handler() { - css(this.$el, this.getCss(.5)); - } - }, { - name: "itemin itemout", - self: true, - el: function el() { - return this.item; - }, - handler: function handler(ref) { - var type = ref.type; - var ref_detail = ref.detail; - var percent = ref_detail.percent; - var duration = ref_detail.duration; - var timing = ref_detail.timing; - var dir = ref_detail.dir; - Transition.cancel(this.$el); - css(this.$el, this.getCss(getCurrent(type, dir, percent))); - Transition.start(this.$el, this.getCss(isIn(type) ? .5 : dir > 0 ? 1 : 0), duration, timing).catch(noop); - } - }, { - name: "transitioncanceled transitionend", - self: true, - el: function el() { - return this.item; - }, - handler: function handler() { - Transition.cancel(this.$el); - } - }, { - name: "itemtranslatein itemtranslateout", - self: true, - el: function el() { - return this.item; - }, - handler: function handler(ref) { - var type = ref.type; - var ref_detail = ref.detail; - var percent = ref_detail.percent; - var dir = ref_detail.dir; - Transition.cancel(this.$el); - css(this.$el, this.getCss(getCurrent(type, dir, percent))); - } - } ] - }; - function isIn(type) { - return endsWith(type, "in"); - } - function getCurrent(type, dir, percent) { - percent /= 2; - return !isIn(type) ? dir < 0 ? percent : 1 - percent : dir < 0 ? 1 - percent : percent; - } - } - function plugin$9(UIkit) { - if (plugin$9.installed) { - return; - } - UIkit.use(plugin$5); - var mixin = UIkit.mixin; - var UIkit_util = UIkit.util; - var $ = UIkit_util.$; - var $$ = UIkit_util.$$; - var addClass = UIkit_util.addClass; - var css = UIkit_util.css; - var data = UIkit_util.data; - var includes = UIkit_util.includes; - var isNumeric = UIkit_util.isNumeric; - var isUndefined = UIkit_util.isUndefined; - var offset = UIkit_util.offset; - var toggleClass = UIkit_util.toggleClass; - var toFloat = UIkit_util.toFloat; - var Transitioner = TransitionerPlugin$1(UIkit); - UIkit.component("slider-parallax", ParallaxPlugin(UIkit, "slider")); - UIkit.component("slider", { - mixins: [ mixin.class, mixin.slider, SliderReactive(UIkit) ], - props: { - center: Boolean, - sets: Boolean - }, - defaults: { - center: false, - sets: false, - attrItem: "uk-slider-item", - selList: ".uk-slider-items", - selNav: ".uk-slider-nav", - clsContainer: "uk-slider-container", - Transitioner: Transitioner - }, - computed: { - avgWidth: function avgWidth() { - return Transitioner.getWidth(this.list) / this.length; - }, - finite: function finite(ref) { - var finite = ref.finite; - return finite || Transitioner.getWidth(this.list) < this.list.offsetWidth + Transitioner.getMaxWidth(this.list) + this.center; - }, - maxIndex: function maxIndex() { - var this$1 = this; - if (!this.finite || this.center && !this.sets) { - return this.length - 1; - } - if (this.center) { - return this.sets[this.sets.length - 1]; - } - css(this.slides, "order", ""); - var max = Transitioner.getMax(this.list); - var i = this.length; - while (i--) { - if (Transitioner.getElLeft(this$1.list.children[i], this$1.list) < max) { - return Math.min(i + 1, this$1.length - 1); - } - } - return 0; - }, - sets: function sets(ref) { - var this$1 = this; - var sets = ref.sets; - var width = this.list.offsetWidth / (this.center ? 2 : 1); - var left = 0; - var leftCenter = width; - sets = sets && this.slides.reduce(function(sets, slide, i) { - var dim = offset(slide); - if (dim.right > left) { - if (!this$1.center && i > this$1.maxIndex) { - i = this$1.maxIndex; - } - if (!includes(sets, i)) { - var cmp = this$1.slides[i + 1]; - if (this$1.center && cmp && dim.width < leftCenter - offset(cmp).width / 2) { - leftCenter -= dim.width; - } else { - leftCenter = width; - sets.push(i); - left = dim.left + width + (this$1.center ? dim.width / 2 : 0); - } - } - } - return sets; - }, []); - return sets && sets.length && sets; - }, - transitionOptions: function transitionOptions() { - return { - center: this.center, - list: this.list - }; - } - }, - connected: function connected() { - toggleClass(this.$el, this.clsContainer, !$("." + this.clsContainer, this.$el)); - }, - update: { - write: function write() { - var this$1 = this; - $$("[" + this.attrItem + "],[data-" + this.attrItem + "]", this.$el).forEach(function(el) { - var index = data(el, this$1.attrItem); - this$1.maxIndex && toggleClass(el, "uk-hidden", isNumeric(index) && (this$1.sets && !includes(this$1.sets, toFloat(index)) || index > this$1.maxIndex)); - }); - }, - events: [ "load", "resize" ] - }, - events: { - beforeitemshow: function beforeitemshow(e) { - var this$1 = this; - if (!this.dragging && this.sets && this.stack.length < 2 && !includes(this.sets, this.index)) { - this.index = this.getValidIndex(); - } - var diff = Math.abs(this.index - this.prevIndex + (this.dir > 0 && this.index < this.prevIndex || this.dir < 0 && this.index > this.prevIndex ? (this.maxIndex + 1) * this.dir : 0)); - if (!this.dragging && diff > 1) { - for (var i = 0; i < diff; i++) { - this$1.stack.splice(1, 0, this$1.dir > 0 ? "next" : "previous"); - } - e.preventDefault(); - return; - } - this.duration = speedUp(this.avgWidth / this.velocity) * ((this.dir < 0 || !this.slides[this.prevIndex] ? this.slides[this.index] : this.slides[this.prevIndex]).offsetWidth / this.avgWidth); - this.reorder(); - }, - itemshow: function itemshow() { - !isUndefined(this.prevIndex) && addClass(this._getTransitioner().getItemIn(), this.clsActive); - }, - itemshown: function itemshown() { - var this$1 = this; - var actives = this._getTransitioner(this.index).getActives(); - this.slides.forEach(function(slide) { - return toggleClass(slide, this$1.clsActive, includes(actives, slide)); - }); - (!this.sets || includes(this.sets, toFloat(this.index))) && this.slides.forEach(function(slide) { - return toggleClass(slide, this$1.clsActivated, includes(actives, slide)); - }); - } - }, - methods: { - reorder: function reorder() { - var this$1 = this; - css(this.slides, "order", ""); - if (this.finite) { - return; - } - var index = this.dir > 0 && this.slides[this.prevIndex] ? this.prevIndex : this.index; - this.slides.forEach(function(slide, i) { - return css(slide, "order", this$1.dir > 0 && i < index ? 1 : this$1.dir < 0 && i >= this$1.index ? -1 : ""); - }); - if (!this.center) { - return; - } - var next = this.slides[index]; - var width = this.list.offsetWidth / 2 - next.offsetWidth / 2; - var j = 0; - while (width > 0) { - var slideIndex = this$1.getIndex(--j + index, index); - var slide = this$1.slides[slideIndex]; - css(slide, "order", slideIndex > index ? -2 : -1); - width -= slide.offsetWidth; - } - }, - getValidIndex: function getValidIndex(index, prevIndex) { - var this$1 = this; - if (index === void 0) index = this.index; - if (prevIndex === void 0) prevIndex = this.prevIndex; - index = this.getIndex(index, prevIndex); - if (!this.sets) { - return index; - } - var prev; - do { - if (includes(this$1.sets, index)) { - return index; - } - prev = index; - index = this$1.getIndex(index + this$1.dir, prevIndex); - } while (index !== prev); - return index; - } - } - }); - } - function AnimationsPlugin$2(UIkit) { - var mixin = UIkit.mixin; - var UIkit_util = UIkit.util; - var assign = UIkit_util.assign; - var css = UIkit_util.css; - var Animations = assign({}, mixin.slideshow.defaults.Animations, { - fade: { - show: function show() { - return [ { - opacity: 0, - zIndex: 0 - }, { - zIndex: -1 - } ]; - }, - percent: function percent(current) { - return 1 - css(current, "opacity"); - }, - translate: function translate$$1(percent) { - return [ { - opacity: 1 - percent, - zIndex: 0 - }, { - zIndex: -1 - } ]; - } - }, - scale: { - show: function show() { - return [ { - opacity: 0, - transform: scale3d(1 + .5), - zIndex: 0 - }, { - zIndex: -1 - } ]; - }, - percent: function percent(current) { - return 1 - css(current, "opacity"); - }, - translate: function translate$$1(percent) { - return [ { - opacity: 1 - percent, - transform: scale3d(1 + .5 * percent), - zIndex: 0 - }, { - zIndex: -1 - } ]; - } - }, - pull: { - show: function show(dir) { - return dir < 0 ? [ { - transform: translate(30), - zIndex: -1 - }, { - transform: translate(), - zIndex: 0 - } ] : [ { - transform: translate(-100), - zIndex: 0 - }, { - transform: translate(), - zIndex: -1 - } ]; - }, - percent: function percent(current, next, dir) { - return dir < 0 ? 1 - Animations.translated(next) : Animations.translated(current); - }, - translate: function translate$1(percent, dir) { - return dir < 0 ? [ { - transform: translate(30 * percent), - zIndex: -1 - }, { - transform: translate(-100 * (1 - percent)), - zIndex: 0 - } ] : [ { - transform: translate(-percent * 100), - zIndex: 0 - }, { - transform: translate(30 * (1 - percent)), - zIndex: -1 - } ]; - } - }, - push: { - show: function show(dir) { - return dir < 0 ? [ { - transform: translate(100), - zIndex: 0 - }, { - transform: translate(), - zIndex: -1 - } ] : [ { - transform: translate(-30), - zIndex: -1 - }, { - transform: translate(), - zIndex: 0 - } ]; - }, - percent: function percent(current, next, dir) { - return dir > 0 ? 1 - Animations.translated(next) : Animations.translated(current); - }, - translate: function translate$2(percent, dir) { - return dir < 0 ? [ { - transform: translate(percent * 100), - zIndex: 0 - }, { - transform: translate(-30 * (1 - percent)), - zIndex: -1 - } ] : [ { - transform: translate(-30 * percent), - zIndex: -1 - }, { - transform: translate(100 * (1 - percent)), - zIndex: 0 - } ]; - } - } - }); - return Animations; - } - function plugin$10(UIkit) { - if (plugin$10.installed) { - return; - } - UIkit.use(plugin$4); - var mixin = UIkit.mixin; - var height = UIkit.util.height; - var Animations = AnimationsPlugin$2(UIkit); - UIkit.component("slideshow-parallax", ParallaxPlugin(UIkit, "slideshow")); - UIkit.component("slideshow", { - mixins: [ mixin.class, mixin.slideshow, SliderReactive(UIkit) ], - props: { - ratio: String, - minHeight: Boolean, - maxHeight: Boolean - }, - defaults: { - ratio: "16:9", - minHeight: false, - maxHeight: false, - selList: ".uk-slideshow-items", - attrItem: "uk-slideshow-item", - selNav: ".uk-slideshow-nav", - Animations: Animations - }, - update: { - read: function read() { - var ref = this.ratio.split(":").map(Number); - var width = ref[0]; - var height = ref[1]; - height = height * this.$el.offsetWidth / width; - if (this.minHeight) { - height = Math.max(this.minHeight, height); - } - if (this.maxHeight) { - height = Math.min(this.maxHeight, height); - } - return { - height: height - }; - }, - write: function write(ref) { - var hgt = ref.height; - height(this.list, Math.floor(hgt)); - }, - events: [ "load", "resize" ] - } - }); - } - function plugin$11(UIkit) { - var obj; - if (plugin$11.installed) { - return; - } - var mixin = UIkit.mixin; - var util = UIkit.util; - var addClass = util.addClass; - var after = util.after; - var assign = util.assign; - var append = util.append; - var attr = util.attr; - var before = util.before; - var closest = util.closest; - var css = util.css; - var height = util.height; - var fastdom = util.fastdom; - var getPos = util.getPos; - var includes = util.includes; - var index = util.index; - var isInput = util.isInput; - var noop = util.noop; - var offset = util.offset; - var off = util.off; - var on = util.on; - var pointerDown = util.pointerDown; - var pointerMove = util.pointerMove; - var pointerUp = util.pointerUp; - var position = util.position; - var preventClick = util.preventClick; - var Promise = util.Promise; - var remove = util.remove; - var removeClass = util.removeClass; - var toggleClass = util.toggleClass; - var toNodes = util.toNodes; - var Transition = util.Transition; - var trigger = util.trigger; - var within = util.within; - UIkit.component("sortable", { - mixins: [ mixin.class ], - props: { - group: String, - animation: Number, - threshold: Number, - clsItem: String, - clsPlaceholder: String, - clsDrag: String, - clsDragState: String, - clsBase: String, - clsNoDrag: String, - clsEmpty: String, - clsCustom: String, - handle: String - }, - defaults: { - group: false, - animation: 150, - threshold: 5, - clsItem: "uk-sortable-item", - clsPlaceholder: "uk-sortable-placeholder", - clsDrag: "uk-sortable-drag", - clsDragState: "uk-drag", - clsBase: "uk-sortable", - clsNoDrag: "uk-sortable-nodrag", - clsEmpty: "uk-sortable-empty", - clsCustom: "", - handle: false - }, - init: function init() { - var this$1 = this; - [ "init", "start", "move", "end" ].forEach(function(key) { - var fn = this$1[key]; - this$1[key] = function(e) { - this$1.scrollY = window.pageYOffset; - var ref = getPos(e); - var x = ref.x; - var y = ref.y; - this$1.pos = { - x: x, - y: y - }; - fn(e); - }; - }); - }, - events: (obj = {}, obj[pointerDown] = "init", obj), - update: { - write: function write() { - if (this.clsEmpty) { - toggleClass(this.$el, this.clsEmpty, !this.$el.children.length); - } - if (!this.drag) { - return; - } - offset(this.drag, { - top: this.pos.y + this.origin.top, - left: this.pos.x + this.origin.left - }); - var ref = offset(this.drag); - var top = ref.top; - var bottom = top + this.drag.offsetHeight; - var scroll; - if (top > 0 && top < this.scrollY) { - scroll = this.scrollY - 5; - } else if (bottom < height(document) && bottom > height(window) + this.scrollY) { - scroll = this.scrollY + 5; - } - scroll && setTimeout(function() { - return window.scrollTo(window.scrollX, scroll); - }, 5); - } - }, - methods: { - init: function init(e) { - var target = e.target; - var button = e.button; - var defaultPrevented = e.defaultPrevented; - var ref = toNodes(this.$el.children).filter(function(el) { - return within(target, el); - }); - var placeholder = ref[0]; - if (!placeholder || isInput(e.target) || this.handle && !within(target, this.handle) || button > 0 || within(target, "." + this.clsNoDrag) || defaultPrevented) { - return; - } - e.preventDefault(); - this.touched = [ this ]; - this.placeholder = placeholder; - this.origin = assign({ - target: target, - index: index(placeholder) - }, this.pos); - on(document, pointerMove, this.move); - on(document, pointerUp, this.end); - on(window, "scroll", this.scroll); - if (!this.threshold) { - this.start(e); - } - }, - start: function start(e) { - this.drag = append(UIkit.container, this.placeholder.outerHTML.replace(/^
    • $/i, "div>")); - css(this.drag, assign({ - boxSizing: "border-box", - width: this.placeholder.offsetWidth, - height: this.placeholder.offsetHeight - }, css(this.placeholder, [ "paddingLeft", "paddingRight", "paddingTop", "paddingBottom" ]))); - attr(this.drag, "uk-no-boot", ""); - addClass(this.drag, this.clsDrag, this.clsCustom); - height(this.drag.firstElementChild, height(this.placeholder.firstElementChild)); - var ref = offset(this.placeholder); - var left = ref.left; - var top = ref.top; - assign(this.origin, { - left: left - this.pos.x, - top: top - this.pos.y - }); - addClass(this.placeholder, this.clsPlaceholder); - addClass(this.$el.children, this.clsItem); - addClass(document.documentElement, this.clsDragState); - trigger(this.$el, "start", [ this, this.placeholder, this.drag ]); - this.move(e); - }, - move: function move(e) { - if (!this.drag) { - if (Math.abs(this.pos.x - this.origin.x) > this.threshold || Math.abs(this.pos.y - this.origin.y) > this.threshold) { - this.start(e); - } - return; - } - this.$emit(); - var target = e.type === "mousemove" ? e.target : document.elementFromPoint(this.pos.x - document.body.scrollLeft, this.pos.y - document.body.scrollTop); - var sortable = getSortable(target); - var previous = getSortable(this.placeholder); - var move = sortable !== previous; - if (!sortable || within(target, this.placeholder) || move && (!sortable.group || sortable.group !== previous.group)) { - return; - } - target = sortable.$el === target.parentNode && target || toNodes(sortable.$el.children).filter(function(element) { - return within(target, element); - })[0]; - if (move) { - previous.remove(this.placeholder); - } else if (!target) { - return; - } - sortable.insert(this.placeholder, target); - if (!includes(this.touched, sortable)) { - this.touched.push(sortable); - } - }, - scroll: function scroll() { - var scroll = window.pageYOffset; - if (scroll !== this.scrollY) { - this.pos.y += scroll - this.scrollY; - this.scrollY = scroll; - this.$emit(); - } - }, - end: function end(e) { - off(document, pointerMove, this.move); - off(document, pointerUp, this.end); - off(window, "scroll", this.scroll); - if (!this.drag) { - if (e.type !== "mouseup" && within(e.target, "a[href]")) { - location.href = closest(e.target, "a[href]").href; - } - return; - } - preventClick(); - var sortable = getSortable(this.placeholder); - if (this === sortable) { - if (this.origin.index !== index(this.placeholder)) { - trigger(this.$el, "moved", [ this, this.placeholder ]); - } - } else { - trigger(sortable.$el, "added", [ sortable, this.placeholder ]); - trigger(this.$el, "removed", [ this, this.placeholder ]); - } - trigger(this.$el, "stop", [ this ]); - remove(this.drag); - this.drag = null; - var classes = this.touched.map(function(sortable) { - return sortable.clsPlaceholder + " " + sortable.clsItem; - }).join(" "); - this.touched.forEach(function(sortable) { - return removeClass(sortable.$el.children, classes); - }); - removeClass(document.documentElement, this.clsDragState); - }, - insert: function insert(element, target) { - var this$1 = this; - addClass(this.$el.children, this.clsItem); - var insert = function() { - if (target) { - if (!within(element, this$1.$el) || isPredecessor(element, target)) { - before(target, element); - } else { - after(target, element); - } - } else { - append(this$1.$el, element); - } - }; - if (this.animation) { - this.animate(insert); - } else { - insert(); - } - }, - remove: function remove$1(element) { - if (!within(element, this.$el)) { - return; - } - if (this.animation) { - this.animate(function() { - return remove(element); - }); - } else { - remove(element); - } - }, - animate: function animate(action) { - var this$1 = this; - var props = []; - var children = toNodes(this.$el.children); - var reset = { - position: "", - width: "", - height: "", - pointerEvents: "", - top: "", - left: "", - bottom: "", - right: "" - }; - children.forEach(function(el) { - props.push(assign({ - position: "absolute", - pointerEvents: "none", - width: el.offsetWidth, - height: el.offsetHeight - }, position(el))); - }); - action(); - children.forEach(Transition.cancel); - css(this.$el.children, reset); - UIkit.update(this.$el); - fastdom.flush(); - css(this.$el, "minHeight", height(this.$el)); - var positions = children.map(function(el) { - return position(el); - }); - Promise.all(children.map(function(el, i) { - return Transition.start(css(el, props[i]), positions[i], this$1.animation); - })).then(function() { - css(this$1.$el, "minHeight", ""); - css(children, reset); - UIkit.update(this$1.$el); - fastdom.flush(); - }, noop); - } - } - }); - function getSortable(element) { - return element && (UIkit.getComponent(element, "sortable") || getSortable(element.parentNode)); - } - function isPredecessor(element, target) { - return element.parentNode === target.parentNode && index(element) > index(target); - } - } - function plugin$12(UIkit) { - var obj; - if (plugin$12.installed) { - return; - } - var util = UIkit.util; - var mixin = UIkit.mixin; - var append = util.append; - var attr = util.attr; - var flipPosition = util.flipPosition; - var hasAttr = util.hasAttr; - var includes = util.includes; - var isTouch = util.isTouch; - var isVisible = util.isVisible; - var matches = util.matches; - var on = util.on; - var pointerDown = util.pointerDown; - var pointerEnter = util.pointerEnter; - var pointerLeave = util.pointerLeave; - var remove = util.remove; - var within = util.within; - var actives = []; - UIkit.component("tooltip", { - attrs: true, - args: "title", - mixins: [ mixin.container, mixin.togglable, mixin.position ], - props: { - delay: Number, - title: String - }, - defaults: { - pos: "top", - title: "", - delay: 0, - animation: [ "uk-animation-scale-up" ], - duration: 100, - cls: "uk-active", - clsPos: "uk-tooltip" - }, - beforeConnect: function beforeConnect() { - this._hasTitle = hasAttr(this.$el, "title"); - attr(this.$el, { - title: "", - "aria-expanded": false - }); - }, - disconnected: function disconnected() { - this.hide(); - attr(this.$el, { - title: this._hasTitle ? this.title : null, - "aria-expanded": null - }); - }, - methods: { - show: function show() { - var this$1 = this; - if (includes(actives, this)) { - return; - } - actives.forEach(function(active) { - return active.hide(); - }); - actives.push(this); - this._unbind = on(document, "click", function(e) { - return !within(e.target, this$1.$el) && this$1.hide(); - }); - clearTimeout(this.showTimer); - this.tooltip = append(this.container, '
      ' + this.title + "
      "); - attr(this.$el, "aria-expanded", true); - this.positionAt(this.tooltip, this.$el); - this.origin = this.getAxis() === "y" ? flipPosition(this.dir) + "-" + this.align : this.align + "-" + flipPosition(this.dir); - this.showTimer = setTimeout(function() { - this$1.toggleElement(this$1.tooltip, true); - this$1.hideTimer = setInterval(function() { - if (!isVisible(this$1.$el)) { - this$1.hide(); - } - }, 150); - }, this.delay); - }, - hide: function hide() { - var index = actives.indexOf(this); - if (!~index || matches(this.$el, "input") && this.$el === document.activeElement) { - return; - } - actives.splice(index, 1); - clearTimeout(this.showTimer); - clearInterval(this.hideTimer); - attr(this.$el, "aria-expanded", false); - this.toggleElement(this.tooltip, false); - this.tooltip && remove(this.tooltip); - this.tooltip = false; - this._unbind(); - } - }, - events: (obj = {}, obj["focus " + pointerEnter + " " + pointerDown] = function(e) { - if (e.type !== pointerDown || !isTouch(e)) { - this.show(); - } - }, obj.blur = "hide", obj[pointerLeave] = function(e) { - if (!isTouch(e)) { - this.hide(); - } - }, obj) - }); - } - function plugin$13(UIkit) { - if (plugin$13.installed) { - return; - } - var ref = UIkit.util; - var addClass = ref.addClass; - var ajax = ref.ajax; - var matches = ref.matches; - var noop = ref.noop; - var on = ref.on; - var removeClass = ref.removeClass; - var trigger = ref.trigger; - UIkit.component("upload", { - props: { - allow: String, - clsDragover: String, - concurrent: Number, - maxSize: Number, - method: String, - mime: String, - msgInvalidMime: String, - msgInvalidName: String, - msgInvalidSize: String, - multiple: Boolean, - name: String, - params: Object, - type: String, - url: String - }, - defaults: { - allow: false, - clsDragover: "uk-dragover", - concurrent: 1, - maxSize: 0, - method: "POST", - mime: false, - msgInvalidMime: "Invalid File Type: %s", - msgInvalidName: "Invalid File Name: %s", - msgInvalidSize: "Invalid File Size: %s Bytes Max", - multiple: false, - name: "files[]", - params: {}, - type: "", - url: "", - abort: noop, - beforeAll: noop, - beforeSend: noop, - complete: noop, - completeAll: noop, - error: noop, - fail: noop, - load: noop, - loadEnd: noop, - loadStart: noop, - progress: noop - }, - events: { - change: function change(e) { - if (!matches(e.target, 'input[type="file"]')) { - return; - } - e.preventDefault(); - if (e.target.files) { - this.upload(e.target.files); - } - e.target.value = ""; - }, - drop: function drop(e) { - stop(e); - var transfer = e.dataTransfer; - if (!transfer || !transfer.files) { - return; - } - removeClass(this.$el, this.clsDragover); - this.upload(transfer.files); - }, - dragenter: function dragenter(e) { - stop(e); - }, - dragover: function dragover(e) { - stop(e); - addClass(this.$el, this.clsDragover); - }, - dragleave: function dragleave(e) { - stop(e); - removeClass(this.$el, this.clsDragover); - } - }, - methods: { - upload: function upload(files) { - var this$1 = this; - if (!files.length) { - return; - } - trigger(this.$el, "upload", [ files ]); - for (var i = 0; i < files.length; i++) { - if (this$1.maxSize && this$1.maxSize * 1e3 < files[i].size) { - this$1.fail(this$1.msgInvalidSize.replace("%s", this$1.allow)); - return; - } - if (this$1.allow && !match(this$1.allow, files[i].name)) { - this$1.fail(this$1.msgInvalidName.replace("%s", this$1.allow)); - return; - } - if (this$1.mime && !match(this$1.mime, files[i].type)) { - this$1.fail(this$1.msgInvalidMime.replace("%s", this$1.mime)); - return; - } - } - if (!this.multiple) { - files = [ files[0] ]; - } - this.beforeAll(this, files); - var chunks = chunk(files, this.concurrent); - var upload = function(files) { - var data = new FormData(); - files.forEach(function(file) { - return data.append(this$1.name, file); - }); - for (var key in this$1.params) { - data.append(key, this$1.params[key]); - } - ajax(this$1.url, { - data: data, - method: this$1.method, - responseType: this$1.type, - beforeSend: function(env) { - var xhr = env.xhr; - xhr.upload && on(xhr.upload, "progress", this$1.progress); - [ "loadStart", "load", "loadEnd", "abort" ].forEach(function(type) { - return on(xhr, type.toLowerCase(), this$1[type]); - }); - this$1.beforeSend(env); - } - }).then(function(xhr) { - this$1.complete(xhr); - if (chunks.length) { - upload(chunks.shift()); - } else { - this$1.completeAll(xhr); - } - }, function(e) { - return this$1.error(e.message); - }); - }; - upload(chunks.shift()); - } - } - }); - function match(pattern, path) { - return path.match(new RegExp("^" + pattern.replace(/\//g, "\\/").replace(/\*\*/g, "(\\/[^\\/]+)*").replace(/\*/g, "[^\\/]+").replace(/((?!\\))\?/g, "$1.") + "$", "i")); - } - function chunk(files, size) { - var chunks = []; - for (var i = 0; i < files.length; i += size) { - var chunk = []; - for (var j = 0; j < size; j++) { - chunk.push(files[i + j]); - } - chunks.push(chunk); - } - return chunks; - } - function stop(e) { - e.preventDefault(); - e.stopPropagation(); - } - } - UIkit$2.use(plugin); - UIkit$2.use(plugin$1); - UIkit$2.use(plugin$2); - UIkit$2.use(plugin$6); - UIkit$2.use(plugin$7); - UIkit$2.use(plugin$9); - UIkit$2.use(plugin$10); - UIkit$2.use(plugin$11); - UIkit$2.use(plugin$12); - UIkit$2.use(plugin$13); - { - boot(UIkit$2); - } - return UIkit$2; -}); - -(function(global, factory) { - typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define("uikiticons", factory) : global.UIkitIcons = factory(); -})(this, function() { - "use strict"; - var album = ' '; - var ban = ' '; - var behance = ' '; - var bell = ' '; - var bold = ' '; - var bolt = ' '; - var bookmark = ' '; - var calendar = ' '; - var camera = ' '; - var cart = ' '; - var check = ' '; - var clock = ' '; - var close = ' '; - var code = ' '; - var cog = ' '; - var comment = ' '; - var commenting = ' '; - var comments = ' '; - var copy = ' '; - var database = ' '; - var desktop = ' '; - var download = ' '; - var dribbble = ' '; - var expand = ' '; - var facebook = ' '; - var file = ' '; - var flickr = ' '; - var folder = ' '; - var forward = ' '; - var foursquare = ' '; - var future = ' '; - var github = ' '; - var gitter = ' '; - var google = ' '; - var grid = ' '; - var happy = ' '; - var hashtag = ' '; - var heart = ' '; - var history = ' '; - var home = ' '; - var image = ' '; - var info = ' '; - var instagram = ' '; - var italic = ' '; - var joomla = ' '; - var laptop = ' '; - var lifesaver = ' '; - var link = ' '; - var linkedin = ' '; - var list = ' '; - var location = ' '; - var lock = ' '; - var mail = ' '; - var menu = ' '; - var minus = ' '; - var more = ' '; - var move = ' '; - var nut = ' '; - var pagekit = ' '; - var pencil = ' '; - var phone = ' '; - var pinterest = ' '; - var play = ' '; - var plus = ' '; - var pull = ' '; - var push = ' '; - var question = ' '; - var receiver = ' '; - var refresh = ' '; - var reply = ' '; - var rss = ' '; - var search = ' '; - var server = ' '; - var settings = ' '; - var shrink = ' '; - var social = ' '; - var soundcloud = ' '; - var star = ' '; - var strikethrough = ' '; - var table = ' '; - var tablet = ' '; - var tag = ' '; - var thumbnails = ' '; - var trash = ' '; - var tripadvisor = ' '; - var tumblr = ' '; - var tv = ' '; - var twitter = ' '; - var uikit = ' '; - var unlock = ' '; - var upload = ' '; - var user = ' '; - var users = ' '; - var vimeo = ' '; - var warning = ' '; - var whatsapp = ' '; - var wordpress = ' '; - var world = ' '; - var xing = ' '; - var yelp = ' '; - var youtube = ' '; - var Icons = { - album: album, - ban: ban, - behance: behance, - bell: bell, - bold: bold, - bolt: bolt, - bookmark: bookmark, - calendar: calendar, - camera: camera, - cart: cart, - check: check, - clock: clock, - close: close, - code: code, - cog: cog, - comment: comment, - commenting: commenting, - comments: comments, - copy: copy, - database: database, - desktop: desktop, - download: download, - dribbble: dribbble, - expand: expand, - facebook: facebook, - file: file, - flickr: flickr, - folder: folder, - forward: forward, - foursquare: foursquare, - future: future, - github: github, - gitter: gitter, - google: google, - grid: grid, - happy: happy, - hashtag: hashtag, - heart: heart, - history: history, - home: home, - image: image, - info: info, - instagram: instagram, - italic: italic, - joomla: joomla, - laptop: laptop, - lifesaver: lifesaver, - link: link, - linkedin: linkedin, - list: list, - location: location, - lock: lock, - mail: mail, - menu: menu, - minus: minus, - more: more, - move: move, - nut: nut, - pagekit: pagekit, - pencil: pencil, - phone: phone, - pinterest: pinterest, - play: play, - plus: plus, - pull: pull, - push: push, - question: question, - receiver: receiver, - refresh: refresh, - reply: reply, - rss: rss, - search: search, - server: server, - settings: settings, - shrink: shrink, - social: social, - soundcloud: soundcloud, - star: star, - strikethrough: strikethrough, - table: table, - tablet: tablet, - tag: tag, - thumbnails: thumbnails, - trash: trash, - tripadvisor: tripadvisor, - tumblr: tumblr, - tv: tv, - twitter: twitter, - uikit: uikit, - unlock: unlock, - upload: upload, - user: user, - users: users, - vimeo: vimeo, - warning: warning, - whatsapp: whatsapp, - wordpress: wordpress, - world: world, - xing: xing, - yelp: yelp, - youtube: youtube, - "500px": ' ', - "arrow-down": ' ', - "arrow-left": ' ', - "arrow-right": ' ', - "arrow-up": ' ', - "chevron-down": ' ', - "chevron-left": ' ', - "chevron-right": ' ', - "chevron-up": ' ', - "cloud-download": ' ', - "cloud-upload": ' ', - "credit-card": ' ', - "file-edit": ' ', - "git-branch": ' ', - "git-fork": ' ', - "github-alt": ' ', - "google-plus": ' ', - "minus-circle": ' ', - "more-vertical": ' ', - "paint-bucket": ' ', - "phone-landscape": ' ', - "play-circle": ' ', - "plus-circle": ' ', - "quote-right": ' ', - "sign-in": ' ', - "sign-out": ' ', - "tablet-landscape": ' ', - "triangle-down": ' ', - "triangle-left": ' ', - "triangle-right": ' ', - "triangle-up": ' ', - "video-camera": ' ' - }; - function plugin(UIkit) { - if (plugin.installed) { - return; - } - UIkit.icon.add(Icons); - } - if (typeof window !== "undefined" && window.UIkit) { - window.UIkit.use(plugin); - } - return plugin; -}); - -(function() { - "use strict"; - var _$JSONLoader_2 = { - load: load - }; - function load(location, callback) { - var xhr = getXHR(); - xhr.open("GET", location, true); - xhr.onreadystatechange = createStateChangeListener(xhr, callback); - xhr.send(); - } - function createStateChangeListener(xhr, callback) { - return function() { - if (xhr.readyState === 4 && xhr.status === 200) { - try { - callback(null, JSON.parse(xhr.responseText)); - } catch (err) { - callback(err, null); - } - } - }; - } - function getXHR() { - return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); - } - "use strict"; - var _$OptionsValidator_3 = function OptionsValidator(params) { - if (!validateParams(params)) { - throw new Error("-- OptionsValidator: required options missing"); - } - if (!(this instanceof OptionsValidator)) { - return new OptionsValidator(params); - } - var requiredOptions = params.required; - this.getRequiredOptions = function() { - return requiredOptions; - }; - this.validate = function(parameters) { - var errors = []; - requiredOptions.forEach(function(requiredOptionName) { - if (typeof parameters[requiredOptionName] === "undefined") { - errors.push(requiredOptionName); - } - }); - return errors; - }; - function validateParams(params) { - if (!params) { - return false; - } - return typeof params.required !== "undefined" && params.required instanceof Array; - } - }; - "use strict"; - function fuzzysearch(needle, haystack) { - var tlen = haystack.length; - var qlen = needle.length; - if (qlen > tlen) { - return false; - } - if (qlen === tlen) { - return needle === haystack; - } - outer: for (var i = 0, j = 0; i < qlen; i++) { - var nch = needle.charCodeAt(i); - while (j < tlen) { - if (haystack.charCodeAt(j++) === nch) { - continue outer; - } - } - return false; - } - return true; - } - var _$fuzzysearch_1 = fuzzysearch; - "use strict"; - var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy(); - function FuzzySearchStrategy() { - this.matches = function(string, crit) { - return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase()); - }; - } - "use strict"; - var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy(); - function LiteralSearchStrategy() { - this.matches = function(str, crit) { - if (typeof str !== "string") { - return false; - } - str = str.trim(); - return str.toLowerCase().indexOf(crit.toLowerCase()) >= 0; - }; - } - "use strict"; - var _$Repository_4 = { - put: put, - clear: clear, - search: search, - setOptions: setOptions - }; - function NoSort() { - return 0; - } - var data = []; - var opt = {}; - opt.fuzzy = false; - opt.limit = 10; - opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6; - opt.sort = NoSort; - function put(data) { - if (isObject(data)) { - return addObject(data); - } - if (isArray(data)) { - return addArray(data); - } - return undefined; - } - function clear() { - data.length = 0; - return data; - } - function isObject(obj) { - return Boolean(obj) && Object.prototype.toString.call(obj) === "[object Object]"; - } - function isArray(obj) { - return Boolean(obj) && Object.prototype.toString.call(obj) === "[object Array]"; - } - function addObject(_data) { - data.push(_data); - return data; - } - function addArray(_data) { - var added = []; - clear(); - for (var i = 0, len = _data.length; i < len; i++) { - if (isObject(_data[i])) { - added.push(addObject(_data[i])); - } - } - return added; - } - function search(crit) { - if (!crit) { - return []; - } - return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort); - } - function setOptions(_opt) { - opt = _opt || {}; - opt.fuzzy = _opt.fuzzy || false; - opt.limit = _opt.limit || 10; - opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6; - opt.sort = _opt.sort || NoSort; - } - function findMatches(data, crit, strategy, opt) { - var matches = []; - for (var i = 0; i < data.length && matches.length < opt.limit; i++) { - var match = findMatchesInObject(data[i], crit, strategy, opt); - if (match) { - matches.push(match); - } - } - return matches; - } - function findMatchesInObject(obj, crit, strategy, opt) { - for (var key in obj) { - if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) { - return obj; - } - } - } - function isExcluded(term, excludedTerms) { - var excluded = false; - excludedTerms = excludedTerms || []; - for (var i = 0, len = excludedTerms.length; i < len; i++) { - var excludedTerm = excludedTerms[i]; - if (!excluded && new RegExp(term).test(excludedTerm)) { - excluded = true; - } - } - return excluded; - } - "use strict"; - var _$Templater_7 = { - compile: compile, - setOptions: __setOptions_7 - }; - var options = {}; - options.pattern = /\{(.*?)\}/g; - options.template = ""; - options.middleware = function() {}; - function __setOptions_7(_options) { - options.pattern = _options.pattern || options.pattern; - options.template = _options.template || options.template; - if (typeof _options.middleware === "function") { - options.middleware = _options.middleware; - } - } - function compile(data) { - return options.template.replace(options.pattern, function(match, prop) { - var value = options.middleware(prop, data[prop], options.template); - if (typeof value !== "undefined") { - return value; - } - return data[prop] || match; - }); - } - "use strict"; - var _$utils_9 = { - merge: merge, - isJSON: isJSON - }; - function merge(defaultParams, mergeParams) { - var mergedOptions = {}; - for (var option in defaultParams) { - if (Object.prototype.hasOwnProperty.call(defaultParams, option)) { - mergedOptions[option] = defaultParams[option]; - if (typeof mergeParams[option] !== "undefined") { - mergedOptions[option] = mergeParams[option]; - } - } - } - return mergedOptions; - } - function isJSON(json) { - try { - if (json instanceof Object && JSON.parse(JSON.stringify(json))) { - return true; - } - return false; - } catch (err) { - return false; - } - } - var _$src_8 = {}; - (function(window) { - "use strict"; - var options = { - searchInput: null, - resultsContainer: null, - json: [], - searchResultTemplate: '
    • {title}
    • ', - templateMiddleware: function() {}, - sortMiddleware: function() { - return 0; - }, - noResultsText: "No results found", - limit: 10, - fuzzy: false, - exclude: [] - }; - var requiredOptions = [ "searchInput", "resultsContainer", "json" ]; - var optionsValidator = _$OptionsValidator_3({ - required: requiredOptions - }); - window.SimpleJekyllSearch = function(_options) { - var errors = optionsValidator.validate(_options); - if (errors.length > 0) { - throwError("You must specify the following required options: " + requiredOptions); - } - options = _$utils_9.merge(options, _options); - _$Templater_7.setOptions({ - template: options.searchResultTemplate, - middleware: options.templateMiddleware - }); - _$Repository_4.setOptions({ - fuzzy: options.fuzzy, - limit: options.limit, - sort: options.sortMiddleware - }); - if (_$utils_9.isJSON(options.json)) { - initWithJSON(options.json); - } else { - initWithURL(options.json); - } - return { - search: search - }; - }; - window.SimpleJekyllSearch.init = window.SimpleJekyllSearch; - if (typeof window.SimpleJekyllSearchInit === "function") { - window.SimpleJekyllSearchInit.call(this, window.SimpleJekyllSearch); - } - function initWithJSON(json) { - _$Repository_4.put(json); - registerInput(); - } - function initWithURL(url) { - _$JSONLoader_2.load(url, function(err, json) { - if (err) { - throwError("failed to get JSON (" + url + ")"); - } - initWithJSON(json); - }); - } - function emptyResultsContainer() { - options.resultsContainer.innerHTML = ""; - } - function appendToResultsContainer(text) { - options.resultsContainer.innerHTML += text; - } - function registerInput() { - options.searchInput.addEventListener("keyup", function(e) { - if (isWhitelistedKey(e.which)) { - emptyResultsContainer(); - search(e.target.value); - } - }); - } - function search(query) { - if (isValidQuery(query)) { - emptyResultsContainer(); - render(_$Repository_4.search(query)); - } - } - function render(results) { - var len = results.length; - if (len === 0) { - return appendToResultsContainer(options.noResultsText); - } - for (var i = 0; i < len; i++) { - appendToResultsContainer(_$Templater_7.compile(results[i])); - } - } - function isValidQuery(query) { - return query && query.length > 0; - } - function isWhitelistedKey(key) { - return [ 13, 16, 20, 37, 38, 39, 40, 91 ].indexOf(key) === -1; - } - function throwError(message) { - throw new Error("SimpleJekyllSearch --- " + message); - } - })(window); -})(); \ No newline at end of file diff --git a/_site/assets/posts/7C04AAA0EE9E3886.png b/_site/assets/posts/7C04AAA0EE9E3886.png deleted file mode 100644 index 8af764e0b5..0000000000 Binary files a/_site/assets/posts/7C04AAA0EE9E3886.png and /dev/null differ diff --git a/_site/assets/posts/SVG/logo.svg b/_site/assets/posts/SVG/logo.svg deleted file mode 100644 index 886b00f891..0000000000 --- a/_site/assets/posts/SVG/logo.svg +++ /dev/null @@ -1 +0,0 @@ -logo \ No newline at end of file diff --git a/_site/assets/posts/books.svg b/_site/assets/posts/books.svg deleted file mode 100644 index 6e5638ac37..0000000000 --- a/_site/assets/posts/books.svg +++ /dev/null @@ -1,51 +0,0 @@ - - - - Books - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/_site/assets/posts/box.svg b/_site/assets/posts/box.svg deleted file mode 100644 index 625e607a25..0000000000 --- a/_site/assets/posts/box.svg +++ /dev/null @@ -1,100 +0,0 @@ - - - - Box - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/_site/assets/posts/city.svg b/_site/assets/posts/city.svg deleted file mode 100644 index 337229f80d..0000000000 --- a/_site/assets/posts/city.svg +++ /dev/null @@ -1,188 +0,0 @@ - - - - Style 9 - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/_site/assets/posts/imac.svg b/_site/assets/posts/imac.svg deleted file mode 100644 index 881398574d..0000000000 --- a/_site/assets/posts/imac.svg +++ /dev/null @@ -1,47 +0,0 @@ - - - - iMac - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/_site/assets/posts/image1.png b/_site/assets/posts/image1.png deleted file mode 100644 index a7125c3140..0000000000 Binary files a/_site/assets/posts/image1.png and /dev/null differ diff --git a/_site/assets/posts/logo.png b/_site/assets/posts/logo.png deleted file mode 100644 index 4b1793c8b5..0000000000 Binary files a/_site/assets/posts/logo.png and /dev/null differ diff --git a/_site/assets/posts/logo.svg b/_site/assets/posts/logo.svg deleted file mode 100644 index 4cf1acadf5..0000000000 --- a/_site/assets/posts/logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - -logo - - - diff --git a/_site/assets/posts/old.logo.svg b/_site/assets/posts/old.logo.svg deleted file mode 100755 index c12f862550..0000000000 --- a/_site/assets/posts/old.logo.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/_site/assets/posts/safe.svg b/_site/assets/posts/safe.svg deleted file mode 100644 index 4ece36002d..0000000000 --- a/_site/assets/posts/safe.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - Safe - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/_site/assets/posts/sky.svg b/_site/assets/posts/sky.svg deleted file mode 100644 index 3913866cb0..0000000000 --- a/_site/assets/posts/sky.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - Style 1 - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/_site/assets/posts/slideshow-1/image1.png b/_site/assets/posts/slideshow-1/image1.png deleted file mode 100644 index a7125c3140..0000000000 Binary files a/_site/assets/posts/slideshow-1/image1.png and /dev/null differ diff --git a/_site/assets/posts/teacup.svg b/_site/assets/posts/teacup.svg deleted file mode 100644 index 729cf60ed3..0000000000 --- a/_site/assets/posts/teacup.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - Teacup - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/_site/assets/posts/touch-icon.png b/_site/assets/posts/touch-icon.png deleted file mode 100644 index 243615bb9f..0000000000 Binary files a/_site/assets/posts/touch-icon.png and /dev/null differ diff --git a/_site/assets/posts/touch-icon.svg b/_site/assets/posts/touch-icon.svg deleted file mode 100644 index cc1205e398..0000000000 --- a/_site/assets/posts/touch-icon.svg +++ /dev/null @@ -1 +0,0 @@ -Asset 1 \ No newline at end of file diff --git a/_site/changelog/index.html b/_site/changelog/index.html deleted file mode 100644 index 26f5ef7cc4..0000000000 --- a/_site/changelog/index.html +++ /dev/null @@ -1,349 +0,0 @@ - - - - - - - - -Changelog | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      - -
      - -

      Changelog

      - -
      -

      Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.

      - -
      - -
      - -
      -
      -
      Oct 21, 2017
      -
      -
      -

      Version 1.0.0 STABLE

      -
        - -
      • Added Slideshow component
      • - -
      • Added style support for radio and minusbox in Firefox
      • - -
      • Removed class from Section component
      • - -
      • Allow fullscreen mode for videos in Lightbox
      • - -
      • Fixed responsive images in modal for IE11
      • - -
      • Fix Grid and Margin component for cells with no height
      • - -
      • Larger horizontal padding for form input and textarea
      • - -
      -
      -
      - -
      -
      -
      Sep 01, 2017
      -
      -
      -

      Version 1.0.0 BETA 1

      -
        - -
      • Allow fullscreen mode for YouTube videos in Lightbox
      • - -
      • Fix icons not displaying if connected in rapid succession
      • - -
      • Fix scrollbar jumping in Switcher
      • - -
      -
      -
      - -
      -
      -
      Aug 15, 2017
      -
      -
      -

      Version 0.6.0

      -
        - -
      • Added style support for radio and checkbox in Firefox
      • - -
      • Removed class from Section component
      • - -
      • Add workaround to mitigate the duplicating icons issue
      • - -
      • Fixed responsive images in modal for IE11
      • - -
      -
      -
      - -
      -
      -
      Oct 21, 2017
      -
      -
      -

      Version 0.5.0

      -
        - -
      • Media options now support any valid media query syntax
      • - -
      • Added style support for radio and checkbox in Firefox
      • - -
      • Fix whitespace trimming in dist
      • - -
      -
      -
      - -
      - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/contact/index.html b/_site/contact/index.html deleted file mode 100644 index ca5b200e7a..0000000000 --- a/_site/contact/index.html +++ /dev/null @@ -1,343 +0,0 @@ - - - - - - - - -Got Any Questions | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      - -
      - -

      Got Any Questions

      - -
      -
      Morbi varius in accumsan blandit, elit ligula velit, luctus mattis ante nulla nulla.
      - -

      Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.

      - -
      - - - - - -
      - -
      - - -

      Contact Form

      -
      -
      - -
      - -
      -
      -
      - -
      - -
      -
      -
      - -
      - -
      -
      -
      - -
      - -
      -
      -
      - - - - - -
      -
      - - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/README.md b/_site/core/README.md deleted file mode 100644 index 8f6f0c78c3..0000000000 --- a/_site/core/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# Documentation - -All Blockstack software documentation is either in this directory, or linked -from this file. - -## FAQs - -* [General FAQ](https://blockstack.org/faq) -* [Technical FAQ](faq_technical.md) - -## System Design and Implementation - -* [Blockstack Naming Service](blockstack_naming_service.md) (start here) -* [Atlas Peer Network](atlas_network.md) -* [Gaia Storage System](https://github.com/blockstack/gaia) -* [Transaction Formats](wire-format.md) -* [Glossary](glossary.md) - -## Command-line Documentation - -* [CLI walkthrough](basic_usage.md) -* [CLI reference](cli.md) - -## Developer Documentation - -* [blockstack.js](https://github.com/blockstack/blockstack.js) -* [Blockstack Browser](https://github.com/blockstack/blockstack-browser) -* [Blockstack Core API](https://core.blockstack.org) -* [Integration Test Framework](../integration_tests/README.md) - -## Advanced Topics - -* [How to Create a Namespace](namespace_creation.md) -* [How to link your OpenBazaar Store to your Blockstack ID](openbazaar.md) -* [How to build a Profile Search Index](search.md) -* [How to Run a Blockstack Subdomain Registrar](subdomains.md) diff --git a/_site/core/advanced_usage.md b/_site/core/advanced_usage.md deleted file mode 100644 index 2f9db4c2de..0000000000 --- a/_site/core/advanced_usage.md +++ /dev/null @@ -1,4 +0,0 @@ -# Advanced Usage - -This document is deprecated, and contains references to commands that are no -longer valid. It has been moved to the [attic](attic/advanced_usage.md). diff --git a/_site/core/aglio_templates/core.jade b/_site/core/aglio_templates/core.jade deleted file mode 100644 index de87cfe99d..0000000000 --- a/_site/core/aglio_templates/core.jade +++ /dev/null @@ -1,36 +0,0 @@ -doctype - -include mixins.jade - -html - head - meta(charset="utf-8") - title= self.api.name || 'API Documentation' - link(rel="stylesheet", href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css") - style!= self.css - body.preload - #nav-background - div.container-fluid.triple - .row - block nav - +Nav(false) - - .content - #right-panel-background - - block content - +ContentTriple(false) - - .middle - p.text-muted(style="text-align: center;") - - script: include scripts.js - - if self.livePreview - script(src="/socket.io/socket.io.js") - script. - var socket = io(); - socket.on('refresh', refresh); - socket.on('reconnect', function () { - socket.emit('request-refresh'); - }); diff --git a/_site/core/aglio_templates/mixins.jade b/_site/core/aglio_templates/mixins.jade deleted file mode 100644 index ddc54690a2..0000000000 --- a/_site/core/aglio_templates/mixins.jade +++ /dev/null @@ -1,357 +0,0 @@ -mixin TryMe(action) - //- Give a "try-me" link for the public api endpoint - - var myUri = action.uriTemplate - - action.parameters.forEach( function (x) { myUri = myUri.replace( "{" + x.name + "}", x.example) } ) - .title - strong - h4 - div - div - span.method(class="badge get",style="float:left") - a.method(href=myUri, style="color:rgb(51, 122, 183);font-size:12pt") - = "Try It!" - |   - p - div - |   - -mixin Badge(method) - //- Draw a badge for a given HTTP method - case method - when 'GET' - span.badge.get: i.fa.fa-arrow-down - when 'HEAD' - span.badge.head: i.fa.fa-info-circle - when 'OPTIONS' - span.badge.options: i.fa.fa-dot-circle-o - when 'POST' - span.badge.post: i.fa.fa-plus - when 'PUT' - span.badge.put: i.fa.fa-pencil - when 'PATCH' - span.badge.patch: i.fa.fa-pencil - when 'DELETE' - span.badge.delete: i.fa.fa-times - default - span.badge: i.fa.fa-dot-circle-o - -mixin Nav(onlyPublic) - //- Draw a navigation bar, which includes links to individual - //- resources and actions. - nav - if self.api.navItems && self.api.navItems.length - .resource-group - .heading - .chevron - i.open.fa.fa-angle-down - a(href='#top') Overview - .collapse-content - ul: each item in self.api.navItems - li - a(href=item[1])!= item[0] - - if (onlyPublic){ - - myGroups = self.api.resourceGroups.filter( filter_public_resourcegroups ) - - }else{ - - myGroups = self.api.resourceGroups.filter( filter_core_resourcegroups ) - - } - each resourceGroup in myGroups || [] - .resource-group - .heading - .chevron - i.open.fa.fa-angle-down - a(href=resourceGroup.elementLink)!= resourceGroup.name || 'Resource Group' - .collapse-content - ul - each item in resourceGroup.navItems || [] - li - a(href=item[1])!= item[0] - - if (onlyPublic){ - - myResources = resourceGroup.resources.filter( filter_public_resources ) - - }else{ - - myResources = resourceGroup.resources.filter( filter_core_resources ) - - } - each resource in myResources || [] - li - - if (onlyPublic){ - - myActions = resource.actions.filter( filter_public_actions ) - - }else{ - - myActions = resource.actions.filter( filter_core_actions ) - - } - if !self.condenseNav || (myActions.length != 1) - a(href=resource.elementLink)!= resource.name || 'Resource' - ul: each action in myActions || [] - li: a(href=resource.elementLink) - +Badge(action.method) - != action.name || action.method + ' ' + (action.attributes && action.attributes.uriTemplate || resource.uriTemplate) - else - - var action = myActions[0] - a(href=resource.elementLink) - +Badge(action.method) - != action.name || resource.name || action.method + ' ' + (action.attributes && action.attributes.uriTemplate || resource.uriTemplate) - //- Link to the API hostname, e.g. api.yourcompany.com - each meta in self.api.metadata || {} - if meta.name == 'HOST' - p(style="text-align: center; word-wrap: break-word;") - a(href=meta.value)= meta.value - -mixin Parameters(params) - //- Draw a definition list of parameter names, types, defaults, - //- examples and descriptions. - .title - strong URI Parameters - .collapse-button.show - span.close Hide - span.open Show - .collapse-content - dl.inner: each param in params || [] - dt= self.urldec(param.name) - dd - code= param.type || 'string' - |   - if param.required - span.required (required) - else - span (optional) - |   - if param.default - span.text-info.default - strong Default:  - span= param.default - |   - if param.example - span.text-muted.example - strong Example:  - span= param.example - != self.markdown(param.description) - if param.values.length - p.choices - strong Choices:  - each value in param.values - code= self.urldec(value.value) - = ' ' - -mixin RequestResponse(title, request, collapse) - .title - strong - = title - if request.name - |    - code= request.name - if collapse && request.hasContent - .collapse-button - span.close Hide - span.open Show - +RequestResponseBody(request, collapse) - -mixin RequestResponseBody(request, collapse, showBlank) - if request.hasContent || showBlank - div(class=collapse ? 'collapse-content' : ''): .inner - if request.description - .description!= self.markdown(request.description) - - if Object.keys(request.headers).length - h5 Headers - pre: code - each item, index in request.headers - != self.highlight(item.name + ': ' + item.value, 'http') - if index < request.headers.length - 1 - br - div(style="height: 1px;") - if request.body - h5 Body - pre: code - != self.highlight(request.body, null, ['json', 'yaml', 'xml', 'javascript']) - div(style="height: 1px;") - if request.schema - h5 Schema - pre: code - != self.highlight(request.schema, null, ['json', 'yaml', 'xml']) - div(style="height: 1px;") - if !request.hasContent - .description.text-muted This response has no content. - div(style="height: 1px;") - -mixin Examples(resourceGroup, resource, action) - each example in action.examples - each request in example.requests - +RequestResponse('Request', request, true) - each response in example.responses - +RequestResponse('Response', response, true) - -mixin Content() - //- Page header and API description - header - h1#top!= self.api.name || 'API Documentation' - - if self.api.descriptionHtml - != self.api.descriptionHtml - - //- Loop through and display information about all the resource - //- groups, resources, and actions. - each resourceGroup in self.api.resourceGroups || [] - section.resource-group(id=resourceGroup.elementId) - h2.group-heading - != resourceGroup.name || 'Resource Group' - = " " - a.permalink(href=resourceGroup.elementLink) ¶ - if resourceGroup.descriptionHtml - != resourceGroup.descriptionHtml - - each resource in resourceGroup.resources || [] - .resource(id=resource.elementId) - h3.resource-heading - != resource.name || ((resource.actions[0] != null) && resource.actions[0].name) || 'Resource' - = " " - a.permalink(href=resource.elementLink)  ¶ - if resource.description - != self.markdown(resource.description) - - each action in resource.actions || [] - .action(class=action.methodLower, id=action.elementId) - h4.action-heading - .name!= action.name - a.method(class=action.methodLower, href=action.elementLink) - = action.method - code.uri= self.urldec(action.uriTemplate) - if action.description - != self.markdown(action.description) - - h4 Example URI - .definition - span.method(class=action.methodLower)= action.method - |   - span.uri - span.hostname= self.api.host - != action.colorizedUriTemplate - - //- A list of sub-sections for parameters, requests - //- and responses. - if action.parameters.length - +Parameters(action.parameters) - if action.examples - +Examples(resourceGroup, resource, action) - -- function filter_public_actions(x){ -- return (x.description.includes('+ Public Endpoint') || x.description.includes('+ Public Only Endpoint')) -- } -- function filter_public_resources(x){ -- return (x.actions.filter( filter_public_actions ).length > 0) -- } -- function filter_public_resourcegroups(x){ -- return (x.resources.filter( filter_public_resources ).length > 0) -- } -- function filter_core_actions(x){ -- return !(x.description.includes('+ Public Only Endpoint')) -- } -- function filter_core_resources(x){ -- return (x.actions.filter( filter_core_actions ).length > 0) -- } -- function filter_core_resourcegroups(x){ -- return (x.resources.filter( filter_core_resources ).length > 0) -- } - -mixin ContentTriple(onlyPublic, descriptionHtml) - - .right - h5 API Endpoint - a(href=self.api.host)= self.api.host - .middle - if descriptionHtml - != descriptionHtml - - //- Loop through and display information about all the resource - //- groups, resources, and actions. - - if (onlyPublic){ - - myGroups = self.api.resourceGroups.filter( filter_public_resourcegroups ) - - }else{ - - myGroups = self.api.resourceGroups.filter( filter_core_resourcegroups ) - - } - each resourceGroup in myGroups || [] - .middle - section.resource-group(id=resourceGroup.elementId) - h2.group-heading - != resourceGroup.name || 'Resource Group' - = " " - a.permalink(href=resourceGroup.elementLink) ¶ - if resourceGroup.descriptionHtml - != resourceGroup.descriptionHtml - - - if (onlyPublic){ - - myResources = resourceGroup.resources.filter( filter_public_resources ) - - }else{ - - myResources = resourceGroup.resources.filter( filter_core_resources ) - - } - each resource in myResources || [] - if resource.public != null - .middle - .resource(id=resource.elementId) - a.permalink(href=resource.elementLink) - h3.resource-heading - != resource.name || ((resource.actions[0] != null) && resource.actions[0].name) || 'Resource' - = " " - ¶ - if resource.description - != self.markdown(resource.description) - - - if (onlyPublic){ - - myActions = resource.actions.filter( filter_public_actions ) - - }else{ - - myActions = resource.actions.filter( filter_core_actions ) - - } - each action in myActions || [] - if action.examples - .right - .definition - span.method(class=action.methodLower)= action.method - |   - span.uri - span.hostname= self.api.host - != action.colorizedUriTemplate - .tabs - if action.hasRequest - .example-names - span Requests - - var requestCount = 0 - each example in action.examples - each request in example.requests - - requestCount++ - span.tab-button= request.name || 'example ' + requestCount - each example in action.examples - each request in example.requests - .tab - +RequestResponseBody(request, false, true) - .tabs - .example-names - span Responses - each response in example.responses - span.tab-button= response.name - each response in example.responses - .tab - +RequestResponseBody(response, false, true) - else - each example in action.examples - .tabs - .example-names - span Responses - each response in example.responses - span.tab-button= response.name - each response in example.responses - .tab - +RequestResponseBody(response, false, true) - .middle - .action(class=action.methodLower, id=action.elementId) - h4.action-heading - .name!= action.name - a.method(class=action.methodLower, href=action.elementLink) - = action.method - code.uri= self.urldec(action.uriTemplate) - if action.description - != self.markdown(action.description) - - //- A list of sub-sections for parameters, requests - //- and responses. - if action.parameters.length - +Parameters(action.parameters) - if onlyPublic - +TryMe(action) - hr.split diff --git a/_site/core/aglio_templates/public.jade b/_site/core/aglio_templates/public.jade deleted file mode 100644 index b5ae6b1592..0000000000 --- a/_site/core/aglio_templates/public.jade +++ /dev/null @@ -1,53 +0,0 @@ -doctype - - -include mixins.jade - -html - head - meta(charset="utf-8") - title= 'Blockstack Core' - link(rel="stylesheet", href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css") - style!= self.css - body.preload - #nav-background - div.container-fluid.triple - .row - block nav - +Nav(true) - - .content - #right-panel-background - .middle - header - h1#top!= 'Getting Started' - p!= 'Welcome to this deployment of Blockstack Core v{{server_info.server_version}}. You can read the documentation and make RESTful calls to this node.' - p - table - tr - td!= 'Consensus hash' - td!= '{{server_info.consensus}}' - tr - - td!= 'Last block seen' - td!= '{{server_info.last_block_seen}}' - tr - td!= 'Last block processed' - td!= '{{server_info.last_block_processed}}' - p!= 'Blockstack Core is open-source software released under a GPLv3 license. The code for this API is available on Github and you can deploy your own nodes by following these instructions.' - block content - +ContentTriple(true) - - .middle - p.text-muted(style="text-align: center;") - - script: include scripts.js - - if self.livePreview - script(src="/socket.io/socket.io.js") - script. - var socket = io(); - socket.on('refresh', refresh); - socket.on('reconnect', function () { - socket.emit('request-refresh'); - }); diff --git a/_site/core/aglio_templates/scripts.js b/_site/core/aglio_templates/scripts.js deleted file mode 100644 index d105df6c43..0000000000 --- a/_site/core/aglio_templates/scripts.js +++ /dev/null @@ -1,223 +0,0 @@ -/* eslint-env browser */ -/* eslint quotes: [2, "single"] */ -'use strict'; - -/* - Determine if a string ends with another string. -*/ -function endsWith(str, suffix) { - return str.indexOf(suffix, str.length - suffix.length) !== -1; -} - -/* - Get a list of direct child elements by class name. -*/ -function childrenByClass(element, name) { - var filtered = []; - - for (var i = 0; i < element.children.length; i++) { - var child = element.children[i]; - var classNames = child.className.split(' '); - if (classNames.indexOf(name) !== -1) { - filtered.push(child); - } - } - - return filtered; -} - -/* - Get an array [width, height] of the window. -*/ -function getWindowDimensions() { - var w = window, - d = document, - e = d.documentElement, - g = d.body, - x = w.innerWidth || e.clientWidth || g.clientWidth, - y = w.innerHeight || e.clientHeight || g.clientHeight; - - return [x, y]; -} - -/* - Collapse or show a request/response example. -*/ -function toggleCollapseButton(event) { - var button = event.target.parentNode; - var content = button.parentNode.nextSibling; - var inner = content.children[0]; - - if (button.className.indexOf('collapse-button') === -1) { - // Clicked without hitting the right element? - return; - } - - if (content.style.maxHeight && content.style.maxHeight !== '0px') { - // Currently showing, so let's hide it - button.className = 'collapse-button'; - content.style.maxHeight = '0px'; - } else { - // Currently hidden, so let's show it - button.className = 'collapse-button show'; - content.style.maxHeight = inner.offsetHeight + 12 + 'px'; - } -} - -function toggleTabButton(event) { - var i, index; - var button = event.target; - - // Get index of the current button. - var buttons = childrenByClass(button.parentNode, 'tab-button'); - for (i = 0; i < buttons.length; i++) { - if (buttons[i] === button) { - index = i; - button.className = 'tab-button active'; - } else { - buttons[i].className = 'tab-button'; - } - } - - // Hide other tabs and show this one. - var tabs = childrenByClass(button.parentNode.parentNode, 'tab'); - for (i = 0; i < tabs.length; i++) { - if (i === index) { - tabs[i].style.display = 'block'; - } else { - tabs[i].style.display = 'none'; - } - } -} - -/* - Collapse or show a navigation menu. It will not be hidden unless it - is currently selected or `force` has been passed. -*/ -function toggleCollapseNav(event, force) { - var heading = event.target.parentNode; - var content = heading.nextSibling; - var inner = content.children[0]; - - if (heading.className.indexOf('heading') === -1) { - // Clicked without hitting the right element? - return; - } - - if (content.style.maxHeight && content.style.maxHeight !== '0px') { - // Currently showing, so let's hide it, but only if this nav item - // is already selected. This prevents newly selected items from - // collapsing in an annoying fashion. - if (force || window.location.hash && endsWith(event.target.href, window.location.hash)) { - content.style.maxHeight = '0px'; - } - } else { - // Currently hidden, so let's show it - content.style.maxHeight = inner.offsetHeight + 12 + 'px'; - } -} - -/* - Refresh the page after a live update from the server. This only - works in live preview mode (using the `--server` parameter). -*/ -function refresh(body) { - document.querySelector('body').className = 'preload'; - document.body.innerHTML = body; - - // Re-initialize the page - init(); - autoCollapse(); - - document.querySelector('body').className = ''; -} - -/* - Determine which navigation items should be auto-collapsed to show as many - as possible on the screen, based on the current window height. This also - collapses them. -*/ -function autoCollapse() { - var windowHeight = getWindowDimensions()[1]; - var itemsHeight = 64; /* Account for some padding */ - var itemsArray = Array.prototype.slice.call( - document.querySelectorAll('nav .resource-group .heading')); - - // Get the total height of the navigation items - itemsArray.forEach(function (item) { - itemsHeight += item.parentNode.offsetHeight; - }); - - // Should we auto-collapse any nav items? Try to find the smallest item - // that can be collapsed to show all items on the screen. If not possible, - // then collapse the largest item and do it again. First, sort the items - // by height from smallest to largest. - var sortedItems = itemsArray.sort(function (a, b) { - return a.parentNode.offsetHeight - b.parentNode.offsetHeight; - }); - - while (sortedItems.length && itemsHeight > windowHeight) { - for (var i = 0; i < sortedItems.length; i++) { - // Will collapsing this item help? - var itemHeight = sortedItems[i].nextSibling.offsetHeight; - if ((itemsHeight - itemHeight <= windowHeight) || i === sortedItems.length - 1) { - // It will, so let's collapse it, remove its content height from - // our total and then remove it from our list of candidates - // that can be collapsed. - itemsHeight -= itemHeight; - toggleCollapseNav({target: sortedItems[i].children[0]}, true); - sortedItems.splice(i, 1); - break; - } - } - } -} - -/* - Initialize the interactive functionality of the page. -*/ -function init() { - var i, j; - - // Make collapse buttons clickable - var buttons = document.querySelectorAll('.collapse-button'); - for (i = 0; i < buttons.length; i++) { - buttons[i].onclick = toggleCollapseButton; - - // Show by default? Then toggle now. - if (buttons[i].className.indexOf('show') !== -1) { - toggleCollapseButton({target: buttons[i].children[0]}); - } - } - - var responseCodes = document.querySelectorAll('.example-names'); - for (i = 0; i < responseCodes.length; i++) { - var tabButtons = childrenByClass(responseCodes[i], 'tab-button'); - for (j = 0; j < tabButtons.length; j++) { - tabButtons[j].onclick = toggleTabButton; - - // Show by default? - if (j === 0) { - toggleTabButton({target: tabButtons[j]}); - } - } - } - - // Make nav items clickable to collapse/expand their content. - var navItems = document.querySelectorAll('nav .resource-group .heading'); - for (i = 0; i < navItems.length; i++) { - navItems[i].onclick = toggleCollapseNav; - - // Show all by default - toggleCollapseNav({target: navItems[i].children[0]}); - } -} - -// Initial call to set up buttons -init(); - -window.onload = function () { - autoCollapse(); - // Remove the `preload` class to enable animations - document.querySelector('body').className = ''; -}; diff --git a/_site/core/api-specs.md b/_site/core/api-specs.md deleted file mode 100644 index 80c445e37b..0000000000 --- a/_site/core/api-specs.md +++ /dev/null @@ -1,3061 +0,0 @@ -# Group Authorization - -## Auth Request View [GET /auth?authRequest={authRequestToken}] - -This endpoint is accessed internally by -[blockstack.js](https://github.com/blockstack/blockstack.js) to process user -sign-in requests. Applications use `blockstack.js` to direct users to sign in -or sign up. Please see the [blockstack.js -documentation](http://blockstack.github.io/blockstack.js/#authentication) on -authentication for details. - -When the user clicks the Blockstack login button in an application, the app should -redirect the user to this endpoint (via `blockstack.js`). If the user already has an -account, they will be redirected along with requested data. If the -user doesn’t have an account, the user will be presented with each of -the app’s requested permissions, then will satisfy or deny them. The -sign-in dashboard will then redirect the user back to the application -with a signed JWT. This JWT contains a signature and an API -token that the app can use for future authorization of endpoints. - -Each application specifies in advance which family of API calls it -will need to make to function properly. This list is passed along to -this endpoint when creating an application account. The -account-creation page shows this list of API endpoints and what they -do, and allows the user to line-item approve or deny them. The list -is stored to the user's profile, and returned to the application -application as part of the session JWT. The API -server will NACK requests to endpoints in API families absent from the -session JWT. - -+ Requires root authorization -+ Parameters - + authRequestToken: eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJhcHBfZG9tYWluIjoiaGVsbG9ibG9ja3N0YWNrLmNvbSIsIm1ldGhvZHMiOltdLCJhcHBfcHVibGljX2tleSI6IjAyYjk0ZjY4NDgzOGFkMjdmZTE0Nzk1MGMyNjQ1ZjRhYzhjYmU1OTJlYjYzYmQwYTQ5MWQ2YzBlYWZjNjE0YzVjMCJ9.0lLrxt8uGtB2rCKB9sb0jK1DdrrWuuuWM-nsyjvFnmjNx0XfG14Npl72w6hp9W2OHoXdPe7VuXkfvKmVNlQdeA (jwt token) - app token before signing -+ Response 200 - + Body - - {"token": - "eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJhcHBfZG9tYWluIjoiaGVsbG9ibG9ja3N0YWNrLmNvbSIsIm1ldGhvZHMiOltdLCJ0aW1lc3RhbXAiOjE0OTkzNDc4OTUsImV4cGlyZXMiOjE0OTk5NTI2OTUsImFwcF91c2VyX2lkIjoiMUVITmE2UTRKejJ1dk5FeEw0OTdtRTQzaWtYaHdGNmtabSIsImRldmljZV9pZCI6IjAiLCJibG9ja2NoYWluX2lkIjpudWxsLCJzdG9yYWdlIjp7ImNsYXNzZXMiOnsid3JpdGVfcHJpdmF0ZSI6WyJkaXNrIiwiczMiLCJibG9ja3N0YWNrX3NlcnZlciIsImRodCJdLCJyZWFkX2xvY2FsIjpbImRpc2siXSwicmVhZF9wdWJsaWMiOlsiczMiLCJibG9ja3N0YWNrX3Jlc29sdmVyIiwiYmxvY2tzdGFja19zZXJ2ZXIiLCJodHRwIiwiZGh0Il0sIndyaXRlX2xvY2FsIjpbImRpc2siXSwid3JpdGVfcHVibGljIjpbXSwicmVhZF9wcml2YXRlIjpbImRpc2siXX0sInByZWZlcmVuY2VzIjp7fX0sImFwaV9lbmRwb2ludCI6ImxvY2FsaG9zdDo2MjcwIiwiYXBwX3B1YmxpY19rZXlzIjpbXSwidmVyc2lvbiI6MX0.Bhne8wQpPVfkV-VLf2mrsoMmNiE2e04crgLN7OUFKEh_YWeGmqjoZU7JVSzXA5r7LCpZ9Eki5uAWlJSHk-JuCA" - } - -# Group Core Node Administration - -Blockstack Core's API module provides a set of API calls for interacting with -the node's configuration. However, most of this section is **DEPRECATED** in favor -of moving configuration state to the [Blockstack -Browser](https://github.com/blockstack/blockstack-browser). Client-side state -is managed by [blockstack.js](https://github.com/blockstack/blockstack.js). - -## Ping the node [GET /v1/node/ping] -Ping the Blockstack node to see if it's alive. -+ Public Endpoint -+ Response 200 (application/json) - + Body - - { - "status": "alive", - "version": "###version###" - } - + Schema - - { - "type": "object", - "properties": { - "status": { - "type": "string" - }, - "version": { - "type": "string" - } - }, - "required": [ - "status", - "version" - ] - } - -## Get the node's config [GET /v1/node/config] -Returns the current configuation settings of the Blockstack node. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) for querying -client-side configuration state. -+ Requires root authorization -+ Legacy Endpoint -+ Response 200 (application/json) - + Body - - { - "bitcoind": { - "passwd": "blockstacksystem", - "port": "18332", - "regtest": "True", - "server": "localhost", - "spv_path": "/tmp/.../spv_headers.dat", - "use_https": "False", - "user": "blockstack" - }, - "blockchain-reader": { - "port": "18332", - "rpc_password": "blockstacksystem", - "rpc_username": "blockstack", - "server": "localhost", - "use_https": "False", - "utxo_provider": "bitcoind_utxo", - "version_byte": "0" - }, - "blockchain-writer": { - "port": "18332", - "rpc_password": "blockstacksystem", - "rpc_username": "blockstack", - "server": "localhost", - "use_https": "False", - "utxo_provider": "bitcoind_utxo", - "version_byte": "0" - }, - "blockstack-client": { - "advanced_mode": "true", - "api_endpoint_port": "6270", - "api_password": "...", - "blockchain_reader": "bitcoind_utxo", - "blockchain_writer": "bitcoind_utxo", - "client_version": "0.18.0.0", - "poll_interval": "300", - "port": "16264", - "queue_path": "/tmp/.../client/queues.db", - "rpc_detach": "True", - "server": "localhost", - "storage_drivers": "disk", - "storage_drivers_required_write": "disk", - } - } - - + Schema - - { - 'type': 'object', - 'patternProperties': { - '.+': { - 'type': 'string', - 'pattern': '.+', - }, - } - -## Set config field [POST /v1/node/config/{section}?{key}={value}] -Set one or more config fields in a config section. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) for client-side -configuration management. -+ Requires root authorization -+ Legacy Endpoint -+ Parameters - + section: blockstack-client (string) - configuration section - + key: server (string) - configuration variable to set - + value: node.blockstack.org (string) - value to set - -+ Response 200 (application/json) - + Body - - { 'status' : true } - - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'status': { - 'type': 'boolean' - }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - }, - }, - }, - ], - } - -## Delete a config field [DELETE /v1/node/config/{section}/{key}] -Delete a single field from the configuration. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) for client-side -configuration management. -+ Requires root authorization -+ Legacy Endpoint -+ Parameters - + section: blockstack-client (string) - configuration section - + key: advanced_mode (string) - configuration variable to set - -+ Response 200 (application/json) - - + Body - - { 'status' : true } - - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'status': { - 'type': 'boolean' - }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - }, - }, - }, - ], - } - -## Delete a config section [DELETE /v1/node/config/{section}] -Deletes a whole section from the node's configuration. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) for client-side -configuration management. -+ Requires root authorization -+ Legacy Endpoint -+ Parameters - + section: blockstack-client (string) - configuration section - -+ Response 200 (application/json) - + Body - - { 'status' : true } - - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'status': { - 'type': 'boolean' - }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - }, - }, - }, - ], - } - -## Get registrar state [GET /v1/node/registrar/state] -Gets the current state of the registrar. That is, the Blockstack operations -that have been submitted to the blockchain but are still waiting for -enough confirmations. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to query -the status of pending transactions. -+ Requires root authorization -+ Legacy Endpoint -+ Response 200 (application/json) - + Body - - [ - { - "block_height": 666, - "fqu": "bar.test", - "owner_address": "myaPViveUWiiZQQTb51KXCDde4iLC3Rf3K", - "payment_address": "mv1uqYWZpnap4VBSKTHfKW6noTZcNtxtCW", - "profile": { - "@type": "Person", - "accounts": [] - }, - "transfer_address": null, - "tx_hash": "b0fa7d4d79bb69cb3eccf40978514dec1620d05fe7822c550c2764c670efcd29", - "type": "preorder", - "zonefile": "$ORIGIN bar.test\n$TTL 3600\npubkey TXT \"pubkey:data:03ea5d8c2a3ba84eb17625162320bb53440557c71f7977a57d61405e86be7bdcda\"\n_file URI 10 1 \"file:///home/bar/.blockstack/storage-disk/mutable/bar.test\"\n", - "zonefile_hash": "cbe11bbbfffe415b915a7f9566748f72a0d8b2bd" - } - ] - - + Schema - - { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'block_height': { - 'type': 'integer', - 'minimum': 0, - }, - 'fqu': { - 'type': 'string', - 'pattern': r'^([a-z0-9\\-_.+]{3,37})$', - }, - 'owner_address': { - 'type': 'string', - 'pattern': r'^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - }, - 'payment_address': { - 'type': 'string', - 'pattern': r'^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - }, - 'profile': { - 'type': 'object', - 'additionalProperties': true, - 'properties': { - '@context': { - 'optional': true, - 'type': 'string' - }, - '@id': { - 'optional': true, - 'type': 'string' - }, - '@type': { - 'type': 'string' - }, - 'account': { - 'items': { - 'properties': { - '@type': { - 'type': 'string' - }, - 'identifier': { - 'optional': true, - 'type': 'string' - }, - 'proofMessage': { - 'optional': true, - 'type': 'string' - }, - 'proofSignature': { - 'optional': true, - 'type': 'string' - }, - 'proofType': { - 'optional': true, - 'type': 'string' - }, - 'proofUrl': { - 'optional': true, - 'type': 'string' - }, - 'service': { - 'optional': true, - 'type': 'string' - } - }, - 'type': 'object' - }, - 'optional': true, - 'type': 'array' - }, - 'address': { - 'optional': true, - 'properties': { - '@type': { - 'type': 'string' - }, - 'addressCountry': { - 'optional': true, - 'type': 'string' - }, - 'addressLocality': { - 'optional': true, - 'type': 'string' - }, - 'postalCode': { - 'optional': true, - 'type': 'string' - }, - 'streetAddress': { - 'optional': true, - 'type': 'string' - } - }, - 'type': 'object' - }, - 'birthDate': { - 'optional': true, - 'type': 'string' - }, - 'description': { - 'optional': true, - 'type': 'string' - }, - 'familyName': { - 'optional': true, - 'type': 'string' - }, - 'givenName': { - 'optional': true, - 'type': 'string' - }, - 'image': { - 'items': { - 'properties': { - '@type': { - 'type': 'string' - }, - 'contentUrl': { - 'optional': true, - 'type': 'string' - }, - 'name': { - 'optional': true, - 'type': 'string' - } - }, - 'type': 'object' - }, - 'optional': true, - 'type': 'array' - }, - 'knows': { - 'items': { - 'properties': { - '@id': { - 'optional': true, - 'type': 'string' - }, - '@type': { - 'type': 'string' - } - }, - 'type': 'object' - }, - 'optional': true, - 'type': 'array' - }, - 'name': { - 'optional': true, - 'type': 'string' - }, - 'taxID': { - 'optional': true, - 'type': 'string' - }, - 'website': { - 'items': { - 'properties': { - '@type': { - 'type': 'string' - }, - 'url': { - 'optional': true, - 'type': 'string' - } - }, - 'type': 'object' - }, - 'optional': true, - 'type': 'array' - }, - 'worksFor': { - 'items': { - 'properties': { - '@id': { - 'optional': true, - 'type': 'string' - }, - '@type': { - 'type': 'string' - } - }, - 'type': 'object' - }, - 'optional': true, - 'type': 'array' - } - } - }, - 'transfer_address': r'^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'tx_hash': r'^([0-9a-fA-F]+)$', - 'type': '.+', - 'zonefile': '.+', - 'zonefile_hash': r'^([0-9a-fA-F]+)$' - } - } - } - - -# Group Core Wallet Management - -This entire section is **DEPRECATED** in favor of the wallet software in -[blockstack.js](https://github.com/blockstack/blockstack.js). Names registered -with this API will need to be transferred to the Blockstack Browser. - -The Blockstack Core node manages its own wallet -- this has three keys -for payment, name ownership, and signing data (e.g., user profiles). This -wallet can be managed through these endpoints. - -## Get balance via mock-insight API [GET /insight-api/addr/{address}/balance] -Returns the integer satoshi balance of the given address, with mininum -of 1 confirmation. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to query -balances. -+ Authorization: `wallet_read` -+ Legacy Endpoint -+ Response 200 (application/json) - + Body - - 2104 - - + Schema - - { 'type' : 'integer' } - -## Get unconfirmed balance via mock-insight API [GET /insight-api/addr/{address}/unconfirmedBalance] -Returns the integer *unconfirmed* satoshi balance of the given address -(only the 0-confirmation balance). To get the min_conf=0 balance of an -address, you want *unconfirmedBalance* + *balance*. The unconfirmed -balance may be negative (if there is an unconfirmed spend). This -specification is strange, I know, but it replicates the interface of -insight-api. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to query -balances. -+ Authorization: `wallet_read` -+ Legacy Endpoint -+ Response 200 (application/json) - + Body - - -1000 - - + Schema - - { 'type' : 'integer' } - -## Get wallet payment address [GET /v1/wallet/payment_address] - -Returns core node's payment address. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to manage keys -and query UTXOs. -+ Authorization: `wallet_read` -+ Legacy Endpoint -+ Response 200 (application/json) - + Body - - { - "address": "mv1uqYWZpnap4VBSKTHfKW6noTZcNtxtCW" - } - - + Schema - - { - 'type': 'object', - 'properties': { - 'type': 'string', - 'pattern': r'^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - } - } - -## Set a specific wallet key [PUT /v1/wallet/keys/{keyname}] -This call instructs the blockstack core node to use a particular key -instead of the core node's configured wallet key. The setting of this -key is *temporary* by default, meaning that it is not written to -`~/.blockstack/wallet.json`, and on a subsequent restart, the key will -return to the original key. However, the core registrar *tracks* the -owner key used for each `PREORDER`, and stores that private key -encrypted (with `scrypt` and the core wallet password) in the -queue. When the registrar detects that the key being used for a -particular name has changed, it will recover by submitting further -transactions with the stored key. - -However, for blockstack core >= 0.14.5, the `persist_change` keyword -will instruct the core node to write the changed key to -`~/.blockstack/wallet.json`. In this mode, the node will backup the -previous wallet to `~/.blockstack/wallet.json.prior.` - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to manage keys. -+ Requires root authorization -+ Legacy Endpoint -+ Parameters - + keyname: owner (string) - which key to set (one of 'owner', 'data', 'payment') - -+ Request (application/json) - + Body - - "cPo24qGYz76xSbUCug6e8LzmzLGJPZoowQC7fCVPLN2tzCUJgfcW" - -+ Request (application/json) - + Schema - - { - "type" : "object", - "properties" : { - "private_key" : { - "anyOf": [ - { - "anyOf": [ - { - "pattern": "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$", - "type": "string" - }, - { - "pattern": "^([0-9a-fA-F]+)$", - "type": "string" - } - ] - }, - { - "properties": { - "address": { - "pattern": "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$", - "type": "string" - }, - "private_keys": { - "items": { - "anyOf": [ - { - "pattern": "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$", - "type": "string" - }, - { - "pattern": "^([0-9a-fA-F]+)$", - "type": "string" - } - ] - }, - "type": "array" - }, - "redeem_script": { - "pattern": "^([0-9a-fA-F]+)$", - "type": "string" - } - }, - "required": [ - "address", - "redeem_script", - "private_keys" - ], - "type": "object" - } - ] - }, - "persist_change" : {"type" : "boolean"} - }, - "required" : [ "private_key" ] - } - -+ Request (application/json) - + Schema - - { - "anyOf": [ - { - "anyOf": [ - { - "pattern": "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$", - "type": "string" - }, - { - "pattern": "^([0-9a-fA-F]+)$", - "type": "string" - } - ] - }, - { - "properties": { - "address": { - "pattern": "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$", - "type": "string" - }, - "private_keys": { - "items": { - "anyOf": [ - { - "pattern": "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$", - "type": "string" - }, - { - "pattern": "^([0-9a-fA-F]+)$", - "type": "string" - } - ] - }, - "type": "array" - }, - "redeem_script": { - "pattern": "^([0-9a-fA-F]+)$", - "type": "string" - } - }, - "required": [ - "address", - "redeem_script", - "private_keys" - ], - "type": "object" - } - ] - } - -+ Response 200 (application/json) - + Body - - {"status": true} - - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'status': { - 'type': 'boolean' - }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - }, - }, - }, - ], - } - -## Get payment wallet balance [GET /v1/wallet/balance/{minconfs}] - -Fetches wallet balance, including UTXOs from transactions with at -least a specified number of confirmations. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to manage keys -and query UTXOs. -+ Authorization: `wallet_read` -+ Legacy Endpoint -+ Parameters - + minconfs: 0 (number, optional) - the minimum confs of transactions to include in balance -+ Response 200 (application/json) - + Body - - { - "balance": { - "bitcoin": 49.931727, - "satoshis": 4993172700 - } - } - - + Schema - - { - 'type': 'object', - 'properties': { - 'balance': { - 'type': 'object', - 'properties': { - 'bitcoin': { - 'type': 'number', - 'minimum': 0, - }, - 'satoshis': { - 'type': 'integer', - 'minimum': 0, - }, - }, - }, - }, - } - -## Withdraw payment wallet funds [POST /v1/wallet/balance] -Withdraw an amount (given in satoshis) from the core payment -wallet, to a particular address. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to manage keys, -generate transactions, and send transactions. -+ Authorization: `wallet_write` -+ Legacy Endpoint -+ Request (application/json) - + Body - - {'address' : 'mibZW6EBpXSTWQNQ9E4fi9hhGKYSMkjyg9', - 'amount' : 100, - 'min_confs' : 6, - 'tx_only' : false} - - + Schema - - { - 'type': 'object', - 'properties': { - 'address': { - 'type': 'string', - 'pattern': r"^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$", - }, - 'amount': { - 'type': 'integer', - 'minimum': 0, - }, - 'message': { - 'type': 'string', - 'pattern': '^.{1,80}$', - } - 'min_confs': { - 'type': 'integer', - 'minimum': 0, - }, - 'tx_only': { - 'type': 'boolean' - }, - 'payment_key': { - 'anyOf': [ - { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - { - 'properties': { - 'address': { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - 'private_keys': { - 'items': { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - 'type': 'array' - }, - 'redeem_script': { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - }, - 'required': [ - 'owner' - ], - 'type': 'object' - } - ] - } - }, - 'required': [ - 'address' - ], - } - -+ Response 200 (application/json) - + Body - - { - "status": true, - "transaction_hash": "c4ee8d1993794487e6b5aca802a1793530bdff35c763ca051fbaa4b998780822", - "success": true - } - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'success': { - 'type': 'boolean' - }, - 'transaction_hash': { - 'type': 'string', - 'pattern': r'^([0-9a-fA-F]+)$', - }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - }, - }, - }, - ], - } - - -## Get wallet owner address [GET /v1/wallet/owner_address] -Returns core node's owner address. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to manage keys. -+ Authorization: `wallet_read` -+ Legacy Endpoint -+ Response 200 (application/json) - + Body - - { - "address": "myaPViveUWiiZQQTb51KXCDde4iLC3Rf3K" - } - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'address': { - 'type': 'string', - 'pattern': r"^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$", - }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - }, - }, - }, - ], - } - - -## Get wallet data public key [GET /v1/wallet/data_pubkey] -Returns the public key the core node uses for signing user data - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to manage keys. -+ Authorization: `wallet_read` -+ Legacy Endpoint -+ Response 200 (application/json) - + Body - - { - "public_key": "03ea5d8c2a3ba84eb17625162320bb53440557c71f7977a57d61405e86be7bdcda" - } - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'public_key': { - 'type': 'string', - 'pattern': r'^([0-9a-fA-F]+)$', - }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - }, - }, - }, - ], - } - -## Change wallet password [PUT /v1/wallet/password] -This will change the password for core's wallet. Currently not working endpoint. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to encrypt keys. -+ Authorization: `wallet_write` -+ Legacy Endpoint -+ Request (application/json) - + Body - - {'password' : '"0123456789abcdef"', - 'new_password' : "abcdef0123456789"'} - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'password': { - 'type': 'string', - }, - 'new_password': { - 'type': 'string', - }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - }, - }, - }, - ], - } - -## Set all wallet keys [PUT /v1/wallet/keys] - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to manage keys -in a client wallet. -+ Legacy Endpoint -+ Requires root authorization - -## Get all wallet keys [GET /v1/wallet/keys] - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to interact with -a client wallet. -+ Legacy Endpoint -+ Requires root authorization - -# Group Managing Names - -All POST, PUT, and DELETE routes in this section are **DEPRECATED** in favor of using either the [Blockstack -Browser](https://github.com/blockstack/blockstack-browser) or -[blockstack.js](https://github.com/blockstack/blockstack.js) to register and -manage names. - -All GET routes are still valid. - -## Register a name [POST /v1/names] -Registers a name, optionally to a given owner key and optionally using -a given payment key to pay for the name and transaction fees. - -This method takes a JSON blob with the following fields: - -* `name`: (required) the fully-qualified name to register. -* `zonefile`: (optional) the zone file to associate with this name. If one is - not given, a default one will be generated. -* `owner_address`: (optional) the recipient of this name. See below. -* `min_confs`: (optional) this is the minimum number of confirmations for UTXOs - that will be used for payments for this name registration. Lower values speed -up the name registration time, at the risk of blockchain reorgs or frontrunners -invalidating your name's registration. -* `tx_fee`: (optional) use this transaction fee (in satoshis) instead of - estimating one. -* `cost_satoshis`: (optional) how much to pay for this name. This value will be - sent to the name's namespace's designated burn address. If not given, the -precise value will be looked up automatically. -* `unsafe`: (optional) ignore internal safety checks when generating and sending - transactions. See below. -* `owner_key`: (optional) if given, this is the *private key* of the owner -that will receive the name. Useful for when you want to use your *personal* -node to register names to a different key, without having to bother with the -extra `NAME_TRANSFER` transaction required by passing `owner_address`. -DO NOT USE IN PUBLIC SETTINGS. -* `payment_key`: (optional) if given, this is the *private key* used to pay for - the name registration fee and transaction fees. Useful for when you want to -use your *personal* node to register names with a different payment key. DO NOT -USE IN PUBLIC SETTINGS. - -If no `owner_address` is supplied in the POSTed JSON -object, the node will register a name for the `owner_key` given in the -JSON blob. If no `owner_key` is given, then the node's current owner address -in its wallet will be used. - -If an `owner_address` is supplied, a `TRANSFER` transaction will be -broadcasted to send the name to appropriate owner. If you intend to register -many names to different addresses, it is recommended that you use one of the -wallet endpoints to set the node's owner keys to save yourself the extra -`TRANSFER` transactions (or pass `owner_key`). However, you should *ONLY* do -this if you trust the node (i.e. only do this for personal nodes). - -The `min_confs` keyword controls the minimum number of confirmations for -UTXOs used as payments for name registration. - -The `unsafe` keyword instructs the node's registrar to ignore certain -safety checks while registering the name (in particular, the registrar -will not verify that the user own's the name before issuing a -`REGISTER` and `UPDATE`). This allows the registrar to submit -operations before they have been confirmed on remote resolvers or -indexers, in this mode, the registrar will wait for 4 confirmations on -a `PREORDER`, 1 confirmation on a `REGISTER` and 1 confirmation on an -`UPDATE`. `node.blockstack.org` will correctly detect the registration -after the `UPDATE` has 6 confirmations. - -+ DEPRECATED. Registering names is now performed by [Blockstack - Browser](https://github.com/blockstack/blocktack-browser) and -[blockstack.js](https://github.com/blockstack/blockstack.js). -+ Authorization: `register` -+ Legacy Endpoint -+ Request (application/json) - + Body - - { - 'name' : 'bar.test' - } - -+ Request (application/json) - + Schema - - { - 'type': 'object', - 'properties': { - "name": { - 'type': 'string', - 'pattern': OP_NAME_PATTERN - }, - "zonefile": { - 'type': 'string', - 'maxLength': RPC_MAX_ZONEFILE_LEN, - }, - "owner_address": { - 'type': 'string', - 'pattern': OP_BASE58CHECK_PATTERN, - }, - 'min_confs': { - 'type': 'integer', - 'minimum': 0, - }, - 'tx_fee': { - 'type': 'integer', - 'minimum': 0, - 'maximum': TX_MAX_FEE, - }, - 'cost_satoshis': { - 'type': 'integer', - 'minimum': 0, - }, - 'unsafe': { - 'type': 'boolean' - }, - 'owner_key': { - 'anyOf': [ - { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - { - 'properties': { - 'address': { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - 'private_keys': { - 'items': { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - 'type': 'array' - }, - 'redeem_script': { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - }, - 'required': [ - 'owner' - ], - 'type': 'object' - } - ] - }, - 'payment_key': { - 'anyOf': [ - { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - { - 'properties': { - 'address': { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - 'private_keys': { - 'items': { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - 'type': 'array' - }, - 'redeem_script': { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - }, - 'required': [ - 'owner' - ], - 'type': 'object' - } - ] - } - }, - 'required': [ - 'name' - ], - 'additionalProperties': False, - } - -+ Response 200 (application/json) - + Body - - { - "message": "Name queued for registration. The process takes several hours. You can check the status with `blockstack info`.", - "success": true, - "transaction_hash": "6cdb9722f72875b30e1ab3de463e3960aced951f674be942b302581a9a9469a5" - } - - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'success': { - 'type': 'boolean' - }, - 'transaction_hash': { - 'type': 'string', - 'pattern': r'^([0-9a-fA-F]+)$', - }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - }, - }, - }, - ], - } - - -## Revoke name [DELETE /v1/names/{name}] -Revokes the name from Blockstack. This renders -the name unusable until it expires. Use this method if your private keys -are compromised. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) for generating -transactions. -+ Authorization: `revoke` -+ Legacy Endpoint -+ Parameters - + name: bar.test (string) - fully-qualified name -+ Response 200 (application/json) - + Body - - { - "message": "Name queued for revocation. The process takes ~1 hour. You can check the status with `blockstack info`.", - "success": true, - "transaction_hash": "b2745b706d7a14ce652265de103d7eaefb44a75eb658d7bb1db8868da08768b2" - } - - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'success': { - 'type': 'boolean' - }, - 'transaction_hash': { - 'type': 'string', - 'pattern': r'^([0-9a-fA-F]+)$', - }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - }, - }, - }, - ], - } - - -## Transfer name [PUT /v1/names/{name}/owner] -Transfers a name to a different owner. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) for generating -transactions. -+ Authorization: `transfer` -+ Legacy Endpoint -+ Parameters - + name: bar.test (string) - name to transfer -+ Request (application/json) - + Body - - { "owner" : "mjZicz7GSJBZuGeCMEgpzr8U9w6d41DfXm" } - -+ Request (application/json) - + Schema - - - { - 'type': 'object', - 'properties': { - 'owner': { - 'type': 'string', - 'pattern': OP_BASE58CHECK_PATTERN, - }, - 'tx_fee': { - 'type': 'integer', - 'minimum': 0, - 'maximum': 500000 - }, - 'owner_key': { - 'anyOf': [ - { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - { - 'properties': { - 'address': { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - 'private_keys': { - 'items': { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - 'type': 'array' - }, - 'redeem_script': { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - }, - 'required': [ - 'owner' - ], - 'type': 'object' - } - ] - }, - 'payment_key': { - 'anyOf': [ - { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - { - 'properties': { - 'address': { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - 'private_keys': { - 'items': { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - 'type': 'array' - }, - 'redeem_script': { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - }, - 'required': [ - 'owner' - ], - 'type': 'object' - } - ] - } - }, - 'additionalProperties': False, - } - - -+ Response 202 (application/json) - + Body - - { - "message": "Name queued for transfer. The process takes ~1 hour. You can check the status with `blockstack info`.", - "success": true, - "transaction_hash": "c0d677f9ee681abbed8ca6d231bc4ece517c8c6695ce883e5e196b5395402779" - } - - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'success': { - 'type': 'boolean' - }, - 'transaction_hash': { - 'type': 'string', - 'pattern': r'^([0-9a-fA-F]+)$', - }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - }, - }, - }, - ], - } - -## Publish zone file [POST /v1/names/zonefile] -Publish the zonefile which has _already_ been announced. -Submit either as a string with the 'zonefile' property, or -as a base64 encoded blob with the 'zonefile_b64' property. -We recommend base64-encoding your zone files in order to guarantee that they -will be JSON-encodable. - -+ DEPRECATED. -+ Request (application/json) - + Schema - - { - 'type': 'object', - 'properties': { - "zonefile": { - 'type': 'string', - }, - "zonefile_b64": { - 'type': 'string', - } - }, - 'additionalProperties': False, - } - -+ Response 200 (application/json) - + Body - - {'success': true, 'servers' : ['...']} - - -## Set zone file [PUT /v1/names/{name}/zonefile] -Sets the user's zonefile hash, and, if supplied, propagates the -zonefile. If you supply the zonefile, the hash will be calculated from -that. Ultimately, your requests should only supply one of `zonefile`, -`zonefile_b64`, or `zonefile_hash`. - -The value for `zonefile_b64` is a base64-encoded string. -New clients _should_ use the `zonefile_b64` field when specifying a zone file. -The `zonefile` field is preserved for legacy compatibility. - -This API call issues a `NAME_UPDATE` transaction for a name that is owned by -this node's wallet. That is, you can only call this API method if your node -owns the name you're updating. - -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) for generating -transactions. -+ Authorization: `update` -+ Legacy Endpoint -+ Parameters - + name: bar.test (string) - fully-qualified name -+ Request (application/json) - + Schema - - { - 'type': 'object', - 'properties': { - "zonefile": { - 'type': 'string', - }, - 'zonefile_b64': { - 'type': 'string', - 'pattern': r'^((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4}))$' - }, - 'zonefile_hash': { - 'type': 'string', - 'pattern': '^([0-9a-fA-F]{20})$' - }, - 'tx_fee': { - 'type': 'integer', - 'minimum': 0, - 'maximum': 500000 - }, - 'owner_key': { - 'anyOf': [ - { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - { - 'properties': { - 'address': { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - 'private_keys': { - 'items': { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - 'type': 'array' - }, - 'redeem_script': { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - }, - 'required': [ - 'owner' - ], - 'type': 'object' - } - ] - }, - 'payment_key': { - 'anyOf': [ - { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - { - 'properties': { - 'address': { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - 'private_keys': { - 'items': { - 'anyOf': [ - { - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - 'type': 'string' - }, - { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - ] - }, - 'type': 'array' - }, - 'redeem_script': { - 'pattern': '^([0-9a-fA-F]+)$', - 'type': 'string' - } - }, - 'required': [ - 'owner' - ], - 'type': 'object' - } - ] - } - }, - 'additionalProperties': False, - } - -+ Response 202 (application/json) - + Body - - {'success': true, 'transaction_hash' : '...'} - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'success': { - 'type': 'boolean' - }, - 'transaction_hash': { - 'type': 'string', - 'pattern': r'^([0-9a-fA-F]+)$', - }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - }, - }, - }, - ], - } - -## Fetch zone file [GET /v1/names/{name}/zonefile] -Fetch a user's raw zone file. This only works for RFC-compliant zone files. -This method returns an error for names that have non-standard zone files. - -+ Parameters - + name: bar.test (string) - fully-qualified name -+ Response 200 (application/json) - + Body - - { - "zonefile": "$ORIGIN bar.test\n$TTL 3600\n_https._tcp URI 10 1 \"https://blockstack.s3.amazonaws.com/bar.test\"\n" - } - - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'zonefile': { - 'type': 'string', - 'pattern': '.+', - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - 'pattern': '.+', - }, - }, - ] - } - -# Group Name Querying -This family of API endpoints deals with querying name information. - -## Get all names [GET /v1/names?page={page}] -Fetch a list of all names known to the node. -+ Public Endpoint -+ Parameters - + page: 23 (number) - names are returned in pages of size 100, - so specify the page number. -+ Response 200 (application/json) - + Body - - [ "aldenquimby.id", "aldeoryn.id", - "alderete.id", "aldert.id", - "aldi.id", "aldighieri.id", ... ] - - + Schema - - { - 'type': 'array', - 'items': { - 'type': 'string', - 'pattern': r'^([a-z0-9\\-_.+]{3,37})$', - } - } - -## Get all subdomains [GET /v1/subdomains?page={page}] -Fetch a list of all names known to the node. -+ Public Endpoint -+ Parameters - + page: 3 (number) - names are returned in pages of size 100, - so specify the page number. -+ Response 200 (application/json) - + Body - - [ - "collegeinfogeek.verified.podcast", - "collider.verified.podcast", - "combatandclassics.verified.podcast", - "combatjack.verified.podcast", - "comedybangbang.verified.podcast", - "comedybutton.verified.podcast", - "commonsense.verified.podcast", - "concilio002.personal.id", ... ] - - + Schema - - { - 'type': 'array', - 'items': { - 'type': 'string', - 'pattern': r'^([a-z0-9\\-_.+]{3,37})\.([a-z0-9\\-_.+]{3,37})$', - } - } - -## Get name info [GET /v1/names/{name}] -+ Public Endpoint -+ Subdomain Aware -+ Parameters - + name: muneeb.id (string) - fully-qualified name -+ Response 200 (application/json) - + Body - - { - "address": "1QJQxDas5JhdiXhEbNS14iNjr8auFT96GP", - "blockchain": "bitcoin", - "expire_block": 489247, - "last_txid": "1edfa419f7b83f33e00830bc9409210da6c6d1db60f99eda10c835aa339cad6b", - "status": "registered", - "zonefile": "$ORIGIN muneeb.id\n$TTL 3600\n_http._tcp IN URI 10 1 \"https://blockstack.s3.amazonaws.com/muneeb.id\"\n", - "zonefile_hash": "b100a68235244b012854a95f9114695679002af9" - } - - + Schema - - { - 'type': 'object', - 'properties': { - 'address': { - 'type': 'string', - 'pattern': r"^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$", - }, - 'blockchain': { - 'type': 'string', - 'pattern': '^bitcoin$', - }, - 'expire_block': { - 'type': 'integer', - 'minimum': 0, - }, - 'last_txid': { - 'type': 'string', - 'pattern': '^[0-9a-fA-F]+$', - }, - 'status': { - 'type': 'string', - 'pattern': '^(registered|revoked)$', - }, - 'zonefile': { - 'anyOf': [ - { - 'type': 'string', - }, - { - 'type': 'object', - 'properties': { - 'error': { - 'type': 'string', - }, - }, - }, - ], - }, - 'zonefile_hash': { - 'type': 'string', - 'pattern': '^[0-9a-fA-F]{20}$`, - }, - }, - { 'required': - [ - 'address', 'blockchain', 'last_txid', - 'status', 'zonefile', 'zonefile_hash' - ] - } - } - -## Name history [GET /v1/names/{name}/history?page={page}] -Get a history of all blockchain records of a registered name. -+ Public Endpoint -+ Subdomain aware -+ Parameters - + name: muneeb.id (string) - name to query - + page: 0 (integer) - the page (in 20-entry pages) of the history to fetch -+ Response 200 (application/json) - + Body - - { - "373821": [ - { - "address": "1QJQxDas5JhdiXhEbNS14iNjr8auFT96GP", - "block_number": 373821, - "consensus_hash": null, - "first_registered": 373821, - "importer": "76a9143e2b5fdd12db7580fb4d3434b31d4fe9124bd9f088ac", - "importer_address": "16firc3qZU97D1pWkyL6ZYwPX5UVnWc82V", - "last_creation_op": ";", - "last_renewed": 373821, - "name": "muneeb.id", - "name_hash128": "deb7fe99776122b77925cbf0a24ab6f8", - "namespace_block_number": 373601, - "namespace_id": "id", - "op": ";", - "op_fee": 100000.0, - "opcode": "NAME_IMPORT", - "preorder_block_number": 373821, - } - ] - } - - + Schema - - { - 'type': 'object', - 'patternProperties': { - '^[0-9]+': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'address': { - 'type': 'string', - 'pattern': r"^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$", - }, - 'base': { - 'type': 'integer', - 'minimum': 0, - 'maximum': 255, - }, - 'buckets': { - 'anyOf': [ - { - 'type': 'array', - 'items': { - 'type': 'integer', - 'minItems': 16, - 'maxItems': 16, - }, - }, - { - 'type': 'null', - }, - ], - }, - 'block_number': { - 'type': 'integer', - 'minimum': 0, - }, - 'coeff': { - 'anyOf': [ - { - 'type': 'integer', - 'minimum': 0, - 'maximum': 255, - }, - { - 'type': 'null' - }, - ], - }, - 'consensus_hash': { - 'anyOf': [ - { - 'type': 'string', - 'pattern': '^[0-9a-fA-F]{32}', - }, - { - 'type': 'null' - }, - ], - }, - 'fee': { - 'type': 'integer', - 'minimum': 0, - }, - 'first_registered': { - 'type': 'integer', - 'minimum': 0, - }, - 'history_snapshot': { - 'type': 'boolean', - }, - 'importer': { - 'anyOf': [ - { - 'type': 'string', - 'pattern': r'^76[aA]914[0-9a-fA-F]{40}88[aA][cC]$', - }, - { - 'type': 'null', - }, - ], - }, - 'importer_address': { - 'anyOf': [ - { - 'type': 'string', - 'pattern': r"^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$", - }, - { - 'type': 'null', - }, - ], - }, - 'last_renewed': { - 'type': 'integer', - 'minimum': 0, - }, - 'op': { - 'type': 'string', - 'pattern': '^([>?+~:!&*:;#]{1}|>>|>~|::)$', - }, - 'op_fee': { - 'type': 'number', - }, - 'opcode': { - 'type': 'string', - 'pattern': '^NAME_TRANSFER|NAME_PREORDER|NAME_UPDATE|NAME_REVOKE|NAME_REGISTRATION|NAMESPACE_READY|NAMESPACE_REVEAL|NAMESPACE_PREORDER|NAME_RENEWAL|NAME_IMPORT|ANNOUNCE$' - }, - 'revoked': { - 'type': 'boolean', - }, - 'sender': { - 'type': 'string', - 'pattern': '^([0-9a-fA-F]+)$', - }, - 'sender_pubkey': { - 'anyOf': [ - { - 'type': 'string', - 'pattern': '^([0-9a-fA-F]+)$', - }, - { - 'type': 'null' - }, - ], - }, - 'recipient': { - 'anyOf': [ - { - 'type': 'string', - 'pattern': '^([0-9a-fA-F]+)$', - }, - { - 'type': 'null' - }, - ], - }, - 'recipient_address': { - 'anyOf': [ - { - 'type': 'string', - 'pattern': '^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$', - }, - { - 'type': 'null' - }, - ], - }, - 'recipient_pubkey': { - 'anyOf': [ - { - 'type': 'string', - 'pattern': '^([0-9a-fA-F]+)$', - }, - { - 'type': 'null' - }, - ], - }, - 'txid': { - 'type': 'string', - 'pattern': '^([0-9a-fA-F]+)$', - }, - 'value_hash': { - 'anyOf': [ - { - 'type': 'string', - 'pattern': '^([0-9a-fA-F]{40})$', - }, - { - 'type': 'null', - }, - ], - }, - 'vtxindex': { - 'type': 'integer', - 'minimum': 0, - }, - }, - 'required': [ - 'op', - 'opcode', - 'txid', - 'vtxindex' - ], - } - } - } - } - -## Get historical zone file [GET /v1/names/{name}/zonefile/{zoneFileHash}] -Fetches the historical zonefile specified by the username and zone hash. -+ Public Endpoint -+ Subdomain aware -+ Parameters - + name: muneeb.id (string) username to fetch - + zoneFileHash: b100a68235244b012854a95f9114695679002af9 -+ Response 200 (application/json) - + Body - - { - "zonefile": - "$ORIGIN muneeb.id\n$TTL 3600\n_http._tcp IN URI 10 1 \"https://blockstack.s3.amazonaws.com/muneeb.id\"\n" - } - - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'zonefile': { 'type': 'string' }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { 'type': 'string' }, - }, - }, - ], - } - -## Get names owned by address [GET /v1/addresses/{blockchain}/{address}] -Retrieves a list of names owned by the address provided. -+ Subdomain Aware -+ Public Endpoint -+ Parameters - + blockchain: bitcoin (string) - the layer-1 blockchain for the address - + address: 1QJQxDas5JhdiXhEbNS14iNjr8auFT96GP (string) - the address to lookup - -+ Response 200 (application/json) - + Body - - { - "names": ["muneeb.id"] - } - - + Schema - - { - 'type': 'object', - 'properties': { - 'names': { - 'type': 'array', - 'items': { - 'type': 'string', - 'pattern': '^([a-z0-9\-_.+]{3,37})$', - }, - }, - }, - } - -# Group Price Checks -## Get namespace price [GET /v1/prices/namespaces/{tld}] - -This endpoint is used to get the price of a namespace. Anyone can create a -namespace by following [this -tutorial](https://github.com/blockstack/blockstack-core/blob/master/docs/namespace_creation.md). - -+ Public Endpoint -+ Parameters - + tld: id (string) - namespace to query price for -+ Response 200 (application/json) - + Body - - { - "satoshis": 4000000000 - } - - + Schema - - { - 'type': 'object', - 'properties': { - 'satoshis': { - 'type': 'integer', - 'minimum': 0, - }, - }, - } - -## Get name price [GET /v1/prices/names/{name}] - -This endpoint is used to get the price of a name. If you are using -a public endpoint, you should *only* rely on the `name_price` field in the -returned JSON blob. Other fields are **DEPRECATED**, since they are relevant -only for estimating the cost of registering a name (which should be done via -[blockstack.js](https://github.com/blockstack/blockstack.js) or the [Blockstack -Browser](https://github.com/blockstack/blockstack-browser)). - -+ Public Endpoint -+ Parameters - + name: muneeb.id (string) - name to query price information for -+ Response 200 (application/json) - + Body - - { - "name_price": { - "satoshis": 100000, - "btc": 0.001 - }, - "total_tx_fees": 519209, - "register_tx_fee": { - "satoshis": 159110, - "btc": 0.0015911 - }, - "preorder_tx_fee": { - "satoshis": 163703, - "btc": 0.00163703 - }, - "warnings": [ - "Insufficient funds; fees are rough estimates." - ], - "total_estimated_cost": { - "satoshis": 619209, - "btc": 0.00619209 - }, - "update_tx_fee": { - "satoshis": 196396, - "btc": 0.00196396 - } - } - - + Schema - - { - 'type': 'object', - 'properties': { - 'name_price': { - 'type': 'object', - 'properties': { - 'btc': { 'type': 'number', 'minimum': 0 }, - 'satoshis': { 'type': 'integer', 'minimum': 0 } - } - }, - 'preorder_tx_fee': { - 'type': 'object', - 'properties': { - 'btc': { 'type': 'number', 'minimum': 0 }, - 'satoshis': { 'type': 'integer', 'minimum': 0 } - } - }, - 'register_tx_fee': { - 'type': 'object', - 'properties': { - 'btc': { 'type': 'number', 'minimum': 0 }, - 'satoshis': { 'type': 'integer', 'minimum': 0 } - } - }, - 'update_tx_fee': { - 'type': 'object', - 'properties': { - 'btc': { 'type': 'number', 'minimum': 0 }, - 'satoshis': { 'type': 'integer', 'minimum': 0 } - } - }, - 'total_estimated_cost': { - 'type': 'object', - 'properties': { - 'btc': { 'type': 'number', 'minimum': 0 }, - 'satoshis': { 'type': 'integer', 'minimum': 0 } - } - }, - 'total_tx_fees': { - 'type': 'integer', - 'minimum': 0, - } - 'name_price': { - 'type': 'object', - 'properties': { - 'btc': { 'type': 'number', 'minimum': 0 }, - 'satoshis': { 'type': 'integer', 'minimum': 0 } - } - }, - 'warnings': { - 'type': 'array', - 'items': { - 'type': 'string', - }, - }, - }, - } - -# Group Blockchain Operations -## Get consensus hash [GET /v1/blockchains/{blockchainName}/consensus] -Get the current Blockstack consensus hash on a blockchain. -+ Public Endpoint -+ Parameters - + blockchainName : bitcoin (string) - the given blockchain -+ Response 200 (application/json) - + Body - - { - "consensus_hash": "2fcbdf66c350894fe03b42c6a2e8a6ac" - } - - + Schema - - { - 'type': 'object', - 'properties': { - 'consensus_hash': { - 'type': 'string', - 'pattern': '^[0-9a-fA-F]{32}$`, - }, - }, - } - -## Get number of names on blockchain [GET /v1/blockchains/{blockchainName}/name_count{?all}] -Get the number of names on a blockchain. -+ Public Endpoint -+ Parameters - + blockchainName: bitcoin (string) - the given blockchain - + all: true (enum[string], optional) - include expired names -+ Response 200 (application/json) - + Body - - { - "names_count": 73950 - } - - + Schema - - { - 'type': 'object', - 'properties': { - 'names_count': { - 'type': 'integer', - 'minimum': 0, - }, - }, - } - -+ Response 401 (application/json) - + Body - - { "error": "Unsupported blockchain" } - - + Schema - - { - 'type': 'object', - 'properties': { - 'error': { 'type': 'string' }, - }, - }, - - -## Get number of subdomains on blockchain [GET /v1/blockchains/{blockchainName}/subdomains_count] -Get the number of subdomains on a blockchain. -+ Public Endpoint -+ Parameters - + blockchainName: bitcoin (string) - the given blockchain -+ Response 200 (application/json) - + Body - - { - "names_count": 1646 - } - - + Schema - - { - 'type': 'object', - 'properties': { - 'names_count': { - 'type': 'integer', - 'minimum': 0, - }, - }, - } - -+ Response 401 (application/json) - + Body - - { "error": "Unsupported blockchain" } - - + Schema - - { - 'type': 'object', - 'properties': { - 'error': { 'type': 'string' }, - }, - }, - - -## Get operations in block [GET /v1/blockchains/{blockchainName}/operations/{blockHeight}] -Get the Blockstack operations in a given block -+ Parameters - + blockchainName : bitcoin (string) - the given blockchain - + blockHeight : 462592 (integer) - the block height -+ Response 200 (application/json) - + Body - - [ - { - "address": "1GS1eHthSK2gqnU9MW9Nis1pUyHP3bJnFK", - "block_number": 462592, - "burn_address": "1111111111111111111114oLvT2", - "consensus_hash": "d206b2f615de00803402cade4d0d51d4", - "op": "?", - "op_fee": 6250, - "opcode": "NAME_PREORDER", - "preorder_hash": "ba22cdf24b05b9a7972e13ada69f96a7850b471e", - "sender": "76a914a944d29012f83c00105778e0bc717c46ea2accfc88ac", - "sender_pubkey": "0343b263f7adc6ae59e8d8310f4a6a87799f6b10cec608f3236cd6a802ffc71728", - "txid": "b3f4f7a43d60666d1a9b42131f9117ad7deac34a478b6ca152344da3d734691f", - "vtxindex": 173 - }, - { - "address": "1gijbF8NkbgwzcoZR1nXMa76NbdcD7GQW", - "block_number": 462592, - "burn_address": "1111111111111111111114oLvT2", - "consensus_hash": "d206b2f615de00803402cade4d0d51d4", - "op": "?", - "op_fee": 6250, - "opcode": "NAME_PREORDER", - "preorder_hash": "386e2de88a908ad056361e586faa95852be454ca", - "sender": "76a91407830f81167f6a2aa253c0f176b7ff2e1c04c61a88ac", - "sender_pubkey": "03b7795d33b362338179e5b2a579431b285f6c303d07ddd83c897277be4e5eb916", - "txid": "4dd315ad1d1b318614a19e15e767efb7ef327bd5cd4ebaf8f80ede58fd1da107", - "vtxindex": 174 - }, - { - "address": "17QEd6rrhNZp4xoyWu6BpA8NQ4axyNKaZy", - "block_number": 462592, - "burn_address": "1111111111111111111114oLvT2", - "consensus_hash": "d206b2f615de00803402cade4d0d51d4", - "op": "?", - "op_fee": 6250, - "opcode": "NAME_PREORDER", - "preorder_hash": "a7a388a2bbe0e7741c6cfdc54d7b5a67811cd582", - "sender": "76a9144635b1794a22bfbe6c5c5eba17b693f4aaf0e34888ac", - "sender_pubkey": "020d6e50b2660af27933c42bc1395fe93df90ffac5e2a989f6a134919fb8cf8075", - "txid": "51d6bd117da5889e710c62967d03233a84fc27f7fad10ca4359111928818f017", - "vtxindex": 332 - }, - { - "address": "15YMyvqz6v9ATSbmqJnudwrdm7RiVfkU3s", - "block_number": 462453, - "consensus_hash": "f6491e1d2b9817fa58512fc9bf8cd3df", - "first_registered": 462575, - "importer": null, - "importer_address": null, - "keep_data": true, - "last_renewed": 462575, - "name": "ablankstein.id", - "name_hash128": "943b8e0613d975c05a05ccd5472e2a72", - "namespace_block_number": 373601, - "namespace_id": "id", - "op": ">>", - "op_fee": 25000, - "opcode": "NAME_TRANSFER", - "preorder_block_number": 462453, - "preorder_hash": "822d5cb6f2e3f0f901d6af8c1111ee466b6c07bd", - "revoked": false, - "sender": "76a91431cee995f242f0f66518080a291714cd7e8d2f5e88ac", - "sender_pubkey": null, - "txid": "121540e81223c45d139fbe03a9713ddd292372f2f88fe2b10b6a7c5e6738e87f", - "value_hash": "96ec93cbc57d17b16a347c11ddfa7ea88d2cf93b", - "vtxindex": 633 - }, - { - "address": "1Dwq9oA5BNz7DAR1LtDncEa647ZxgmkoVV", - "block_number": 462325, - "consensus_hash": "1288cef43f52bf97e2f458a4afe40b61", - "first_registered": 462359, - "importer": null, - "importer_address": null, - "keep_data": true, - "last_renewed": 462359, - "name": "fpenrose.id", - "name_hash128": "7af28a9834934a0af81a19ee14a45f8e", - "namespace_block_number": 373601, - "namespace_id": "id", - "op": ">>", - "op_fee": 25000, - "opcode": "NAME_TRANSFER", - "preorder_block_number": 462325, - "preorder_hash": "59c25d7cddf433b5122cabcbf2ebcc1bc1519e4d", - "revoked": false, - "sender": "76a9148e002a93b9b1936b5d320967194eaff3deaa979088ac", - "sender_pubkey": null, - "txid": "6461bb4bbf517e9c80ffcac4c349836972656572e113aba736b356119655064e", - "value_hash": "ac73155702ca7aea1161d0f0c7877ac81d48d8fc", - "vtxindex": 637 - }, - { - "address": "1Q44Md5KFr6gxQ6TdUSFaCRm3MaUyXMF6t", - "block_number": 462316, - "consensus_hash": "1288cef43f52bf97e2f458a4afe40b61", - "first_registered": 462353, - "importer": null, - "importer_address": null, - "keep_data": true, - "last_renewed": 462353, - "name": "rahulpradhan.id", - "name_hash128": "c55ff9e14c72b2950b14ff10067d7e27", - "namespace_block_number": 373601, - "namespace_id": "id", - "op": ">>", - "op_fee": 25000, - "opcode": "NAME_TRANSFER", - "preorder_block_number": 462316, - "preorder_hash": "fcb3389ca4d2ab8003ce8b6b3baa0a5ae1600cce", - "revoked": false, - "sender": "76a914fcdef125f40f984fafad4b58e30e3b1761a953f388ac", - "sender_pubkey": null, - "txid": "be58e02642c457fec2835a354fbc2de45e8c838aa5b7fd18ed831f67d08269e6", - "value_hash": "e213e58ca1446875b79d866720130cc90cbca681", - "vtxindex": 638 - }, - { - "address": "1D8pL725X9HWvoTVgzqDNbTPayHGG7tkY6", - "block_number": 462345, - "consensus_hash": "919df884f14f34fd15a791af2fddb569", - "first_registered": 462380, - "importer": null, - "importer_address": null, - "keep_data": true, - "last_renewed": 462380, - "name": "sajithskurup.id", - "name_hash128": "3fda1c60620c42e1ede385bb246bd5f0", - "namespace_block_number": 373601, - "namespace_id": "id", - "op": ">>", - "op_fee": 25000, - "opcode": "NAME_TRANSFER", - "preorder_block_number": 462345, - "preorder_hash": "540daefe1f3b520253f7ab954dbc8bf131471133", - "revoked": false, - "sender": "76a914851bee0185dd799755234fb20710a26ec40354d288ac", - "sender_pubkey": null, - "txid": "e7d35196ca3eec697274d848136f5267b1c935055a917020f93e8ecaf821ba99", - "value_hash": "92534954e934019718478bb52150765dfad79171", - "vtxindex": 644 - }, - { - "address": "1EbjXtYv9QCVBp8iWiDH6xQ1B74oFW696X", - "block_number": 462345, - "consensus_hash": "e0c31e03125f2feefd4090e5c635ee45", - "first_registered": 462380, - "importer": null, - "importer_address": null, - "keep_data": true, - "last_renewed": 462380, - "name": "hubject.id", - "name_hash128": "03e8bf92dd3cbde65cac012350efb79d", - "namespace_block_number": 373601, - "namespace_id": "id", - "op": ">>", - "op_fee": 25000, - "opcode": "NAME_TRANSFER", - "preorder_block_number": 462345, - "preorder_hash": "ded4d097614cf5321388bbe56b24d3d592b2ef76", - "revoked": false, - "sender": "76a914952b4844005dd98a1f7fc99813db2a649109b45988ac", - "sender_pubkey": null, - "txid": "7b7a2a2963f7454b93003031cfce64ac609f902b4c2cababfbbfad2c01bbeb9b", - "value_hash": "be968a1f17ac828179e5b2fbc70d238056af7482", - "vtxindex": 645 - }, - { - "address": "14YsDo5qgAP7kmnq33tw9JdHVBywpg9pge", - "block_number": 462326, - "consensus_hash": "e0c31e03125f2feefd4090e5c635ee45", - "first_registered": 462354, - "importer": null, - "importer_address": null, - "keep_data": true, - "last_renewed": 462354, - "name": "ramimassoud.id", - "name_hash128": "61a48b6f8aeb027883ecd1f8d808c8ac", - "namespace_block_number": 373601, - "namespace_id": "id", - "op": ">>", - "op_fee": 25000, - "opcode": "NAME_TRANSFER", - "preorder_block_number": 462326, - "preorder_hash": "23aa275e42d7d6d7e538584a799252939687c457", - "revoked": false, - "sender": "76a91426ef31b7aac60eff23cbbab51d453b84700e330388ac", - "sender_pubkey": null, - "txid": "85babcf66caf41cb7beb2e637cbed4e728ab8030337fb5df8461d0e14dd2be75", - "value_hash": "e27c9c3dcce8a8445d84fb8b4d81fbd30fac9749", - "vtxindex": 646 - }, - { - "address": "1H934mT7AVVZmHwjddUZ9EiostLwm655oF", - "block_number": 462345, - "consensus_hash": "919df884f14f34fd15a791af2fddb569", - "first_registered": 462391, - "importer": null, - "importer_address": null, - "keep_data": true, - "last_renewed": 462391, - "name": "was2bme.id", - "name_hash128": "f2b5688682fd47b8f3fbf709bb35ef33", - "namespace_block_number": 373601, - "namespace_id": "id", - "op": ">>", - "op_fee": 6250, - "opcode": "NAME_TRANSFER", - "preorder_block_number": 462345, - "preorder_hash": "3dfdcee2b0e64697c4bb0b0dd791518bcb078dc7", - "revoked": false, - "sender": "76a914b107105f8ae57e7bb5bad58caba666faa679c70f88ac", - "sender_pubkey": null, - "txid": "16171e4e20778354a94c5353b0c6ed0b29a3e73c1b59b9bfbcbe6d26c570fd0c", - "value_hash": "ac73155702ca7aea1161d0f0c7877ac81d48d8fc", - "vtxindex": 649 - }, - { - "address": "1B4zxvVMPm1PBGarc8PrYQjQY2ezwniyG6", - "block_number": 462345, - "consensus_hash": "e0c31e03125f2feefd4090e5c635ee45", - "first_registered": 462391, - "importer": null, - "importer_address": null, - "keep_data": true, - "last_renewed": 462391, - "name": "tadas_serbenta.id", - "name_hash128": "6d800932daf830925ab47dee5ceb8661", - "namespace_block_number": 373601, - "namespace_id": "id", - "op": ">>", - "op_fee": 6250, - "opcode": "NAME_TRANSFER", - "preorder_block_number": 462345, - "preorder_hash": "07a85eac4dbf20000a66a14a4a89a01134b70fab", - "revoked": false, - "sender": "76a9146e72e44bbe4c1706ea5830096a4bb4449dcc948f88ac", - "sender_pubkey": null, - "txid": "e3f0b019550417a7acfe27addfbd34ec7ec5fc1dd9616ed8c6bc86a0ad148290", - "value_hash": "fbac107ba5d9bbfc30ecdeae3e10ca3db72b3431", - "vtxindex": 855 - }, - { - "address": "16BF35VputeLEmbsk7gDnUcwKXcjwPDUvf", - "block_number": 462345, - "consensus_hash": "919df884f14f34fd15a791af2fddb569", - "first_registered": 462359, - "importer": null, - "importer_address": null, - "keep_data": true, - "last_renewed": 462359, - "name": "alexucf.id", - "name_hash128": "d9bc88b0fdc536e7ac5467609faed518", - "namespace_block_number": 373601, - "namespace_id": "id", - "op": ">>", - "op_fee": 25000, - "opcode": "NAME_TRANSFER", - "preorder_block_number": 462345, - "preorder_hash": "30f841114af6ada90ba720d563672113c4f74439", - "revoked": false, - "sender": "76a91438c8814ae2a9035e85bbf2b7976919c2e3387ac588ac", - "sender_pubkey": null, - "txid": "f8e9eebd48b9182b82b22e5ce10f805d3db38786bb2aaf56f9badf83aa3cc0ee", - "value_hash": "8ae0f51263f540be175230d6b46f5d9609de799d", - "vtxindex": 856 - }, - { - "address": "1EmXTRHC6f9bnLJkVZRavv7HLG1owLgNir", - "block_number": 462326, - "consensus_hash": "31a304b682e3291811441a12f19d14e5", - "first_registered": 462391, - "importer": null, - "importer_address": null, - "keep_data": true, - "last_renewed": 462391, - "name": "seamur.id", - "name_hash128": "09f3b9d2da3d0aa1999824f7884f0d18", - "namespace_block_number": 373601, - "namespace_id": "id", - "op": ">>", - "op_fee": 100000, - "opcode": "NAME_TRANSFER", - "preorder_block_number": 462326, - "preorder_hash": "678991fd4d3833babe27f732206a40d1f15dd3ca", - "revoked": false, - "sender": "76a91497055c47fa0ab396fb321e9d37f6bce1796e3d5688ac", - "sender_pubkey": null, - "txid": "e32124770c359eaf57709e5a666894f2954aa687820c41c6911f214e9006b58e", - "value_hash": "4bcdd931185537902ef1af9975198c6404d4c73e", - "vtxindex": 857 - }, - { - "address": "13pGtMcHsNdq3EeLMa1yVVKppP1WjSKgFG", - "block_number": 462345, - "consensus_hash": "e0c31e03125f2feefd4090e5c635ee45", - "first_registered": 462354, - "importer": null, - "importer_address": null, - "keep_data": true, - "last_renewed": 462354, - "name": "innergame.id", - "name_hash128": "a3e4e010d82369ee19b64fccc2b97f69", - "namespace_block_number": 373601, - "namespace_id": "id", - "op": ">>", - "op_fee": 25000, - "opcode": "NAME_TRANSFER", - "preorder_block_number": 462345, - "preorder_hash": "f54850caf10c3041cb2a4d9186bbb234dd7d9f85", - "revoked": false, - "sender": "76a9141ee10ff0ae9969e2dc39d94a959e3160b26b6adf88ac", - "sender_pubkey": null, - "txid": "28de7193e28e1b0c950a32af393284578669c15dc98bad68f382f8b920d94509", - "value_hash": "bab40c2b10f676288edea119edade67ff5e853ba", - "vtxindex": 869 - } - ] - -## Get pending transactions [GET /v1/blockchains/{blockchainName}/pending] -Get the current transactions that the node has issued and are still pending. -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to query the -blockchain. -+ Public Endpoint -+ Parameters - + blockchainName : bitcoin (string) - the given blockchain -+ Response 200 (application/json) - + Body - - { - "queues": {} - } - - + Schema - - { - 'type': 'object', - 'properties': { - 'preorder': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'name': { 'type': 'string', 'pattern': '^([a-z0-9\-_.+]{3,37})$' }, - 'tx_hash': { 'type': 'string', 'pattern': '^[0-9a-fA-F]+$' }, - 'confirmations': { 'type': 'integer', 'minimum': 0 }, - }, - }, - }, - 'register': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'name': { 'type': 'string', 'pattern': '^([a-z0-9\-_.+]{3,37})$' }, - 'tx_hash': { 'type': 'string', 'pattern': '^[0-9a-fA-F]+$' }, - 'confirmations': { 'type': 'integer', 'minimum': 0 }, - }, - }, - }, - 'update': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'name': { 'type': 'string', 'pattern': '^([a-z0-9\-_.+]{3,37})$' }, - 'tx_hash': { 'type': 'string', 'pattern': '^[0-9a-fA-F]+$' }, - 'confirmations': { 'type': 'integer', 'minimum': 0 }, - }, - }, - }, - 'transfer': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'name': { 'type': 'string', 'pattern': '^([a-z0-9\-_.+]{3,37})$' }, - 'tx_hash': { 'type': 'string', 'pattern': '^[0-9a-fA-F]+$' }, - 'confirmations': { 'type': 'integer', 'minimum': 0 }, - }, - }, - }, - 'renew': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'name': { 'type': 'string', 'pattern': '^([a-z0-9\-_.+]{3,37})$' }, - 'tx_hash': { 'type': 'string', 'pattern': '^[0-9a-fA-F]+$' }, - 'confirmations': { 'type': 'integer', 'minimum': 0 }, - }, - }, - }, - 'revoke': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'name': { 'type': 'string', 'pattern': '^([a-z0-9\-_.+]{3,37})$' }, - 'tx_hash': { 'type': 'string', 'pattern': '^[0-9a-fA-F]+$' }, - 'confirmations': { 'type': 'integer', 'minimum': 0 }, - }, - }, - }, - 'name_import': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'name': { 'type': 'string', 'pattern': '^([a-z0-9\-_.+]{3,37})$' }, - 'tx_hash': { 'type': 'string', 'pattern': '^[0-9a-fA-F]+$' }, - 'confirmations': { 'type': 'integer', 'minimum': 0 }, - }, - }, - }, - } - } - -## Get unspent outputs [GET /v1/blockchains/{blockchainName}/{address}/unspent] -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to query the -blockchain. -+ Authorization: `blockchain` -+ Parameters - + blockchainName : bitcoin (string) - the given blockchain - + address : 1GuKR3nJi2VH3E1ZSPvuX8nAu3jNnr7xzq (string) - the address to get unspents for -+ Response 200 (application/json) - + Body - - [ - { - "confirmations": 18, - "out_script": "76a914ae6ee3760fccb8225541ca89f08c927930adf97b88ac", - "outpoint": { - "hash": "977d3a025790e2cbdb50f63761872f36e78fbb9c53d515cb4c53155a1964932d", - "index": 1 - }, - "transaction_hash": "977d3a025790e2cbdb50f63761872f36e78fbb9c53d515cb4c53155a1964932d", - "value": 76779 - } - ] - - + Schema - - { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'confirmations': { 'type': 'integer', 'minimum': 0 }, - 'out_script': { 'type': 'string', 'pattern': '^[0-9a-fA-F]+$' }, - 'outpoint': { - 'type': 'object', - 'properties': { - 'hash': { 'type': 'string', 'pattern': '^[0-9a-fA-F]+$' }, - 'index': { 'type': 'integer', 'minimum': 0 }, - }, - }, - 'transaction_hash': { 'type': 'string', 'pattern': '^[0-9a-fA-F]+$' }, - 'value': { 'type': 'integer', 'minimum': 0 }, - }, - }, - } - -## Broadcast transaction [POST /v1/blockchains/{blockchainName}/txs] -+ DEPRECATED. Blockstack clients should use - [blockstack.js](https://github.com/blockstack/blockstack.js) to broadcast -transactions. -+ Authorization: `blockchain` -+ Parameters - + blockchainName : bitcoin (string) - the blockchain to broadcast on -+ Request (application/json) - + Schema - - { - 'type': 'object', - 'properties': { - 'tx': { - 'type': 'string', - 'pattern': OP_HEX_PATTERN, - }, - }, - 'additionalProperties': False, - 'required': [ - 'tx' - ], - } - -+ Response 200 (application/json) - + Body - - { 'status' : True, 'tx_hash' : '...' } - - + Schema - - { - 'anyOf': [ - { - 'type': 'object', - 'properties': { - 'status': { 'type': 'boolean' }, - 'tx_hash': { 'type': 'string', 'pattern': '^[0-9a-fA-F]+$' }, - }, - }, - { - 'type': 'object', - 'properties': { - 'error': { 'type': 'string' }, - }, - }, - ] - } - -# Group Namespace Operations -## Get all namespaces [GET /v1/namespaces] -+ Public Endpoint -+ Response 200 (application/json) - + Body - - { - "namespaces": [ - ".id" - ] - } - -## Get namespace names [GET /v1/namespaces/{tld}/names?page={page}] -Fetch a list of names from the namespace. -+ Public Endpoint -+ Parameters - + tld: id (string) - the namespace to fetch names from - + page: 23 (number) - names are returned in pages of size 100, - so specify the page number. -+ Response 200 (application/json) - + Body - - [ "aldenquimby.id", "aldeoryn.id", - "alderete.id", "aldert.id", - "aldi.id", "aldighieri.id", ... ] - -# Group Resolver Endpoints -## Lookup User [GET /v1/users/{username}] -Lookup and resolver a user's profile. Defaults to the `id` namespace. -Note that [blockstack.js](https://github.com/blockstack/blockstack.js) does -*not* rely on this endpoint. - -+ Public Only Endpoint -+ Subdomain Aware -+ Legacy Endpoint -+ Parameters - + username: fred (string) - username to lookup -+ Response 200 (application/json) - - - - { - "fred.id": { - "owner_address": "1CER5u4QXuqffHjHKrU76iMCsqtJLM5VHu", - "profile": { - "@context": "http://schema.org", - "@type": "Person", - "account": [ - { - "@type": "Account", - "identifier": "fredwilson", - "placeholder": false, - "proofType": "http", - "proofUrl": "https://twitter.com/fredwilson/status/943066895422455809", - "service": "twitter" - } - ], - "description": "I am a VC", - "image": [ - { - "@type": "ImageObject", - "contentUrl": "https://gaia.blockstack.org/hub/1CER5u4QXuqffHjHKrU76iMCsqtJLM5VHu/0/avatar-0", - "name": "avatar" - } - ], - "name": "Fred Wilson" - }, - "public_key": "026c94d1897fa148fa6401247a339b55abd869a3d562fdae8a7fcb9a11f1f846f3", - "verifications": [ - { - "identifier": "fredwilson", - "proof_url": "https://twitter.com/fredwilson/status/943066895422455809", - "service": "twitter", - "valid": true - } - ], - "zone_file": { - "$origin": "fred.id", - "$ttl": 3600, - "uri": [ - { - "name": "_http._tcp", - "priority": 10, - "target": "https://gaia.blockstack.org/hub/1CER5u4QXuqffHjHKrU76iMCsqtJLM5VHu/0/profile.json", - "weight": 1 - } - ] - } - } - } - - -## Profile Search [GET /v1/search?query={query}] -Searches for a profile using a search string. -+ Public Only Endpoint -+ Parameters - + query: wenger (string) - the search query -+ Response 200 (application/json) - + Body - - { - "results": [ - { - "fullyQualifiedName": "albertwenger.id", - "username": "albertwenger", - "profile": { - "@type": "Person", - "account": [ - { - "@type": "Account", - "identifier": "albertwenger", - "proofType": "http", - "service": "twitter" - }, - { - "@type": "Account", - "identifier": "albertwenger", - "proofType": "http", - "service": "facebook" - }, - { - "@type": "Account", - "identifier": "albertwenger", - "proofType": "http", - "service": "github" - }, - { - "@type": "Account", - "identifier": "1QHDGGLEKK7FZWsBEL78acV9edGCTarqXt", - "role": "payment", - "service": "bitcoin" - } - ], - "address": { - "@type": "PostalAddress", - "addressLocality": "New York" - }, - "description": "VC at USV.com", - ... - } - diff --git a/_site/core/attic/README.md b/_site/core/attic/README.md deleted file mode 100644 index 33ae11f758..0000000000 --- a/_site/core/attic/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Legacy Documentation - -Documents here are out-of-date but preserved for posterity. Do not rely on -them. diff --git a/_site/core/attic/advanced_usage.md b/_site/core/attic/advanced_usage.md deleted file mode 100644 index 5d75b35eff..0000000000 --- a/_site/core/attic/advanced_usage.md +++ /dev/null @@ -1,124 +0,0 @@ -# Advanced Usage - -This section details some of the advanced features in the CLI. - -## A Word of Warning - -Advanced features are meant to be used by experienced Blockstack users and developers, They receive less UI/UX testing than basic features, and their interfaces are expected to change to accomodate bugfixes and security fixes. Moreover, improper use of some advanced methods can cost you money, corrupt your profile, or compromise your wallet. Once they receive sufficient testing, an advanced feature may become a basic-mode feature in a subsequent release. - -**Do not use advanced mode unless you know what you are doing!** - -## Activating Advanced Mode - -To activate advanced mode, use the command `blockstack set_advanced_mode on`. - -To deactivate it later (recommended), use the command `blockstack set_advanced_mode off`. - -## Changing or Using Exiting Keys - -If you already have a payment key you want to use, or an owner key you want to migrate over, you can generate a wallet directly with `import_wallet`. We recommend using this command interactively, so you know which keys correspond to which usages. - -## Accounts - -With the accounts methods, you can directly manage your social proofs, link existing services to your profile, and store small bits of information. - -The account management methods are: -* `get_account`: Look up an account in a name's profile. There can be more than one match. -* `list_accounts`: List all accounts in a name's profile. -* `put_account`: Add or update an account in a name's profile. -* `delete_account`: Remove an account from a name's profile. This may need to be done more than once, if there are duplicates of the account. - -## Advanced Blockstack ID Queries - -Beyond `lookup` and `whois`, there are a few other more advanced queries you can run on Blockstack IDs. These include: - -### Listing Blockstack IDs -* `get_all_names`: Get the list of every single Blockstack ID in existance. -* `get_names_owned_by_address`: Get the list of names owned by a particular ownership address. - -### Querying the Blockchain -* `get_name_blockchain_record`: Get the raw database record for a Blockstack ID. It will contain a *compressed* history of all name operations that have affected it. This is meant primarily for debugging purposes; to get an easier-to-parse listing of the information this command returns, use `get_name_blockchain_history`. -* `get_name_blockchain_history`: Get the set of all prior states a Blockstack ID has been in, keyed by the block heights at which the state-change was processed. -* `get_records_at`: Get the list of name operation records processed at a particular block height. -* `list_update_history`: Get the list of all zonefile hashes that a Blockstack ID has ever had. - -### Zonefiles -* `get_name_zonefile`: Get only a Blockstack ID's zonefile. -* `list_zonefile_history`: Get the list of all zonefiles a Blockstack ID has ever had. **NOTE:** There is no guarantee that the server will hold copies of old zonefiles. This command is meant mainly for determining which historic zonefiles a server has processed. -* `set_zonefile_hash`: This is the counterpart to `update`, but instead of setting the zonefile directly and uploading it to storage, you can use this command to directly set the data hash for a Blockstack ID. **NOTE:** You should ensure that the associated zonefile data has been replicated off-chain to a place where other users can get at it. - -### Lightweight Queries - -The lightweight lookup protocol for Blockstack is called *Simplified Name Verification* (SNV). This command returns a prior blockchain-level record given a more recent known-good consensus hash, serial number, or transaction ID of a transaction that contains a consensus hash. The CLI does not need to trust the Blockstack server to use these commands. - -* `lookup_snv`: Use the Merkle skip-list in the name database to look up a historic name operation on a Blockstack ID. - -## Consensus Queries - -You can query consensus hash information from the server with the following commands: - -* `consensus`: Get the consensus hash at a particular block height - -## Namespace Queries - -In addition to querying Blockstack IDs, the CLI has advanced commands for querying namespaces. These include: - -* `get_namespace_blockchain_record`: Get the raw database record for a Blockstack namespace. It will contain a *compressed* history of all namespace operations that have affected it. -* `get_names_in_namespace`: Get the list of every Blockstack ID in a particular namespace. -* `get_namespace_cost`: Get the cost required to preorder a namespace. Does *not* include the cost to reveal and ready it, nor does it include the transaction fees. - -## Namespace Creation - -**WARNING:** We do not recommend that you try to do this by yourself. Creating a namespace is **EXTREMELY EXPENSIVE**. If you are interested in creating your own namespace, please contact the Blockstack developers on the [Blockstack Slack](http://chat.blockstack.org). - -These methods allow you to create a namespace. There are three steps: preordering, revealing, and readying. Preordering a namespace is like preordering a name--you announce the hash of the namespace ID and the address that will control it. Revealing a namespace not only reveals the namespace ID, but also sets the pricing and lifetime rules for names in the namespace. After revealing the namespace, the namespace controller can pre-populate the namespace by importing Blockstack IDs. Once the namespace has been pre-populated, the controller sends a final transaction that readies the namespace for general use. - -* `namespace_preorder`: Preorder a namespace. -* `namespace_reveal`: Reveal a namespace, and set its pricing and lifetime parameters. **NOTE:** This must be done within 144 blocks of sending the namespace preorder transaction. -* `name_import`: Import a name into a revealed (but not readied) namespace. You can set its owner address and zonefile hash directly. -* `namespace_ready`: Open a namespace for general registrations. - -## Data Storage - -Blockstack allows users to store arbitrary data to any set of storage providers for which the CLI has a driver. The data will be signed by the user's data key, so when other users read the data later on, they can verify that it is authentic (i.e. the storage provider is not trusted). Moreover, Blockstack is designed such that users don't have to know or care about which storage providers were used--as far as users can see, storage providers are just shared hard drives. - -There are two types of data supported by Blockstack: *mutable* data, and *immutable* data. Mutable data is linked by the profile, and can be written as fast and as frequently as the storage provider allows. Mutable data is addressed by URL. - -**WARNING:** While mutable data guarantees end-to-end authenticity, there is a chance that a malicious storage provider can serve new readers stale versions of the data. That is, users who have read the latest data already will not get tricked into reading stale data, but users who have *not yet* read the latest data *can* be tricked (i.e. the CLI keeps a version number for mutable data to do so). This must be taken into account if you intend to use this API. - -Immutable data, however, is content-addressed, and its cryptographic hash is stored to the user's zonefile. Writing immutable data will entail updating the zonefile and sending an `update` transaction (handled internally), so it will be slow by comparison. This has the advantage that storage providers cannot perform the aforementioned stale data attack, but has the downside that writes cost money and take a long time to complete. - -That said, we recommend using the mutable data API with several different storage providers whenever possible. - -### Mutable Data - -The following commands affect mutable data: - -* `get_mutable`: Use the profile to look up and fetch a piece of mutable data. -* `put_mutable`: Add a link to mutable data to the profile, and replicate the signed data itself to all storage providers. Other users will need the data's name to read it with `get_mutable`. -* `delete_mutable`: Remove a link to mutable data from the profile, and ask all storage providers to delete the signed data. - -### Immutable Data - -The following commnds affect immutable data: - -* `get_immutable`: Look up and fetch a piece of immutable data. You can supply either the name of the data, or its hash (both are stored in the zonefile, so there is no gain or loss of security in this choice). -* `put_immutable`: Replicate a piece of data to all storage providers, add its name and hash to the zonefile, and issue an `update` to upload the new zonefile to Blockstack servers and write the hash to the blockchain. -* `delete_immutable`: Remove the link to the data from the zonefile, ask all storage providers to delete the data, and issue an `update` to upload the new zonefile to Blockstack servers and write the new hash to the blockchain. -* `list_immutable_data_history`: Given the name of a piece of immutable data, query the zonefile history to find the historic list of hashes it has had. **NOTE:** Like `list_zonefile_history` above, this only returns data hashes for the data if the Blockstack server has the historic zonefile. - -## Fault Recovery - -Sometimes, things beyond our control can happen. Transactions can get stuck, storage providers can go offline or corrupt data, and so on. These commands are meant to assist in recovering from these problems: - -* `set_profile`: Directly set a Blockstack ID's profile. All previous accounts, data links, etc. must be included in the new profile, since the old profile (if still present) will be overwritten by the one given here. -* `convert_legacy_profile`: Given a legacy profile taken from a resolver, directly convert it into a new profile. This can be used with `set_profile` to recover from a failed profile migration. -* `unqueue`: If a transaction gets lost or stuck, you can remove it from the CLI's transaction queue with this command. This will allow you to re-try it. -* `rpcctl`: This lets you directly start or stop the Blockstack CLI's background daemon, which lets you recover from any crashes it experiences (you can find a trace of its behavior in `~/.blockstack/api_endpoint.log`) - -## Programmatic Access - -Other programs may want to make RPC calls the Blockstack CLI daemon. They can do so using either the `blockstack_client` Python package, or they can do so via the CLI as follows: - -* `rpc`: Issue a JSON RPC call. Takes a raw JSON string that encodes a list of arguments. - diff --git a/_site/core/attic/figures/gaia-authentication.png b/_site/core/attic/figures/gaia-authentication.png deleted file mode 100644 index 03ca186ef2..0000000000 Binary files a/_site/core/attic/figures/gaia-authentication.png and /dev/null differ diff --git a/_site/core/attic/figures/gaia-connect.png b/_site/core/attic/figures/gaia-connect.png deleted file mode 100644 index 34ab79479f..0000000000 Binary files a/_site/core/attic/figures/gaia-connect.png and /dev/null differ diff --git a/_site/core/attic/figures/gaia-getfile.png b/_site/core/attic/figures/gaia-getfile.png deleted file mode 100644 index 29d87c7252..0000000000 Binary files a/_site/core/attic/figures/gaia-getfile.png and /dev/null differ diff --git a/_site/core/attic/figures/gaia-listdir.png b/_site/core/attic/figures/gaia-listdir.png deleted file mode 100644 index b48072b7d2..0000000000 Binary files a/_site/core/attic/figures/gaia-listdir.png and /dev/null differ diff --git a/_site/core/attic/figures/gaia-putfile.png b/_site/core/attic/figures/gaia-putfile.png deleted file mode 100644 index 6ad02a5b82..0000000000 Binary files a/_site/core/attic/figures/gaia-putfile.png and /dev/null differ diff --git a/_site/core/attic/gaia.md b/_site/core/attic/gaia.md deleted file mode 100644 index b63e0536d3..0000000000 --- a/_site/core/attic/gaia.md +++ /dev/null @@ -1,461 +0,0 @@ -# LEGACY DOCUMENTATION - -Please see the [latest Gaia documentation](https://github.com/blockstack/gaia) - -Gaia: The Blockstack Storage System -==================================== - -The Blockstack storage system, called "Gaia", is used to host each user's data -without requiring users to run their own servers. - -Gaia works by hosting data in one or more existing storage systems of the user's choice. -These storage systems include cloud storage systems like Dropbox and Google -Drive, they include personal servers like an SFTP server or a WebDAV server, and -they include decentralized storage systems like BitTorrent or IPFS. The point -is, the user gets to choose where their data lives, and Gaia enables -applications to access it via a uniform API. - -A high-level analogy is to compare Gaia to the VFS and block layer in a UNIX -operating system kernel, and to compare existing storage systems to block -devices. Gaia has "drivers" for each storage system that allow it to load, -store, and delete chunks of data via a uniform interface, and it gives -applications a familiar API for organizing their data. - -Applications interface with Gaia via the [Blockstack Core -API](https://github.com/blockstack/blockstack-core/tree/master/api). Javascript -applications connect to Gaia using [Blockstack Portal](https://github.com/blockstack/blockstack-portal), -which helps them bootstrap a secure connection to Blockstack Core. - -# Datastores - -Gaia organizes data into datastores. A **datastore** is a filesystem-like -collection of data that is backed by one or more existing storage systems. - -When a user logs into an application, the application will create or connect to -the datastore that holds the user's data. Once connected, it can proceed to -interact with its data via POSIX-like functions: `mkdir`, `listdir`, `rmdir`, -`getFile`, `putFile`, `deleteFile`, and `stat`. - -A datastore has exactly one writer: the user that creates it. However, all data -within a datastore is world-readable by default, so other users can see the -owner's writes even when the owner is offline. Users manage access controls -by encrypting files and directories to make them readable to other specific users. -All data in a datastore is signed by a datastore-specific key on write, in order -to guarantee that readers only consume authentic data. - -The application client handles all of the encryption and signing. The other -participants---Blockstack Portal, Blockstack Core, and the storage -systems---only ferry data back and forth between application clients. - -## Data Organization - -True to its filesystem inspiration, data in a datastore is organized into a -collection of inodes. Each inode has two parts: - -* a **header**, which contains: - - * the inode type (i.e. file or directory) - - * the inode ID (i.e. a UUID4) - - * the hash of the data it stores - - * the size of the data it stores - - * a signature from the user - - * the version number - - * the ID of the device from which it was sent (see Advanced Topics below) - -* a **payload**, which contains the raw bytes to be stored. - - * For files, this is just the raw bytes. - - * For directories, this is a serialized data structure that lists the names - and inode IDs of its children, as well as a copy of the header. - -The header has a fixed length, and is somewhat small--only a few hundred bytes. -The payload can be arbitrarily large. - -## Data Consistency - -The reason for organizing data this way is to make cross-storage system reads -efficient, even when there are stale copies of the data available. In this -organization, reading an inode's data is a matter of: - -1. Fetching all copies of the header - -2. Selecting the header with the highest version number - -3. Fetching the payload from the storage system that served the latest header. - -This way, we can guarantee that: - -* The inode payload is fetched *once* in the common case, even if there are multiple stale copies of the inode available. - -* All clients observe the *strongest* consistency model offerred by the - underlying storage providers. - -* All readers observe a *minimum* consistency of monotonically-increasing reads. - -* Writers observe sequential consistency. - -This allows Gaia to interface with decentralized storage systems that make -no guarantees regarding data consistency. - -*(Aside 1: The Core node keeps track of the highest last-seen inode version number, -so if all inodes are stale, then no data will be returned).* - -*(Aside 2: In step 3, an error path exists whereby all storage systems will be -queried for the payload if the storage system that served the fresh inode does -not have a fresh payload).* - -# Accessing the Datastore - -Blockstack applications get access to the datastore as part of the sign-in -process. Suppose the user wishes to sign into the application `foo.app`. Then, -the following protocol is executed: - -![Gaia authentication](/docs/figures/gaia-authentication.png) - -1. Using `blockstack.js`, the application authenticates to Blockstack Portal via -`makeAuthRequest()` and `redirectUserToSignIn()`. - -2. When Portal receives the request, it contacts the user's Core node to get the - list of names owned by the user. - -3. Portal redirects the user to a login screen, and presents the user with the - list of names to use. The user selects which name to sign in as. - -4. Now that Portal knows which name to use, and which application is signing in, - it loads the datastore private key and requests a Blockstack Core session -token. This token will be used by the application to access Gaia. - -5. Portal creates an authentication response with `makeAuthResponse()`, which it - relays back to the application. - -6. The application retrieves the datastore private key and the Core session - token from the authentication response object. - - -## Creating a Datastore - -Once the application has a Core session token and the datastore private key, it -can proceed to connect to it, or create it if it doesn't exist. To do so, the -application calls `datastoreConnectOrCreate()`. - -This method contacts the Core node directly. It first requests the public -datastore record, if it exists. The public datastore record -contains information like who owns the datastore, when it was created, and which -drivers should be used to load and store its data. - -![Gaia connect](/docs/figures/gaia-connect.png) - -Suppose the user signing into `foo.app` does not yet have a datastore, and wants -to store her data on storage providers `A`, `B`, and `C`. Then, the following -protocol executes: - -1. The `datastoreConnectOrCreate()` method will generate a signed datastore record -stating that `alice.id`'s public key owns the datastore, and that the drivers -for `A`, `B`, and `C` should be loaded to access its data. - -2. The `datastoreConnectOrCreate()` method will call `mkdir()` to create a -signed root directory. - -3. The `datastoreConnectOrCreate()` method will send these signed records to the Core node. -The Core node replicates the root directory header (blue path), the root -direcory payload (green path), and the datastore record (gold path). - -4. The Core node then replicates them with drivers `A`, `B`, and `C`. - -Now, storage systems `A`, `B`, and `C` each hold a copy of the datastore record -and its root directory. - -*(Aside: The datastore record's consistency is preserved the same way as the -inode consistency).* - -## Reading Data - -Once the application has a Core session token, a datastore private key, and a -datastore connection object, it can proceed to read it. The available methods -are: - -* `listDir()`: Get the contents of a directory - -* `getFile()`: Get the contents of a file - -* `stat()`: Get a file or directory's header - -Reading data is done by path, just as it is in UNIX. At a high-level, reading -data involes (1) resolving the path to the inode, and (2) reading the inode's -contents. - -Path resolution works as it does in UNIX: the root directory is fetched, then -the first directory in the path, then the second directory, then the third -directory, etc., until either the file or directory at the end of the path is -fetched, or the name does not exist. - -### Authenticating Data - -Data authentication happens in the Core node,. -This is meant to enable linking files and directories to legacy Web -applications. For example, a user might upload a photo to a datastore, and -create a public URL to it to share with friends who do not yet use Blockstack. - -By default, the Core node serves back the inode payload data -(`application/octet-stream` for files, and `application/json` for directories). -The application client may additionally request the signatures from the Core -node if it wants to authenticate the data itself. - -### Path Resolution - -Applications do not need to do path resolution themselves; they simply ask the -Blockstack Core node to do so on their behalf. Fetching the root directory -works as follows: - -1. Get the root inode ID from the datastore record. - -2. Fetch all root inode headers. - -3. Select the latest inode header, and then fetch its payload. - -4. Authenticate the data. - -For example, if a client wanted to read the root directory, it would call -`listDir()` with `"/"` as the path. - -![Gaia listdir](/docs/figures/gaia-listdir.png) - -The blue paths are the Core node fetching the root inode's headers. The green -paths are the Core node selecting the latest header and fetching the root -payload. The Core node would reply the list of inode names within the root -directory. - -Once the root directory is resolved, the client simply walks down the path to -the requested file or directory. This involves iteratively fetching a -directory, searching its children for the next directory in the path, and if it -is found, proceeding to fetch it. - -### Fetching Data - -Once the Core node has resolved the path to the base name, it looks up the inode -ID from the parent directory and fetches it from the backend storage providers -via the relevant drivers. - -For example, fetching the file `/bar` works as follows: - -![Gaia getFile](/docs/figures/gaia-getfile.png) - -1. Resolve the root directory (blue paths) - -2. Find `bar` in the root directory - -3. Get `bar`'s headers (green paths) - -4. Find the latest header for `bar`, and fetch its payload (gold paths) - -5. Return the contents of `bar`. - -## Writing Data - -There are three steps to writing data: - -* Resolving the path to the inode's parent directory - -* Creating and replicating the new inode - -* Linking the new inode to the parent directory, and uploading the new parent - directory. - -All of these are done with both `putFile()` and `mkdir()`. - -### Creating a New Inode - -When it calls either `putFile()` or `mkdir()`, the application client will -generate a new inode header and payload and sign them with the datastore private -key. Once it has done so successfully, it will insert the new inode's name and -ID into the parent directory, give the parent directory a new version number, -and sign and replicate it and its header. - -For example, suppose the client attempts to write the data `"hello world"` to `/bar`. -To do so: - -![Gaia putFile](/docs/figures/gaia-putfile.png) - -1. The client executes `listDir()` on the parent directory, `/` (blue paths). - -2. If an inode by the name of `bar` exists in `/`, then the method fails. - -3. The client makes a new inode header and payload for `bar` and signs them with - the datastore private key. It replicates them to the datastore's storage -drivers (green paths). - -4. The client adds a record for `bar` in `/`'s data obtained from (1), - increments the version for `/`, and signs and replicates `/`'s header and -payload (gold paths). - - -### Updating a File or Directory - -A client can call `putFile()` multiple times to set the file's contents. In -this case, the client creates, signs, and replicates a new inode header and new -inode payload for the file. It does not touch the parent directory at all. -In this case, `putFile()` will only succeed if the parent directory lists an -inode with the given name. - -A client cannot directly update the contents of a directory. - -## Deleting Data - -Deleting data can be done with either `rmdir()` (to remove an empty directory) -or `deleteFile()` (to remove a file). In either case, the protocol executed is - -1. The client executes `listDir()` on the parent directory - -2. If an inode by the given name does not exist, then the method fails. - -3. The client removes the inode's name and ID from the directory listing, signs - the new directory, and replicates it to the Blockstack Core node. - -4. The client tells the Blockstack Core node to delete the inode's header and - payload from all storage systems. - - -# Advanced Topics - -These features are still being implemented. - -## Data Integrity - -What happens if the client crashes while replicating new inode state? What -happens if the client crashes while deleting inode state? The data hosted in -the underlying data stores can become inconsistent with the directory structure. - -Given the choice between leaking data and rendering data unresolvable, Gaia -chooses to leak data. - -### Partial Inode-creation Failures - -When creating a file or directory, Gaia stores four records in this order: - -* the new inode payload - -* the new inode header - -* the updated parent directory payload - -* the updated parent directory header - -If the new payload replicates successfully but the new header does not, then the -new payload is leaked. - -If the new payload and new header replicate successfully, but neither parent -directory record succeeds, then the new inode header and payload are leaked. - -If the new payload, new header, and updated parent directory payload replicate -successfully, but the updated parent header fails, then not only are the new -inode header and payload leaked, but also *reading the parent directory will -fail due to a hash mismatch between its header and inode*. This can be detected -and resolved by observing that the copy of the header in the parent directory -payload has a later version than the parent directory header indicates. - -### Partial Inode-deletion Failures - -When deleting a file or directory, Gaia alters records in this order: - -* update parent directory payload - -* update parent directory header - -* delete inode header - -* delete inode payload - -Similar to partial failures from updating parent directories when creating -files, if the client replicates the new parent directory inode payload but fails -before it can update the header, then clients will detect on the next read that -the updated payload is valid because it has a signed inode header with a newer -version. - -If the client successfully updates the parent directory but fails to delete -either the inode header or payload, then they are leaked. However, since the -directory was updated, no correct client will access the deleted inode data. - -### Leak Recovery - -Gaia's storage drivers are designed to keep the inode data they store in a -"self-contained" way (i.e. within a single folder or bucket). In the future, -we will implement a `fsck`-like tool that will scan through a datastore and find -the set of inode headers and payloads that are no longer referenced and delete -them. - -## Multi-Device Support - -Contemporary users read and write data across multiple devices. In this -document, we have thus far described datastores with the assumption that there -is a single writer at all times. - -This assumption is still true in a multi-device setting, since a user is -unlikely to be writing data with the same application simultaneously from two -different devices. - -However, an open question is how multiple devices can access the same -application data for a user. Our design goal is to **give each device its own -keyring**, so if it gets lost, the user can revoke its access without having to -re-key her other devices. - -To do so, we'll expand the definition of a datastore to be a **per-user, -per-application, and per-device** collection of data. The view of a user's -application data will be constructed by merging each device-specific -datastore, and resolving conflicts by showing the "last-written" inode (where -"last-written" is determined by a loosely-synchronized clock). - -For example, if a user uploads a profile picture from their phone, and then -uploads a profile picture from their tablet, a subsequent read will query the -phone-originated picture and the tablet-originated picture, and return the -tablet-originated picture. - -The aforementioned protocols will need to be extended to search for inode -headers not only on each storage provider, but also search for inodes on the -same storage provider that may have been written by each of the user's devices. -Without careful optimization, this can lead to a lot of inode header queries, -which we address in the next topic. - -## A `.storage` Namespace - -Blockstack Core nodes can already serve as storage "gateways". That is, one -node can ask another node to store its data and serve it back to any reader. - -For example, Alice can make her Blockstack Core node public and program it to -store data to her Amazon S3 bucket and her Dropbox account. Bob can then post data to Alice's -node, causing her node to replicate data to both providers. Later, Charlie can -read Bob's data from Alice's node, causing Alice's node to fetch and serve back -the data from her cloud storage. Neither Bob nor Charlie have to set up accounts on -Amazon S3 and Dropbox this way. - -Since Alice is on the read/write path between Bob and Charlie and cloud storage, -she has the opportunity to make optimizations. First, she can program her -Core node to synchronously write data to -local disk and asynchronously back it up to S3 and Dropbox. This would speed up -Bob's writes, but at the cost of durability (i.e. Alice's node could crash -before replicating to the cloud). - -In addition, Alice can program her Core node to service all reads from disk. This -would speed up Charlie's reads, since he'll get the latest data without having -to hit back-end cloud storage providers. - -Since Alice is providing a service to Bob and Charlie, she will want -compensation. This can be achieved by having both of them send her money via -the underlying blockchain. - -To do so, she would register her node's IP address in a -`.storage` namespace in Blockstack, and post her rates per GB in her node's -profile and her payment address. Once Bob and Charlie sent her payment, her -node would begin accepting reads and writes from them up to the capacity -purchased. They would continue sending payments as long as Alice provides them -with service. - -Other experienced node operators would register their nodes in `.storage`, and -compete for users by offerring better durability, availability, performance, -extra storage features, and so on. diff --git a/_site/core/auth/howitworks.html b/_site/core/auth/howitworks.html deleted file mode 100644 index 121d9c31ca..0000000000 --- a/_site/core/auth/howitworks.html +++ /dev/null @@ -1,597 +0,0 @@ - - - - - - - - -How Atlas Works | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      How Atlas Works

      - - - - - - -
      - -

      Atlas was designed to overcome the structural weaknesses inherent to all -distributed hash tables. In particular, it uses an unstructured peer network to -maximize resilience against network link failure, and it uses the underlying -blockchain (through BNS) to rate-limit chunk announcements.

      - -

      This section contains the following sections:

      - - - -

      Peer Selection

      - -

      Atlas peers self-organize into an unstructured peer-to-peer network. -The Atlas peer network is a random K-regular -graph. Each node maintains -K neighbors chosen at random from the set of Atlas peers.

      - -

      Atlas nodes select peers by carrying out an unbiased random walk of the peer -graph. When “visiting” a node N, it will ask for N’s neighbors and then -“step” to one of them with a probability dependent on N’s out-degree and the -neighbor’s in-degree.

      - -

      The sampling algorithm is based on the Metropolis-Hastings (MH) random graph walk -algorithm, but with a couple key differences. In particular, the algorithm -attempts to calculate an unbiased peer graph sample that accounts for the fact -that most nodes will be short-lived or unreliable, while a few persistent nodes -will remain online for long periods of time. The sampling algorithm accounts -for this with the following tweaks:

      - -
        -
      • -

        If the neighbors of the visited node N are all unresponsive, the random -walk resets to a randomly-chosen known neighbor. There is no back-tracking on -the peer graph in this case.

        -
      • -
      • -

        The transition probability from N to a live neighbor is NOT min(1, -degree(neighbor)/degree(N)) like it is in the vanilla MH algorithm. Instead, -the transition probability discourages backtracking to the previous neighbor N_prev, -but in a way that still guarantees that the sampling will remain unbiased.

        -
      • -
      • -

        A peer does not report its entire neighbor set when queried, -but only reports a random subset of peers that have met a minimium health threshold.

        -
      • -
      • -

        A new neighbor is only selected if it belongs to the same BNS -fork-set (i.e. it reports -as having a recent valid consensus hash).

        -
      • -
      - -

      The algorithm was adapted from the work from Lee, Xu, and -Eun in the proceedings of -ACM SIGMETRICS 2012.

      - -

      Comparison to DHTs

      - -

      The reason Atlas uses an unstructured random peer network -instead of a distributed hash table -(DHT) is that DHTs are susceptbile to Sybil attacks. An adaptive adversary can -insert malicious nodes into the DHT in order to stop victims from -resolving chunks or finding honest neighbors.

      - -

      Chunk Censorship

      - -

      In a DHT, an attacker can censor a chunk by inserting nodes into the peers’ routing tables -such that the attacker takes control over all of the chunk’s hash buckets. -It can do so at any point in time after the chunk was first stored, -because only the peers who maintain the chunk’s hash bucket have to store it. -This is a fundamental problem with structured overlay networks -that perform request routing based on content hash—they give the attacker -insight as to the path(s) the queries take through the peer graph, and thus -reduce the number of paths the attacker must disrupt in order to censor the -chunk.

      - -

      Atlas uses an unstructured overlay network combined with a 100% chunk -replication strategy in order to maximize -the amount of work an adversary has to do to censor a chunk. -In Atlas, all peers replicate a chunk, and the paths the chunk take through the -network are independent of the content and randomized by the software -(so the paths cannot be predicted in advance). The attacker’s only -recourse is to quickly identify the nodes that can serve the chunk and partition them from -the rest of the network in order to carry out a censorship attack. -This requires them to have visibility into the vast majority of network links in -the Atlas network (which is extremely difficult to do, because in practice Atlas -peers maintain knowledge of up to 65536 neighbors and only report 10 random peers -when asked).

      - -

      Neighbor Censorship

      - -

      Another problem with DHTs is that their overlay -network structure is determined by preferential attachment. Not every peer that -contacts a given DHT node has an equal chance of becoming its neighbor. -The node will instead rank a set of peers as being more or less ideal -for being neighbors. In DHTs, the degree of preference a node exhibits to -another node is usually a function of the node’s self-given node identifier -(e.g. a node might want to select neighbors based on proximity in the key -space).

      - -

      The preferential attachment property means that an adaptive adversary can game the node’s -neighbor selection algorithm by inserting malicious nodes that do not -forward routing or lookup requests. The attacker does not even have to eclipse -the victim node—the victim node will simply prefer to talk to the attacker’s unhelpful nodes -instead of helpful honest nodes. In doing so, the attacker can prevent honest peers from discovering each -other and each other’s chunks.

      - -

      Atlas’s neighbor selection strategy does not exhibit preferential attachment -based on any self-reported node properties. A -node is selected as a neighbor only if it is reached through an unbiased random graph -walk, and if it responds to queries correctly. -In doing so, an attacker is forced to completely eclipse a set of nodes -in order to cut them off from the rest of the network.

      - -

      Chunk Propagation

      - -

      Atlas nodes maintain an inventory of chunks that are known to exist. Each -node independently calculates the chunk inventory from its BNS database. -Because the history of name operations in BNS is linearized, each node can -construct a linearized sub-history of name operations that can set chunk -hashes as their name state. This gives them a linearized sequence of chunks, -and every Atlas peer will independently arrive at the same sequence by reading -the same blockchain.

      - -

      Atlas peers keep track of which chunks are present and which are absent. They -each construct an inventory vector of chunks V such that V[i] is set to 1 -if the node has the chunk whose hash is in the ith position in the chunk -sequence (and set to 0 if it is absent).

      - -

      Atlas peers exchange their inventory vectors with their neighbors in order to -find out which chunks they each have. Atlas nodes download chunks from -neighbors in rarest-first order in order to prioritize data replication for the -chunks that are currently most at-risk for disappearing due to node failure.

      - -
         Name operation   |  chunk hashes  |   chunk data    |  Inventory
      -      history       |  as name state |                 |   vector
      -
      -+-------------------+
      -| NAME_PREORDER     |
      -+-------------------+----------------+
      -| NAME_REGISTRATION | chunk hash     |  "0123abcde..."       1
      -+-------------------+----------------+
      -| NAME_UPDATE       | chunk hash     |    (null)             0
      -+-------------------+----------------+
      -| NAME_TRANSFER     |
      -+-------------------+
      -| NAME_PREORDER     |
      -+-------------------+----------------+
      -| NAME_IMPORT       | chunk hash     |  "4567fabcd..."       1
      -+-------------------+----------------+
      -| NAME_TRANSFER     |
      -+-------------------|
      -      .  .  .
      -
      -
      -Figure 2:  Relationship between Atlas node chunk inventory and BNS name state.
      -Some name operations announce name state in the blockchain, which Atlas
      -interprets as a chunk hash.  The Atlas node builds up a vector of which chunks
      -it has and which ones it does not, and announces it to other Atlas peers so
      -they can fetch chunks they are missing.  In this example, the node's
      -inventory vector is [1, 0, 1], since the 0th and 2nd chunks are present
      -but the 1st chunk is missing.
      -
      -
      - -

      Querying Chunk Inventories

      - -

      Developers can query a node’s inventory vector as follows:

      - -
      >>> import blockstack
      ->>> result = blockstack.lib.client.get_zonefile_inventory("https://node.blockstack.org:6263", 0, 524288)
      ->>> print len(result['inv'])
      -11278
      ->>>
      -
      -
      - -

      The variable result['inv'] here is a big-endian bit vector, where the ith -bit is set to 1 if the ith chunk in the chunk sequence is present. The bit at -i=0 (the earliest chunk) refers to the leftmost bit.

      - -

      A sample program that inspects a set of Atlas nodes’ inventory vectors and determines -which ones are missing which chunks can be found -here.

      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - -
      - - - - - - - -
      - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/auth/howtouse.html b/_site/core/auth/howtouse.html deleted file mode 100644 index c8a85c133c..0000000000 --- a/_site/core/auth/howtouse.html +++ /dev/null @@ -1,531 +0,0 @@ - - - - - - - - -How to Use the Atlas Network | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      How to Use the Atlas Network

      - - - - - - -
      - -

      This section teaches you how to use the Atlas network, it contains the -following sections:

      - - - -

      The API

      - -

      While the Blockstack software stack expects that Atlas-hosted data is made up of -DNS zone files, Atlas itself does not enforce this (nor does it care about the -format of its chunks). It is designed as a general-purpose chunk store. -Nevertheless, the ubiquitous use of Atlas to store data as DNS zone files has -had an influence on its API design—fields and method names frequently allude -to zone files and zone file hashes. This is intentional.

      - -

      The public BNS API endpoint does not support -resolving Atlas chunks that do not encode Gaia routing information or subdomain -information. To directly interact with Atlas, developers will need to install -Blockstack Core and use its -Python client libraries for these examples.

      - -

      Looking up Chunks

      - -

      All Atlas chunks are addressed by the RIPEMD160 hash of the SHA256 hash of the -chunk data. A client can query up to 100 chunks in one RPC call.

      - -

      A client can look up a chunk with the get_zonefiles() method. If successful, -the returned payload will be a dict with a zonefiles key that maps the chunk -hashes to their respective data.

      - -
      >>> import blockstack
      ->>> data = blockstack.lib.client.get_zonefiles('https://node.blockstack.org:6263', ['1b89a685f4c4ea245ce9433d0b29166c22175ab4'])
      ->>> print data['zonefiles']['1b89a685f4c4ea245ce9433d0b29166c22175ab4']
      -$ORIGIN duckduckgo_tor.id
      -$TTL 3600
      -tor TXT "3g2upl4pq6kufc4m.onion"
      -
      ->>>
      -
      -
      - -

      (This particular chunk happens to be associated with the BNS name -duckduckgo_tor.id).

      - -

      Adding a New Chunk

      - -

      The only way to add a chunk to Atlas is to do so through an on-chain name in -BNS. Adding a new chunk is a two-step process:

      - -
        -
      • The name owner announces the chunk hash as a name’s state -via a NAME_REGISTRATION, NAME_UPDATE, NAME_RENEWAL, or NAME_IMPORT transaction.
      • -
      • Once the transaction is confirmed and processed by BNS, the name owner -broadcasts the matching zone file.
      • -
      - -

      Setting a name’s state to be the hash of a chunk is beyond the scope of this -document, since it needs to be done through a BNS client. -See the relevant documentation for -blockstack.js and the Blockstack -Browser for doing this.

      - -

      Once the name operation is confirmed, you can announce the data to the -Atlas network. You can do so with the Python client as follows:

      - -
      >>> import blockstack
      ->>> import base64
      ->>> data = "..."   # this is the chunk data you will announce
      ->>> data_b64 = base64.b64encode(data)
      ->>> result = blockstack.lib.client.put_zonefiles('https://node.blockstack.org:6263', [data_b64])
      ->>> assert result['saved'][0] == 1
      ->>>
      -
      -
      - -

      At most five chunks can be announced in one RPC call. -Note that the data must be base64-encoded before it can be announced.

      - -

      When the put_zonefiles() method succeeds, it returns a dict with a list -under the saved key. Here, result['saved'][i] will be 1 if the ith -chunk given to put_zonefiles() was saved by the node, and 0 if not. -The node will not save a chunk if it is too big, or if it has not yet processed -the name operation that contained the chunk’s hash.

      - -

      The put_zonefiles() method is idempotent.

      - -

      Propagating Chunks

      - -

      Atlas peers will each store a copy of the chunks you announce. In the -background, they will asynchronously announce to one another which chunks they -have available, and replicate them to one another in a rarest-first order (much -like how BitTorrent works). Eventually, every Atlas peer will receive the -chunk.

      - -

      However, developers can accelerate this process by eagerly propagating chunks. -To do so, they can ask an Atlas peer for its immediate neighbors in the Atlas -peer graph, and replicate the chunk to each of them as well.

      - -

      For example, this code will replicate the chunk to not only -https://node.blockstack.org:6263, but also to its immediate neighbors.

      - -
      >>> import blockstack
      ->>> import base64
      ->>> data = "..."   # this is the chunk you will replicate widely
      ->>> data_b64 = base64.b64encode(data)
      ->>>
      ->>> result = blockstack.lib.client.get_atlas_peers('https://node.blockstack.org:6263')
      ->>> neighbors = result['peers']
      ->>> print ", ".join(neighbors)
      -13.65.207.163:6264, 52.225.128.191:6264, node.blockstack.org:6264, 23.102.162.7:6264, 52.167.230.235:6264, 23.102.162.124:6264, 52.151.59.26:6264, 13.92.134.106:6264
      ->>>
      ->>> for neighbor in neighbors:
      -...    result = blockstack.lib.client.put_zonefiles(neighbor, [data_b64])
      -...    assert result['saved'][0] == 1
      -...
      ->>>
      -
      -
      - -

      This is not strictly necessary, but it does help accelerate chunk replication -and makes it less likely that a chunk will get lost due to individual node -failures.

      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - -
      - - - - - - - -
      - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/auth/overview.html b/_site/core/auth/overview.html deleted file mode 100644 index 3a2e5ba13e..0000000000 --- a/_site/core/auth/overview.html +++ /dev/null @@ -1,588 +0,0 @@ - - - - - - - - -Overview of the Atlas network | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      Overview of the Atlas network

      - - - - - - -
      - -

      https://zbabystack.netlify.com/core/auth/overview.html

      - -

      This document describes the Atlas network, a peer-to-peer content-addressed -storage system whose chunks’ hashes are announced on a public blockchain. Atlas -allows users and developers to permanently store chunks of data that are -replicated across every peer. As long as at least one Atlas peer is online, -all chunks are available to clients.

      - -

      This document is aimed at developers and technical users. The following -concepts are discussed:

      - - - -

      The reader of this document is expected to be familiar with the Blockstack -Naming Service (BNS), as well as Blockstack’s -storage system Gaia. We advise the reader -to familiarize themselves with both systems before approaching this document.

      - -

      Architecture

      - -

      Atlas is designed to integrate with BNS in order to allow users to -store name state off-chain, encoded as a DNS zone file. -The overwhelmingly-common use-cases in Blockstack are:

      - -
        -
      • Storing a name’s routing information for its owners’ Gaia -datastores.
      • -
      • Storing BNS subdomain transactions and associated state.
      • -
      - -

      Atlas is a middleware system in Blockstack. Most developers do not -interact with it directly. BNS clients like the -Blockstack Browser -automatically generate zone files for the names they register, and automatically -propagate them to the Atlas network. BNS API endpoints, including our -public endpoint and the -blockstack.js library, -will automatically fetch zone files from Atlas when they need to look -up data in Gaia (such as profiles and app data).

      - -
                     +--------------+       +---------------+       +----------------+
      -clients        |  Blockstack  |       | blockstack.js |       | BNS API module |
      -               |    Browser   |       |               |       |                |
      -               +--------------+       +---------------+       +----------------+
      -                 ^          ^           ^           ^           ^            ^
      -                 |          |           |           |           |            |
      -                 |          |           |           |           |            |
      -                 V          |           V           |           V            |
      -          +----------+      |    +----------+       |    +----------+        |
      -Gaia      | Gaia hub |      |    | Gaia hub |       |    | Gaia hub |        |
      -          +----------+      |    +----------+       |    +----------+        |
      -                            |                       |                        |
      -                            |                       |                        |
      -                            V                       V                        V
      -               +---------------------------------------------------------------+
      -Atlas          |                      Atlas Peer Network                       |
      -               +----------+------+----------+-----+----------+------+----------+
      -BNS            | BNS node |      | BNS node |     | BNS node |      | BNS node |
      -               +----------+      +----------+     +----------+      +----------+
      -                    ^                 ^                ^                 ^
      -                    | (indexing       |                |                 |
      -                    |  blockchain)    |                |                 |
      -               +---------------------------------------------------------------+
      -Blockchain     |                    Blockchain Peer Network                    |
      -               +---------------------------------------------------------------+
      -
      -
      -Figure 1:  Location of Atlas in the Blockstack architecture.  Each BNS node
      -implements an Atlas peer.  An Atlas peer treats a name state value in BNS as
      -the hash of a DNS zone file.  Atlas peers exchange zone files with one another
      -until they each have a full replica of all known zone files.  Clients can look
      -up zone files for names using the name's stat value as a zone file hash.  Clients
      -can broadcast zone files to the network  if they match a previously-announced
      -hash.  In practice, zone files store URLs to a name owner's Gaia hubs, thereby
      -allowing Blockstack apps to read and write data in Gaia.
      -
      -
      - -

      Nevertheless, Atlas is a general-purpose content-addressed storage -system that advanced developers can use to host data in an immutable -and durable manner. Beyond its default use-case in Blockstack, -Atlas is ideal for tasks like:

      - -
        -
      • Announcing PGP public keys under a human-readable name
      • -
      • Storing package hashes for a software release
      • -
      • Securely deploying shell scripts to remote VMs
      • -
      • Binding human-readable names to Tor .onion addresses -(example)
      • -
      - -

      Motivation

      - -

      Atlas was designed to augment BNS. BNS allows each name to store a small -amount of state—on the order of 20 bytes. The size is so small because the -state must be recorded to a public blockchain, where the cost per byte is -high and the blockchain protocol limits the size of transactions.

      - -

      To compensate for this, we developed an off-chain storage system allows BNS -names to bind and store a large amount of state to each name in a way that -preserves the security properties of having written that state to the -blockchain. Instead of storing 20 bytes of data on the blockchain, a BNS name -owner would store the cryptograhpic hash of its state, and then store the actual state -Atlas. This decouples the name’s state size from the blockchain.

      - -

      The reference implementation of Atlas currently allows up to 40kb of state to be -bound to a BNS name, instead of a measly 20 bytes. The 40kb of data is -replicated to each BNS node, where it is stored forever.

      - -

      Feature Comparison

      - -

      Atlas is not the only peer-to-peer content-addressible chunk store in existance. The following -feature table describes Atlas in relation to other popular chunk stores.

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      FeaturesAtlasBitTorrentDATIPFSSwarm
      Each peer stores all chunksXX   
      Replicas are permanent [1]XXX  
      Replicas are free XXX 
      Sybil-resistant chunk discoveryXX  X
      Sybil-resistant peer discoveryX    
      Fixed chunk sizeX XXX
      - -

      [1] Here, “permanent” means that once a peer has data, they will never evict it -as part of the protocol.

      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - -
      - - - - - - - -
      - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/basic_usage.md b/_site/core/basic_usage.md deleted file mode 100644 index b579a04679..0000000000 --- a/_site/core/basic_usage.md +++ /dev/null @@ -1,324 +0,0 @@ -# Basic Usage - -This section describes the basic features of Blockstack CLI. This document is meant to supplement the built-in documentation in the tool. - -If at any point you forget how to use a particular CLI command, you can just type `blockstack `, and you will be interactively prompted for each piece of data it needs. - -## Client Setup - -By default, the CLI tool will automatically configure itself with sensible defaults. If you don't like the defaults, you can change them by running `blockstack configure`. - -In particular, you can enable the advanced features with `blockstack set_advanced_mode on`. Please see the `advanced_usage.md` file for details. - -## Wallet Setup - -Blockstack uses its own wallet, stored to `~/.blockstack/wallet.json`. You do not need a wallet to do lookups. - -Blockstack will set up a wallet for you when you try to do something that requires payment (e.g. registering a name). When prompted, you will be asked for a wallet password. This password will be used to encrypt your private keys, and must be at least 16 characters. We recommend using a random string of words that you can easily memorize, since **there is no way to recover your wallet if you forget your password.** - -Once you generate a wallet, **make a back-up to a USB key**. You can also print it out, since it's just a string of plain text. - -## Doing Lookups - -There are two kinds of lookups: a `whois` and a `lookup`. The `whois` queries only information found in the blockchain, whereas a `lookup` queries off-chain data. - -A `lookup` fetches the off-chain profile and zonefile. For example: - -``` - $ blockstack lookup guylepage3.id - { - "profile": { - "@type": "Person", - "account": [ - { - "@type": "Account", - "identifier": "1Mp5vKwCbekeWetMHLKDD2fDLJzw4vKxiQ", - "role": "payment", - "service": "bitcoin" - }, - { - "@type": "Account", - "identifier": "guylepage3", - "proofType": "http", - "proofUrl": "https://twitter.com/guylepage3/status/731252874886385665", - "service": "twitter" - }, - { - "@type": "Account", - "identifier": "g3lepage", - "proofType": "http", - "proofUrl": "https://www.facebook.com/g3lepage/posts/10154223148908760", - "service": "facebook" - }, - { - "@type": "Account", - "identifier": "guylepage3", - "proofType": "http", - "proofUrl": "https://gist.github.com/guylepage3/06f522444fb71f1daf01a534396d1f9e", - "service": "github" - } - ], - "address": { - "@type": "PostalAddress", - "addressLocality": "New York, NY" - }, - "description": "@blockstackorg developer. 1st hire, Design Partner @blockstacklabs (YC/USV backed) entrepreneur, blockchain, creative, marketing, surf, triathlon, ironman", - "graph": { - "url": "https://s3.amazonaws.com/grph/guylepage3" - }, - "image": [ - { - "@type": "ImageObject", - "contentUrl": "https://s3.amazonaws.com/dx3/guylepage3", - "name": "cover" - }, - { - "@type": "ImageObject", - "contentUrl": "https://s3.amazonaws.com/kd4/guylepage3", - "name": "avatar" - } - ], - "name": "Guy Lepage", - "website": [ - { - "@type": "WebSite", - "url": "http://blockstack.com/team" - } - ] - }, - "zonefile": { - "$origin": "guylepage3.id", - "$ttl": 3600, - "uri": [ - { - "name": "_http._tcp", - "priority": 10, - "target": "https://blockstack.s3.amazonaws.com/guylepage3.id", - "weight": 1 - } - ] - } - } -``` -There will always be two keys defined: `profile` and `zonefile`. The hash of the contents of the `zonefile` object are stored in the blockchain, and the Blockstack servers hold copies of zonefile data. The `profile` object is constructed from a **signed JSON web token** (JWT), which is hosted on the storage providers of the user's choice (as determined in the `client.ini` config file). A modern zonefile points to where the profile JWT is hosted. - -The `whois` query is more low-level, and pulls up information that's hosted only in the blockchain. Here's a sample `whois` query: -``` - $ blockstack whois muneeb.id - { - "approx_expiration_date": "2016 Sep 09 13:12:31 UTC", - "block_preordered_at": 373821, - "block_renewed_at": 373821, - "expire_block": 426416, - "has_zonefile": true, - "last_transaction_height": 402804, - "last_transaction_id": "904c5f187ab143d187e26afaddaa6061059451407193fbfc4c4a9b0baa24dbd7", - "owner_address": "1QJQxDas5JhdiXhEbNS14iNjr8auFT96GP", - "owner_script": "76a914ff95f5612a26a81e919e4b6e63fdd929fd115d6d88ac", - "zonefile_hash": "3085137b19ce56092f5cb91b7f78d073c815dbc1" - } -``` - -This information includes the block heights at which the name was preordered (for the first time ever), when it was registered to its curent owner, when it will expire, and when the last transaction (whose ID is given) that affected the name was seen. It also encodes the hash of the zonefile (stored off-chain), the owner's address (public key hash), and owner script (scriptPubKey from Bitcoin). - -Note that the `approx_expiration_data` is *approxmiate*. The system uses the `expire_block` to determine when exactly the name expires; the date is extrapolated from the average block time. You should renew your name well before it expires, just to be sure the transaction gets accepted. We recommend doing it 1,000 blocks before it is set to expire. - -### A Note on Legacy Profiles - -Older profiles used a different structure for storing information. Looking them up will produce a "legacy" zonefile, as well as the profile it represents. - -Legacy zonefiles do not look like DNS zonefiles at all. For example: -``` - $ blockstack lookup muneeb.id - { - "profile": { - "@type": "Person", - "account": [ - { - "@type": "Account", - "identifier": "muneeb", - "proofType": "http", - "service": "twitter" - }, - { - "@type": "Account", - "identifier": "muneeb.ali", - "proofType": "http", - "service": "facebook" - }, - { - "@type": "Account", - "identifier": "muneeb-ali", - "proofType": "http", - "service": "github" - }, - { - "@type": "Account", - "identifier": "1LNLCwtigWAvLkNakUK4jnmmvdVvmULeES", - "role": "payment", - "service": "bitcoin" - }, - { - "@type": "Account", - "contentUrl": "http://muneebali.com/static/files/key.asc", - "identifier": "9862A3FB338BE9EB6C6A5E05639C89272AFEC540", - "role": "key", - "service": "pgp" - } - ], - "address": { - "@type": "PostalAddress", - "addressLocality": "New York, NY" - }, - "description": "Co-founder of Onename (YC S14), final-year PhD candidate at Princeton. Interested in distributed systems and blockchains.", - "image": [ - { - "@type": "ImageObject", - "contentUrl": "https://s3.amazonaws.com/kd4/muneeb", - "name": "avatar" - }, - { - "@type": "ImageObject", - "contentUrl": "https://s3.amazonaws.com/dx3/muneeb", - "name": "cover" - } - ], - "name": "Muneeb Ali", - "website": [ - { - "@type": "WebSite", - "url": "http://muneebali.com" - } - ] - }, - "zonefile": { - "avatar": { - "url": "https://s3.amazonaws.com/kd4/muneeb" - }, - "bio": "Co-founder of Onename (YC S14), final-year PhD candidate at Princeton. Interested in distributed systems and blockchains.", - "bitcoin": { - "address": "1LNLCwtigWAvLkNakUK4jnmmvdVvmULeES" - }, - "cover": { - "url": "https://s3.amazonaws.com/dx3/muneeb" - }, - "facebook": { - "proof": { - "url": "https://facebook.com/muneeb.ali/posts/10152524743274123" - }, - "username": "muneeb.ali" - }, - "github": { - "proof": { - "url": "https://gist.github.com/muneeb-ali/0f00d4da967646ee0bc3" - }, - "username": "muneeb-ali" - }, - "graph": { - "followee_count": 4, - "url": "https://s3.amazonaws.com/grph/muneeb" - }, - "location": { - "formatted": "New York, NY" - }, - "name": { - "formatted": "Muneeb Ali" - }, - "pgp": { - "fingerprint": "9862A3FB338BE9EB6C6A5E05639C89272AFEC540", - "url": "http://muneebali.com/static/files/key.asc" - }, - "twitter": { - "proof": { - "url": "https://twitter.com/muneeb/status/483765788478689280" - }, - "username": "muneeb" - }, - "v": "0.2", - "website": "http://muneebali.com" - } - } -``` - -## Blockstack Wallet - -The Blockstack wallet has three keys: your payment key, your ownership key (i.e. the key that owns the names), and your data key (i.e. the key that signs your profile data). -In the basic mode of operation, you can query information about them with these commands: - -* `blockstack balance`: Query your payment account balance (excludes transactions with less than 6 confirmations). -* `blockstack deposit`: Get your payment address information. This is the address that **pays for names and transaction fees**. -* `blockstack import`: Get your name owner address. This is the address for **transferring a name to a different wallet**. -* `blockstack names`: See the list of names that are owned by your owner address. - -## Registering a Name - -To register a name, simply type: -``` - $ blockstack register .id -``` - -At this time, the name must end in `.id`. It will be registered in the `.id` namespace, since only the `.id` namespace exists (see [here](https://blockstack.org/docs/namespaces) for details). - -You will be prompted to confirm the purchase, and (if you haven't entered it yet), you will be prompted for your wallet password. - -If you'd like to see the price of a name, without actually purchasing it, you can use `blockstack price .id`. - -### A Note on Transaction Fees -The total name cost includes all the relevant transaction fees. However, fee prices are dynamic, and may change during the registration (which requires issuing three transactions). - -To ensure timely registration, you should fill your payment address with **at least +0.001 BTC more than the name cost**. - -## Migrating an Existing Name to the New Profile Schema - -Some advanced options are disabled for older names registered through [Onename](https://onename.com). To enable them, you will need to migrate your name's off-chain data. - -To do so, run `blockstack migrate .id`. It will take about an hour and a half to complete, but only needs to be done once, and only if your name has a legacy zonefile (see above). - -## Checking the Blockstack Server - -You can track the progress of your name transactions with `blockstack info`, which will show you which names have unconfirmed transactions (and what kind they are). The CLI waits for 10 confirmations before considering it confirmed. - -When registering a name, a name will pass through three states: `preorder`, `register`, and `update`. The first step registers your public key and the hash of the name, and waits for it to be confirmed (so no one can front-run you when you reveal the name). The second step reveals the name in the blockchain; you can look at the transaction in a block explorer and find it in the `OP_RETURN` data. The third stage sets up your zonefile and your profile, and writes your zonefile's hash to the blockchain. - -## Other Operations - -### Transferring a Name - -You can send a name to a new ownership address with `blockstack transfer .id `. - -### Renewing a Name - -Names do not last forever, and must be periodically renewed. You can see when a name expires using `whois`, and renew it with `blockstack renew .id`. - -**If you do not renew your name, someone else can register it, and you will not be able to get it back.** The best alternative is to try to ask the new owner if (s)he will sell it back to you. - -### Revoking a Name - -If you lose your Blockstack wallet or the device(s) that host it, you have the option of revoking the name using a backed up copy of your wallet. To do so, type `blockstack revoke .id`. - - -### Updating a Name's Zonefile - -**OpenBazaar Users**: If you are trying to add your OpenBazaar GUID to your Blockstack ID, please follow [these instructions](https://github.com/blockstack/blockstack-cli/blob/master/docs/openbazaar.md) instead. - -**CAUTION**: You almost never want to update your name's zonefile, since it's slow, tedious, and costs money. It is meant primarily for recovering from zonefile loss, for changing where people find your profile, and for changing your data public key. If you want to store data in your profile, please see the [data storage](https://github.com/blockstack/blockstack-cli/blob/master/docs/advanced_usage.md#data-storage) and [accounts](https://github.com/blockstack/blockstack-cli/blob/master/docs/advanced_usage.md#accounts) commands in the [advanced usage](https://github.com/blockstack/blockstack-cli/blob/master/docs/advanced_usage.md) section (but read the [warnings](https://github.com/blockstack/blockstack-cli/blob/master/docs/advanced_usage.md#a-word-of-warning) first). - -If you want to change the name's zonefile, you can do so with `blockstack update`. You must specify the new zonefile to do so. For example: - -``` - $ blockstack update judecn.id '$ORIGIN judecn.id - > $TTL 3600 - > pubkey TXT "pubkey:data:04cabba0b5b9a871dbaa11c044066e281c5feb57243c7d2a452f06a0d708613a46ced59f9f806e601b3353931d1e4a98d7040127f31016311050bedc0d4f1f62ff" - > _file URI 10 1 "file:///home/jude/.blockstack/storage-disk/mutable/judecn.id" - > _https._tcp URI 10 1 "https://blockstack.s3.amazonaws.com/judecn.id" - > _http._tcp URI 10 1 "http://node.blockstack.org:6264/RPC2#judecn.id" - > _dht._udp URI 10 1 "dht+udp://fc4d9c1481a6349fe99f0e3dd7261d67b23dadc5" - > ' -``` - -The zonefile can be any valid DNS zonefile, but must follow these extra rules: -* There must be only one `$ORIGIN`, and it must be the blockstack ID. -* There must be at least one `URI` resource record. -* If you want to set a new data keypair, you must do so via a `TXT` record named `pubkey`, and the text field must start with `pubkey:data:` (as per the example). It must be an ECDSA public key. - -**WARNING**: Each of the URLs must refer to the **signed JSON Web Token** (JWT) that encodes your profile data. The JWT can be signed either with the private key that owns your name, or with a private key that matches the `pubkey:data:` TXT record. If you do not do this, your profile **will not be readable** to the Blockstack CLI tool or to any public profile resolvers. diff --git a/_site/core/cli.md b/_site/core/cli.md deleted file mode 100644 index 09b7f060c2..0000000000 --- a/_site/core/cli.md +++ /dev/null @@ -1,530 +0,0 @@ -# Blockstack CLI - -Blockstack CLI is both a command-line interface (CLI) tool, a system service (daemon), and a client (Python library) for interacting with Blockstack. It talks to Blockstack Core and provides an interface for interacting with it. - -## Architecture Overview - -Most of the complexity of Blockstack lives in its client library. Specifically, the library does the following: - -* Generating and sending name operation transactions. -* Reading, writing, and deleting data in your storage providers (and reading other peoples' data from their storage providers). -* Handling data authenticity, encryption, and validation. -* Querying a Blockstack Server for blockchain-hosted information. - -The CLI tool is a wrapper around the library. Most of its commands are thin wrappers around library functions. - -In addition to a CLI tool and library, Blockstack CLI comes with a system service that runs in the background as a daemon. The daemon does the following: - -* Acts as a personal registrar. It queues up all your name operation transactions, waits for them to be confirmed by the blockchain, sends them out, and replicates your zonefile and profile as needed. -* Hosts your wallet. Your wallet is never stored in plaintext; it only lives in the daemon's RAM (for when it needs to send out transactions). -* Allows programmatic access to a subset of CLI commands. This allows other programs on your computer to do things like look up Blockstack IDs, query their data, and so on. For security, the daemon will never serve the wallet via the API, nor does it expose any API call that can change data or send transactions (it is effectively a read-only API). - -## Files - -These files are created by Blockstack CLI: - -### Files You Can Edit - -These files define how the CLI behaves. - -* `~/.blockstack/client.ini`: This is the CLI config file. You can interactively modify it with `blockstack configure`. -* `~/.blockstack/wallet.json`: This is your JSON-encoded wallet. It contains your password-encrypted keys. - -### Files You Can Read - -These files are useful primarily for troubleshooting. - -* `~/.blockstack/api_endpoint.log`: This is the log for the system service that manages your wallet, sends your name operations, and accesses your profile and data. It's a great source for troubleshooting. -* `~/.blockstack/api_endpoint.pid`: This contains the PID of the system service. -* `~/.blockstack/metadata/`: This directory contains versioning information for mutable data from other profiles you have read. It gets used to stop malicious storage providers from serving you older versions of the data you've already seen. -* `~/.blockstack/storage-disk/`: If you use the `disk` storage driver (it is activated by default), then this is where it holds your zonefiles, profiles, and data. - -### Files You Should NOT Edit - -You shouldn't touch these files unless you're a developer, and even then, you should only do so at your own risk. - -* `~/.blockstack/queues.db`: This is a SQLite database that contains queued-up transactions for name operations. If you want to remove any stuck transactions, use `blockstack unqueue` in the [advanced](https://github.com/blockstack/blockstack-cli/blob/master/docs/advanced_usage.md) usage. -* `~/.blockstack/registrar.lock`: This is a lockfile held by a thread in the registrar thread in the CLI daemon. -* `~/.blockstack/blockchain_headers.dat`: SPV headers. -* `~/.blockstack/client.uuid`: Used for anonymous statistics gathering purposes (which you can disable using `blockstack configure`). If you remove it, a new one will be generated. - -## Getting Help - -If you ever need help with these instructions or want to learn more, please join the [Blockstack Slack](https://blockstack.slack.com) and drop us a line on the \#cli channel. - -## Installation - -Installing the command line interface and client library: - -### Debian + Ubuntu - -Via APT: -``` -$ curl https://raw.githubusercontent.com/blockstack/packaging/master/repo-key.pub | sudo apt-key add - -$ sudo sh -c "echo \"deb http://packages.blockstack.com/repositories/ubuntu xenial main\" > /etc/apt/sources.list.d/blockstack.list" -$ sudo apt-get update -$ sudo apt-get install blockstack -``` - -Via pip: -``` -$ sudo apt-get update && sudo apt-get install -y python-pip python-dev libssl-dev libffi-dev rng-tools -$ sudo pip install blockstack --upgrade -``` - -### OS X - -```bash -$ brew install libffi openssl -$ sudo pip install blockstack --upgrade -``` - -We recommend installing the CLI inside of a [virtual environment](http://docs.python-guide.org/en/latest/dev/virtualenvs/), in which case you can drop the "sudo" at the beginning like so: - -```bash -$ pip install blockstack --upgrade -``` - -If the installation command above fails, see the [troubleshooting section](#troubleshooting-installation). - -### Windows Subsystem for Linux - -Installation will mirror `Debian + Ubuntu`, above, with an additional package. - -```bash -$ sudo apt-get update && sudo apt-get install -y python-pip python-dev libssl-dev libffi-dev -``` - -```bash -$ sudo pip install functools32 -$ sudo pip install blockstack -``` - -## Command-Line Reference - -### Listing All Commands - -```bash -$ blockstack -usage: blockstack [-h] - ... - -Blockstack cli version 0.14.0 -positional arguments: - balance Get the account balance - configure Interactively configure the client - deposit Display the address with which to receive bitcoins - import Display the address with which to receive names - info Get details about pending name commands - lookup Get the zone file and profile for a particular name - migrate Migrate a profile to the latest profile format - names Display the names owned by local addresses - ping Check server status and get server details - price Get the price of a name - register Register a name - renew Renew a name - revoke Revoke a name - set_advanced_mode Enable advanced commands - transfer Transfer a name to a new address - update Set the zone file for a name - whois Look up the blockchain info for a name - -optional arguments: - -h, --help show this help message and exit -``` - -### Info (or ping or status) - -```bash -$ blockstack info -``` - -##### Examples - -```bash -$ blockstack info -{ - "advanced_mode": false, - "cli_version": "0.14.0", - "consensus_hash": "106d4648661d49e16d103b071e26617e", - "last_block_processed": 420518, - "last_block_seen": 420596, - "server_alive": true, - "server_host": "40.76.8.249", - "server_port": "6264", - "server_version": "0.14.0" -} -``` - -### Config - -```bash -$ blockstack configure -``` - -##### Examples - -```bash -$ blockstack configure -# ... interactive prompts ... -{ - "path": "/home/jude/.blockstack/client.ini" -} -``` - -### Cost - -```bash -$ blockstack price -``` - -##### Examples - -```bash -$ blockstack price $(whoami).id -{ - "name_price": { - "btc": "0.00025", - "satoshis": "25000" - }, - "preorder_tx_fee": { - "btc": "0.00047406", - "satoshis": "47406" - }, - "register_tx_fee": { - "btc": "0.00046184", - "satoshis": "46184" - }, - "total_estimated_cost": { - "btc": "0.00188394", - "satoshis": "188394" - }, - "update_tx_fee": { - "btc": "0.00069804", - "satoshis": "69804" - } -} -``` - -### Whois - -```bash -$ blockstack whois -``` - -##### Examples - -```bash -$ blockstack whois fredwilson.id -{ - "block_preordered_at": 374084, - "block_renewed_at": 374084, - "expire_block": 426679, - "has_zonefile": true, - "last_transaction_id": "2986ec31ec957692d7f5bc58a3b02d2ac2d1a60039e9163365fc954ff51aeb5a", - "owner_address": "1F2nHEDLRJ39XxAvSxwQhJsaVzvS5RHDRM", - "owner_script": "76a91499e7f97f5d2c77b4f32b4ed9ae0f0385c45aa5c788ac", - "zonefile_hash": "1a587366368aaf8477d5ddcea2557dcbcc67073e" -} -``` - -```bash -$ blockstack whois $(whoami)_$(date +"%m_%d").id -Not found. -``` - -### Lookup - -```bash -$ blockstack lookup -``` - -##### Examples - -```bash -$ blockstack lookup fredwilson.id -{ - "profile": { - "avatar": { - "url": "https://s3.amazonaws.com/kd4/fredwilson1" - }, - "bio": "I am a VC", - ... -} - -``` - -```bash -$ blockstack lookup $(whoami)_$(date +"%m_%d").id -Not found. -``` - -### Register - -```bash -$ blockstack register -``` - -##### Example - -```bash -$ blockstack register $(whoami)_$(date +"%m_%d").id -Registering muneeb_02_22.id will cost 0.0002225 BTC. Continue? (y/n): y -{ - "transaction_hash": "f576313b2ff4cc7cb0d25545e1e38e2d0d48a6ef486b7118e5ca0f8e8b98ae45", - "message": "The name has been queued up for registration and will take a few hours to go through. You can check on the status at any time by running 'blockstack info'." - "success": true -} -``` - -```bash -$ blockstack register fredwilson.id -fredwilson.id is already registered. -``` - -### Update - -```bash -$ blockstack update -``` - -##### Examples - -```bash -$ echo > new_zone_file.txt <
      -``` - -##### Examples - -```bash -$ blockstack transfer $(whoami)_$(date +"%m_%d").id 1Jbcrh9Lkwm73jXyxramFukViEtktwq8gt -{ - "transaction_hash": "8a68d52d70cf06d819eb72a9a58f4dceda942db792ceb35dd333f43f55fa8713", - "message": "The name has been queued up for transfer and will take ~1 hour to process. You can check on the status at any time by running 'blockstack info'." - "success": true -} -``` - -```bash -$ blockstack transfer fredwilson.id 1Jbcrh9Lkwm73jXyxramFukViEtktwq8gt -fredwilson.id is not in your possession. -``` - -### Balance - -```bash -$ blockstack balance -``` - -##### Examples - -```bash -$ blockstack balance -{ - "addresses": [ - { - "address": "16yE3e928JakaXbympwSywyrJPM9cuL4wZ", - "bitcoin": 0.00959454, - "satoshis": 959454 - } - ], - "total_balance": { - "bitcoin": 0.00959454, - "satoshis": 959454 - } -} -``` - -### Names - -```bash -$ blockstack names -``` - -##### Examples - -```bash -$ blockstack names -{ - "addresses": [ - { - "address": "16CtpS8LhmW3bGtVC69UGZ3wSwvi95BE8E", - "names_owned": [ - "testregistration001.id", - "testregistration002.id" - ] - } - ], - "names_owned": [ - "testregistration001.id", - "testregistration002.id" - ] -} -``` - -### Deposit - -```bash -$ blockstack deposit -``` - -##### Examples - -```bash -$ blockstack deposit -{ - "address": "1EHgqHVpA1tjn6RhaVj8bx6y5NGvBwoMNS", - "message": "Send bitcoins to the address specified.", -} -``` - -### Import - -```bash -$ blockstack import -``` - -##### Examples - -```bash -$ blockstack import -{ - "address": "1Jbcrh9Lkwm73jXyxramFukViEtktwq8gt" - "message": "Send the name you want to receive to the address specified.", -} -``` - -## Troubleshooting Installation - -**a) Error installing pycrypto** - -If you see the following error, while pycrpyto installs on OS X: - -```bash -error: command 'cc' failed with exit status 1 -``` - -Try installing it with the following: - -```bash -$ ARCHFLAGS=-Wno-error=unused-command-line-argument-hard-error-in-future pip install pycrypto -``` - -**b) Blockstack hangs while running in a VM** - -If Blockstack hangs while performing one of the above operations while running in a VM, and you hit Ctrl+C, you -may see a stack trace like this: - -``` -Traceback (most recent call last): - File "/home/dev/blockstack-venv/bin/blockstack", line 67, in - result = run_cli() - File "/home/dev/blockstack-venv/local/lib/python2.7/site-packages/blockstack_client/cli.py", line 287, in run_cli - result = method( args, config_path=config_path ) - File "/home/dev/blockstack-venv/local/lib/python2.7/site-packages/blockstack_client/actions.py", line 479, in cli_price - fees = get_total_registration_fees( fqu, payment_privkey_info, owner_privkey_info, proxy=proxy, config_path=config_path, payment_address=payment_address ) - File "/home/dev/blockstack-venv/local/lib/python2.7/site-packages/blockstack_client/actions.py", line 271, in get_total_registration_fees - preorder_tx_fee = estimate_preorder_tx_fee( name, data['satoshis'], payment_address, utxo_client, owner_privkey_params=get_privkey_info_params(owner_privkey_info), config_path=config_path, include_dust=True ) - File "/home/dev/blockstack-venv/local/lib/python2.7/site-packages/blockstack_client/backend/nameops.py", line 116, in estimate_preorder_tx_fee - fake_privkey = make_fake_privkey_info( owner_privkey_params ) - File "/home/dev/blockstack-venv/local/lib/python2.7/site-packages/blockstack_client/backend/nameops.py", line 103, in make_fake_privkey_info - return virtualchain.make_multisig_wallet( m, n ) - File "/home/dev/blockstack-venv/local/lib/python2.7/site-packages/virtualchain/lib/blockchain/bitcoin_blockchain/multisig.py", line 82, in make_multisig_wallet - pk = BitcoinPrivateKey().to_wif() - File "/home/dev/blockstack-venv/local/lib/python2.7/site-packages/pybitcoin/privatekey.py", line 55, in __init__ - secret_exponent = random_secret_exponent(self._curve.order) - File "/home/dev/blockstack-venv/local/lib/python2.7/site-packages/pybitcoin/privatekey.py", line 32, in random_secret_exponent - random_hex = hexlify(dev_random_entropy(32)) - File "/home/dev/blockstack-venv/local/lib/python2.7/site-packages/utilitybelt/entropy.py", line 38, in dev_random_entropy - return open("/dev/random", "rb").read(numbytes) -KeyboardInterrupt -``` - -If so, the reason is because the VM does not have enough entropy. This causes reads to `/dev/random` to block -for a long time. - -The solution is to install `rng-tools` and configure it to seed `/dev/random` with entropy from `/dev/urandom`. -Please see your distribution documentation for setting up `rng-tools`. - -If the issue you are experiencing is not listed here, please -[report it as a new issue](https://github.com/blockstack/blockstack-client/issues/new). - -## Running Your Server - -The CLI by default talks to a remote server, but you can easily start your own server. - -Open a new terminal window and run the following command: - -```bash -$ blockstack-server start --foreground -``` - -You can now switch the cli to use the local server: - -```bash -$ blockstack configure -... -server (default: 'node.blockstack.org'): 127.0.0.1 -... -``` - -[More information on the Blockstack Server(http://github.com/blockstack/blockstack-server) - -## Client Library - -You can also import the blockstack client and write your own programs. - -Here is some example code to get you started: - -```python -from blockstack_client import client -from blockstack_client.utils import print_result as pprint - -client.session(server_host='127.0.0.1', server_port=6264) -resp = client.ping() -pprint(resp) -``` diff --git a/_site/core/faq_technical.md b/_site/core/faq_technical.md deleted file mode 100644 index f1f1c30f97..0000000000 --- a/_site/core/faq_technical.md +++ /dev/null @@ -1,385 +0,0 @@ -# Blockstack Technical FAQ - -This document lists frequently-asked questions and answers to technical -questions about Blockstack. - -If you are new to Blockstack, you should read the -[non-technical FAQ](https://blockstack.org/faq) first. - -If you have a technical question that gets frequently asked on the -[forum](https://forum.blockstack.org) or [Slack](https://blockstack.slack.com), -feel free to send a pull-request with the question and answer. - -# General Questions - -## What is Blockstack? - -Blockstack is a new Internet for decentralized applications. Blockstack -applications differ from Web applications in two ways: - -* **Users own their identities**. The user brings their identity to the - applications; applications do not require the user to create accounts and -passwords. -* **Users own their data**. Users control who can read it, and where it gets stored. - The application does not need to worry about hosting any user data. - -The Blockstack project provides all of the infrastructure required for building -these kinds of applications. - -## Is Blockstack decentralized? - -Yes! The components that make up Blockstack do not have any central points of -control. - -* The [Blockstack Naming Service](blockstack_naming_service.md) runs on top of - the Bitcoin blockchain, which itself is decentralized. It binds Blockstack -IDs to a small amount of on-chain data (usually a hash of off-chain data). -* The [Atlas Peer Network](atlas_network.md) stores chunks of data referenced by -names in BNS. It operates under similar design principles to BitTorrent, and -has no single points of failure. The network is self-healing---if a node -crashes, it quickly recovers all of its state from its peers. -* The [Gaia storage system](https://github.com/blockstack/gaia) lets users - choose where their application data gets hosted. Gaia reduces all storage -systems---from cloud storage to peer-to-peer networks---to dumb, interchangeable -hard drives. Users have maximum flexibility and control over their data in a -way that is transparent to app developers. - -## Are Blockstack applications usable today? - -Yes! Blockstack applications are as easy to use as normal Web applications, if -not easier. Moreover, they are just as performant if not more so. - -If you install the [Blockstack -Browser](https://github.com/blockstack/blockstack-browser), or use our -[Web-hosted Blockstack Browser](https://browser.blockstack.org), you can get -started with them right away. - -## Where does Blockstack keep my user account? - -Your user account is ultimately controlled by a [private -key](https://en.wikipedia.org/wiki/Public-key_cryptography). You and only you -know what the private key is, and using your private key, you can prove to other -people that you own a particular piece of data (such as your Blockstack ID). - -Your private key resides within your locally-running Blockstack Browser. -It never leaves your computer. - -Your public keys are stored off-chain, and the *hash* of your public key is -stored on the Bitcoin blockchain. The [Blockstack Naming -Service](blockstack_naming_service.md) allows anyone to look up your public key -hash, given your Blockstack ID. - -## Where does Blockstack keep my app data? - -As a Blockstack user, you can choose exactly where your data gets stored. -Blockstack uses a decentralized storage system called -[Gaia](https://github.com/blockstack/gaia) to host your data. Gaia is different -from other storage systems because it lets you securely host your data wherever you want---in cloud -storage providers, on your personal server, or in another decentralized storage -system like BitTorrent or IPFS. - -When you register, you are given a default Gaia hub that replicates your -data to a bucket in Microsoft Azure. However, you can configure and -deploy your own Gaia hub and have Blockstack store your data there instead. - -The [Blockstack Naming Service](blockstack_naming_service.md) and the [Atlas -network](atlas_network.md) work together to help other users discover your -app-specific public data, given your Blockstack ID. - -# Blockstack IDs - -## What is a Blockstack ID? - -Blockstack IDs are usernames. Unlike normal Web app usernames, Blockstack IDs -are usable *across every Blockstack app.* They fill a similar role to -centralized single-signon services like Facebook or Google. However, you and -only you control your Blockstack ID, and no one can track your logins. - -## How do I get a Blockstack ID? - -If you install the [Blockstack -Browser](https://github.com/blockstack/blockstack-browser) or use the -[Web-hosted Blockstack Browser](https://browser.blockstack.org), you can -purchase one with Bitcoin. - -## Do I need a Blockstack ID to use Blockstack apps? - -No, you can use Blockstack applications right away. However, if you want to -*share data with other users*, then you need a Blockstack ID. - -## Why do I need a Blockstack ID? - -Blockstack IDs are used to discover where you are keeping your -(publicly-readable) application data. For example, if `alice.id` wants to share -a document with `bob.id`, then `bob.id`'s browser uses the Blockstack ID -`alice.id` to look up where `alice.id` stored it. - -The technical descriptions of how and why this works are quite long. -Please see the [Blockstack Naming Service](blockstack_naming_service.md) -documentation for a full description. - -## What is a Blockstack Subdomain? - -This is also a Blockstack ID, and can be used for all the things a Blockstack ID -can be used for. The only difference is that they have the format `foo.bar.baz` -instead of `bar.baz`. For example, -[jude.personal.id](https://core.blockstack.org/v1/users/jude.personal.id) is a -Blockstack ID, and is a subdomain of `personal.id`. - -Subdomains are first-class Blockstack IDs---they can be used for all the same -things that an on-chain Blockstack ID can be used for, and they have all of -the same safety properties. They are globally unique, they are strongly owned -by a private key, and they are human-readable. - -Subdomains are considerably cheaper than Blockstack IDs, since hundreds of them -can be registered with a single transaction. The [BNS -documentation](blockstack_naming_service.md) describes them in detail. - -Subdomains provide a fast, inexpensive way to onboard many users at once. - -## Can I get a Blockstack ID without spending Bitcoin? - -Blockstack subdomains can be obtained without spending Bitcoin -by asking a subdomain registrar to create one for you. - -## Is there a Blockstack name explorer? - -Yes! It's at https://explorer.blockstack.org - -# Blockstack App Development - -## I'm a Web developer. Can I build on Blockstack? - -Yes! Blockstack is geared primarily towards Web developers. All of your -existing knowledge is immediately applicable to Blockstack. Anything you can do -in a Web browser, you can do in a Blockstack app. - -## I'm a non-Web developer. Can I build on Blockstack? - -Yes! Blockstack implements a [RESTful API](https://core.blockstack.org) which -lets you interact with Blockstack from any language and any runtime. In fact, -the reference client -([blockstack.js](https://github.com/blockstack/blockstack.js)) is mainly a -wrapper around these RESTful API calls, so you won't be missing much by using a -language other than Javascript. - -## What's the difference between a Web app and a Blockstack app? - -Blockstack apps are built like [single-page Web -apps](https://en.wikipedia.org/wiki/Single-page_application)---they are, in -fact, a type of Web application. - -Blockstack apps are a subset of Web applications that use Blockstack's -technology to preserve the user's control over their identities and data. -As such, they tend to be simpler -in design and operation, since in many cases they don't have to host anything -besides the application's assets. - -## Do I need to learn any new languages or frameworks? - -No. Blockstack applications are built using existing Web frameworks and programming -The only new thing you need to learn is either [blockstack.js](https://github.com/blockstack/blockstack.js) or -the [Blockstack RESTful API](https://core.blockstack.org). - -## How does my Web app interact with Blockstack? - -The [blockstack.js](https://github.com/blockstack/blockstack.js) library gives -any Web application the ability to interact with Blockstack's authentication and -storage services. In addition, we supply a [public RESTful API](https://core.blockstack.org). - -## What does `blockstack.js` do? - -This is the reference client implementation for Blockstack. You use it in your -Web app to do the following: - -* Authenticate users -* Load and store user data -* Read other users' public data - -## How do I use `blockstack.js`? - -Please see the API documentation [here](https://github.com/blockstack/blockstack.js). - -## How can I look up names and profiles? - -You can use `blockstack.js`, or you can use the [public Blockstack Core -endpoint](https://core.blockstack.org). - -## How can I read my public app data without `blockstack.js`? - -The URLs to a user's public app data are in a canonical location in their -profile. For example, here's how you would get public data from the -[Publik](https://publik.ykliao.com) app, stored under the Blockstack ID `ryan.id`. - -1. Get the bucket URL -```bash -$ BUCKET_URL="$(curl -sL https://core.blockstack.org/v1/users/ryan.id | jq -r '."ryan.id"["profile"]["apps"]["http://publik.ykliao.com"]')" -$ echo "$BUCKET_URL" -https://gaia.blockstack.org/hub/1FrZTGQ8DM9TMPfGXtXMUvt2NNebLiSzad/ -``` - -2. Get the data -```bash -$ curl -sL "${BUCKET_URL%%/}/statuses.json" -[{"id":0,"text":"Hello, Blockstack!","created_at":1515786983492}] -``` - -## How do I register Blockstack IDs? - -You should use the [Blockstack Browser](https://github.com/blockstack/blockstack-browser). - -## How do I register Blockstack Subdomains? - -You can deploy and use a [Blockstack Subdomain Registrar](subdomains.md), or -use an existing one. - -## Can I programmatically register Blockstack IDs? - -Blockstack applications do not currently have -have access to the user's wallet. Users are expected to -register Blockstack IDs themselves. - -However, if you feel particularly ambitious, you can do one of the following: - -* Set up a `blockstack api` endpoint (see the project [README](../README.md)) and write a - program to automatically register names. Also, see the [API -documentation](https://blockstack.github.io/blockstack-core/#managing-names-register-a-name) -for registering names on this endpoint. - -* Write a `node.js` program that uses `blockstack.js` to register - names. This is currently in development. - -## Can I programmatically register Blockstack Subdomains? - -Yes! Once you deploy your own subdomain registrar, you can have your Web app -send it requests to register subdomains on your Blockstack ID. You can also -create a program that drives subdomain registration on your Blockstack ID. - -## Do you have a testnet or sandbox to experiment with Blockstack? - -We have an [integration test framework](../integration_tests) that provides a -private Blockstack testnet. It uses `bitcoin -regtest` to create a private -blockchain that you can interact with, without having to spend any Bitcoin or -having to wait for blocks to confirm. Please see the -[README](../integration_tests/README.md) for details. - -## Does Blockstack have a smart contract system? - -No, not yet. This is because -Blockstack's design philosophy focuses on keeping system complexity at the -"edges" of the network (e.g. clients), instead of the "core" of the network (e.g. -the blockchain), in accordance with the [end-to-end -principle](https://en.wikipedia.org/wiki/End-to-end_principle). -Generally speaking, this can be interpreted as "if you can do X without -a smart contract, you should do X without a smart contract." This organizing -principle applies to a lot of useful decentralized applications. - -## Can Blockstack applications interact with Bitcoin? Ethereum? Smart contracts? Other blockchains? - -Yes! Since Blockstack applications are built like Web applications, all you need to do is include the -relevant Javascript library into your application. - -## Do you have a Blockstack app development tutorial? - -Yes! See [here](https://blockstack.org/tutorials). - -# Comparisons to Other Systems - -## Blockstack vs DNS - -Blockstack and DNS both implement naming systems, but in fundamentally -different ways. Blockstack *can be used* for resolving host names to IP -addresses, but this is not its default use-case. The [Blockstack Naming -Service](blockstack_naming_service.md) (BNS) instead behaves -more like a decentralized -[LDAP](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol) system for -resolving user names to user data. - -While DNS and BNS handle different problems, they share some terminology and -serialization formats. However, it is important to recognize that this is the -*only* thing they have in common---BNS has fundamentally different semantics -than DNS: - -* **Zone files**: Blockstack stores a DNS zone file for each name. However, -the semantics of a BNS zone file are nothing like the semantics of a DNS zone -file---the only thing they have in common is their format. -A "standard" Blockstack zone files only have `URI` and `TXT` resource records -that point to the user's application data. Moreover, a Blockstack ID has a -*history* of zone files, and historic zone files can alter the way in which a -Blockstack ID gets resolved (DNS has no such concept). It is conceivable that an advanced -user could add `A` and `AAAA` records to their Blockstack ID's zone file, -but these are not honored by any Blockstack software at this time. - -* **Subdomains**: Blockstack has the concept of a subdomain, but it is - semantically very different from a DNS subdomain. In Blockstack, a subdomain -is a Blockstack ID whose state and transaction history are anchored to the -blockchain, but stored within an on-chain Blockstack ID's zone file history. -Unlike DNS subdomains, a BNS subdomain has -its own owner and is a first-class BNS name---all subdomains are resolvable, -and only the subdomain's owner can update the subdomain's records. The only thing BNS subdomains and DNS -subdomains have in common is the name format (e.g. `foo.bar.baz` is a subdomain -of `bar.baz` in both DNS and BNS). - -More details can be found in the [Blockstack vs -DNS](https://blockstack.org/docs/blockstack-vs-dns) document. A feature -comparison can be found at the end of the [Blockstack Naming -Service](blockstack_naming_service.md) document. - -## Blockstack vs Namecoin - -Namecoin also implements a decentralized naming service on top of a blockchain, -just like BNS. In fact, early versions of Blockstack were built on Namecoin. -However, [it was discovered](https://www.usenix.org/node/196209) that Namecoin's -merged mining with Bitcoin regularly placed it under the *de facto* control of a single -miner. This prompted a re-architecting of the system to be *portable* across -blockchains, so that if Blockstack's underlying blockchain (currently Bitcoin) -ever became insecure, the system could migrate to a more secure blockchain. - -A feature comparison can be found at the end of the [Blockstack Naming -Service](blockstack_naming_service.md) document. - -## Blockstack vs ENS - -ENS also implements a decentralized naming system on top of a blockchain, but as -a smart contract on Ethereum. Like BNS, ENS is geared towards resolving names -to off-chain state (ENS names resolve to a hash, for example). Moreover, ENS is -geared towards providing programmatic control over names with Turing-complete -on-chain resolvers. - -BNS has a fundamentally different relationship with blockchains than ENS. -WHereas ENS tries to use on-chain logic as much as possible, BNS -tries to use the blockchain as little as possible. BNS only uses it to store a -database log for name operations (which are interpreted with an off-chain BNS -node like Blockstack Core). BNS name state and BNS subdomains reside entirely -off-chain in the Atlas network. This has allowed BNS to migrate from blockchain -to blockchain in order to survive individual blockchain failures, and this has -allowed BNS developers to upgrade its consensus rules without having to get the -blockchain's permission (see the [virtualchain -paper](https://blockstack.org/virtualchain_dccl2016.pdf) for details). - -A feature comparison can be found at the end of the [Blockstack Naming -Service](blockstack_naming_service.md) document. - -## Blockstack vs Ethereum - -Blockstack and Ethereum both strive to provide a decentralized application -platform. Blockstack's design philosophy differs from Ethereum's design -philosophy in that Blockstack emphasizes treating the blockchain as a "dumb -ledger" with no special functionality or properties beyond a few bare minimum -requirements. Instead, it strives to do everything off-chain---an application of the [end-to-end principle](https://en.wikipedia.org/wiki/End-to-end_principle). -Most Blockstack applications do *not* -interact with the blockchain, and instead interact with Blockstack -infrastructure through client libraries and RESTful endpoints. -This is evidenced by Blockstack's decision to implement its naming system (BNS), discovery and routing system -(Atlas), and storage system (Gaia) as blockchain-agnostic components that can be -ported from one blockchain to another. - -Ethereum takes the opposite approach. Ethereum dapps are expected to interface -directly with on-chain smart contract logic, and are expected to host a -non-trivial amount of state in the blockchain itself. This is necessary for -them, because many Ethereum dapps' business logic is centered around the -mechanics of an ERC20 token. - -Blockstack does not implement a smart contract system (yet), but it will soon -implement a [native token](https://blockstack.com/distribution.pdf) that will be -accessible to Blockstack applications. diff --git a/_site/core/figures/test-screen.png b/_site/core/figures/test-screen.png deleted file mode 100644 index c47fc26b65..0000000000 Binary files a/_site/core/figures/test-screen.png and /dev/null differ diff --git a/_site/core/gaia.md b/_site/core/gaia.md deleted file mode 100644 index 0060549c62..0000000000 --- a/_site/core/gaia.md +++ /dev/null @@ -1,3 +0,0 @@ -# Gaia Documentation - -This document has moved to the [Gaia repository](https://github.com/blockstack/gaia) diff --git a/_site/core/glossary.md b/_site/core/glossary.md deleted file mode 100644 index c710ca277e..0000000000 --- a/_site/core/glossary.md +++ /dev/null @@ -1,148 +0,0 @@ -# Glossary - -Commonly used terms and jargon in Blockstack - -## Account - -A field in a profile that links the name to an existing service, like Twitter or OpenBazaar. They are listed under the `accounts` listing in a profile. - -Some accounts serve as social proofs, but they can contain any data the user wants. - -## Atlas - -A peer-to-peer network maintained by Blockstack Core nodes that stores each -name's zone files and immutable data. See [this document](atlas_network.md) for -details. - -## Blockstack ID - -(Also called a "name"). - -A human-readable name in Blockstack. It is comprised only of upper and lower-case ASCII characters, numbers, as well as `-`, `_`, and `.`. It must end with a `.`, followed by a namespace ID. It has at least 3 characters, and at most 37 (including the `.` and the namespace ID). - -Anyone can register a Blockstack ID, such as through the [Blockstack Browser](https://github.com/blockstack/blockstack-browser) - -## Blockstack Core - -A server that reads a blockchain with [virtualchain](https://github.com/blockstack/blockstack-virtualchain), filters out transactions that represent name operations, and builds up a database of (name, public key, state value) triples. - -## Blockstack Naming Service (BNS) - -This is the naming protocol that Blockstack Core implements. See [this -document](blockstack_naming_service.md) for details. - -## Consensus Hash - -A cryptographic hash that represents a proof-of-computation by a Blockstack Core node. Two Blockstack Core nodes have seen and processed the same name operations up to block `n` if and only if they each calculate the same consensus hash at height `n`. - -A Blockstack Core node only accepts a name operation if it has a previously-calculated but recent consensus hash. Blockstack clients obtain a consensus hash from a Blockstack Core node in order to construct a name operation. - -## Gaia - -This is Blockstack's storage system. Gaia hosts all of your app-specific data. - -## Gaia Hub - -This is a publicly-routable server that serves as an entry point for Gaia data. -Anyone can stand up and run a Gaia hub by following [these -instructions](https://github.com/blockstack/gaia). -Blockstack provides a [default Gaia hub](https://gaia.blockstack.org). - -## Immutable Data - -This is the general term for chunks of data whose hash is cryptographically -bound to a blockchain transaction. This includes all data stored in the Atlas -network (such as your Blockstack ID's zone file), -as well as any data whose hash is stored in the Atlas network. - -## Mutable Data - -This is the general term for data that is (1) signed by your Blockstack ID, and -(2) can be looked up using your Blockstack ID. This includes all your Gaia -data, as well as your profile. - -## Name - -See Blockstack ID. - -## Name Database - -The set of (name, public key, name state) triples that the Blockstack Core node generates by reading the blockchain. The name state is usually the hash of a DNS zone file stored in Atlas. - -## Name Operation - -A specially-crafted transaction in the underlying blockchain that, when processed, will change each Blockstack Core's name database. Examples include `NAME_PREORDER` (preorders a name), `NAME_REGISTRATION` (registers a name), `NAME_UPDATE` (changes a name's zonefile hash), `NAME_TRANSFER` (changes a name's public key), and `NAME_REVOKE` (locks everyone out of a name until it expires). - -Name operations are encoded on Bitcoin as `OP_RETURN` outputs that start with `id`, followed by a 1-byte character that identifies the particular operation. - -See the [wire format](wire-format.md) document for details. - -## Namespace - -Analogous to a DNS TLD, it represents a grouping of names. All names under the same namespace have the same pricing and lifetime rules. - -Anyone can create a namespace, but doing so is expensive by design. See the -[namespace creation](namespace_creation.md) tutorial for details. - -## Preorder - -The first of two steps to acquire a name. This operation writes the hash of both the name and the address that will own it. - -## Profile - -A signed JSON web token that describes a [Person](https://schema.org/Person), which describes the name's owner. You can put anything you want into your profile. - -Additionally, profiles hold lists of social verifications and pointers to your Gaia data. - -## Register - -(1) The act of acquiring a name in Blockstack. - -(2) The second of two steps to create a new name entry in the name database. Reveals the name as plaintext to the world. Must match a recent preorder to be accepted. - -## Registrar - -An online service that lets you sign up for and manage the profiles of Blockstack IDs. - -## Resolver - -An online service that displays zonefile and profile data for a Blockstack ID. [The Blockstack Explorer](https://explorer.blockstack.org) is a resolver. - -## Social proof - -A post in an account on an existing Web service like Twitter, Facebook, or GitHub that points back to a Blockstack ID. Used to provide some evidence that the person who owns the Blockstack ID is also the person who owns the Web service account. - -Social proofs are listed in your profile. - -## Storage Provider - -This is any service that can serve your zone file, profile, or data. In all cases, the data is signed by one of your wallet's keys, so you can use any provider without having to worry about it trying to change the data. - -Storage providers are accessed through a Gaia hub. Gaia hubs ship with drivers -that allow them to treat storage providers as dumb hard drives, which store -signed encrypted data on the hub's behalf. - -Not all storage providers support writes--some of them are read-only. - -Supported storage providers today include: -* Amazon S3 -* Dropbox -* Your harddrive -* Any HTTP/HTTPS/FTP server (read-only) -* Any public-use Gaia hub -* IPFS - -Support is being added for: -* Google Drive -* Microsoft OneDrive -* Box.com -* BitTorrent - -If you have a preferred storage provider, and you're a developer, please consider sending us a pull request to add support for it! - -## Zone file - -A specially-formatted file that stores routing information for a Blockstack ID. -Blockstack clients use your zone file to find out where your preferred Gaia -hub(s) are. Ever Blockstack Core node stores a copy of every zone file for -every Blockstack ID by participating in the Atlas network. diff --git a/_site/core/identity/openbazaar.md b/_site/core/identity/openbazaar.md deleted file mode 100644 index 9859401344..0000000000 --- a/_site/core/identity/openbazaar.md +++ /dev/null @@ -1,64 +0,0 @@ -# Linking your OpenBazaar GUID to your Blockstack ID - -If you don't have the Blockstack CLI. Download and install it first. Instructions are [here](https://github.com/blockstack/blockstack-cli/blob/master/README.md). The rest of this tutorial assumes that you've already registered a name using the Blockstack CLI. - -## Step 1: Advanced Mode - -The first step is to activate "advanced mode" in the CLI. The command to do so is: - -``` - $ blockstack set_advanced_mode on -``` - -## Step 2: Add an OpenBazaar Account - -The second step is to create an OpenBazaar account for your profile (the [Onename](https://onename.com) app also enabled to link your OpenBazaar GUID). The command to do so is: - -``` - $ blockstack put_account "" "openbazaar" "" "" -``` - -The URL can be any valid URL; it won't be used by OpenBazaar. Here's an example, using the name `testregistration001.id` and the GUID `0123456789abcdef`: - -``` - $ blockstack put_account "testregistration001.id" "openbazaar" "0123456789abcdef" "https://bazaarbay.org/@testregistration001" -``` - -The update should be instantaneous. You can verify that your store is present with `list_accounts`: - -``` - $ blockstack list_accounts "testregistration001.id" - { - "accounts": [ - { - "contentUrl": "https://bazaarbay.org/@testregistration001.id", - "identifier": "0123456789abcdef", - "service": "openbazaar" - } - ] - } -```` - -# Troubleshooting - -Common problems you might encounter. - -## Profile is in legacy format - -If you registered your blockstack ID before spring 2016, there's a chance that your profile is still in a legacy format. It will get migrated to the new format automatically if you update your profile on the [Onename](https://onename.com) app. However, you have to do this manually with the CLI. - -To do so, the command is: -``` - $ blockstack migrate -``` - -It will take a little over an hour to complete, but once finished, you'll be able to manage your accounts with the above commands (and do so with no delays). - -## Failed to broadcast update transaction - -This can happen during a `migrate` for one of a few reasons: -* You do not have enough balance to pay the transaction fee (which is calculated dynamically). -* Your payment address has unconfirmed transactions. -* You can't connect to a Bitcoin node. - -To determine what's going on, you should try the command again by typing `BLOCKSTACK_DEBUG=1 blockstack ...` instead of `blockstack...`. diff --git a/_site/core/install-api.md b/_site/core/install-api.md deleted file mode 100644 index 932bc110c7..0000000000 --- a/_site/core/install-api.md +++ /dev/null @@ -1,80 +0,0 @@ -# Blockstack API - -Step-by-step instructions for deploying a Blockstack API node on Debian or -Ubuntu are below. - -- **Step 1:** Make sure you have Blockstack Core running locally (see [instructions](https://github.com/blockstack/blockstack-core/blob/master/README.md#quick-start)). - -- **Step 2:** Make sure you have [virtualenv installed](http://docs.python-guide.org/en/latest/dev/virtualenvs/). -Then, setup the API: -``` -$ sudo apt-get install -y python-pip memcached rng-tools python-dev libmemcached-dev zlib1g-dev libgmp-dev libffi-dev libssl-dev -$ sudo service memcached start -$ sudo pip install virtualenv -$ sudo npm -g install aglio -$ virtualenv api && source api/bin/activate -$ git clone https://github.com/blockstack/blockstack-core.git -$ cd blockstack-core/ -$ pip install . -$ pip install -r api/requirements.txt -$ blockstack setup_wallet -$ blockstack api start -$ deactivate -$ ./build_docs.sh public_api -``` - -### Search Subsystem - -If you want to enable the search subsystem in your installation, you can -follow the instructions [here](search.md). - -### Nginx Deployment - -For a production deployment we recommend using nginx and uwsgi: - -- **Step 1:** Install nginx and uWSGI: -``` -$ sudo apt-get install -y nginx -$ sudo pip install uwsgi -``` -- **Step 2:** Copy [this sample nginx sites file](../api/nginx/config/nginx_sites-available/blockstack_api) to - -> /etc/nginx/sites-available/blockstack_api - -and edit the paths depending on the uwsgi blockstack_api socket directory (defaults to /tmp/blockstack_api.sock) -You can test your nginx settings: -``` -$ sudo nginx -t -``` -- **Step 3:** Copy [this sample systemd service file](../api/nginx/config/systemd_system/blockstack_api.service) to - -> /etc/systemd/system/blockstack_api.service - -and edit the service user and blockstack paths depending on where your blockstack repo is located, and -where your virtualenv is located. - -Note: The following sed commands will work if the virtualenv is currently active and your shell is in the repo's root directory. - -``` -$ sudo sed -i "s/User\=USER/User\=$USER/" /etc/systemd/system/blockstack_api.service -$ sudo sed -i "s#/path/to/blockstack#$PWD#" /etc/systemd/system/blockstack_api.service -$ sudo sed -i "s#/path/to/virtualenv#$VIRTUAL_ENV#" /etc/systemd/system/blockstack_api.service -``` - -- **Step 4:** Get a security certificate from [Let's Encrypt](https://letsencrypt.org/). -``` -$ git clone https://github.com/certbot/certbot.git -$ cd certbot/ -$ ./certbot-auto --nginx -d -``` - -And copy the cert files to the path given in the nginx sites file earlier. - -- **Step 5:** Start nginx and the Blockstack API uwsgi server: -``` -sudo systemctl restart blockstack_api -sudo systemctl restart nginx -``` - -If you run into any issues, please [submit a Github issue](https://github.com/blockstack/blockstack-core/issues) and we'll update these -instructions. diff --git a/_site/core/interactive_regtest_macos.md b/_site/core/interactive_regtest_macos.md deleted file mode 100644 index cc4a535965..0000000000 --- a/_site/core/interactive_regtest_macos.md +++ /dev/null @@ -1,3 +0,0 @@ -Documentation for setting up the regtest mode for Blockstack Browser -using core's integration tests in macOS and Linux has -moved [here](../integration_tests). diff --git a/_site/core/memcached.md b/_site/core/memcached.md deleted file mode 100644 index aedd01e151..0000000000 --- a/_site/core/memcached.md +++ /dev/null @@ -1,42 +0,0 @@ -Installing Memcached -======= - -The Blockstack API optionally uses memcached and pylibmc for scaling read-only -calls. If you want to enable this functionality then you should have memcached -running locally. - -### Memcached on Debian & Ubuntu: - -``` -$ sudo apt-get install -y python-dev libmemcached-dev zlib1g-dev -$ pip install pylibmc -``` - -### Memcached on macOS: - -Easiest way to install memcached on macOS is by using [Homebrew](https://brew.sh/). - -After installing Homebrew: - -``` -$ brew install memcached -$ brew install libmemcached -$ pip install pylibmc --install-option="--with-libmemcached=/usr/local/Cellar/libmemcached/1.0.18_1/" -``` - -After installing, you can start memcached and check if it's running properly: - -``` -$ memcached -d -$ echo stats | nc localhost 11211 -``` - -### Memcached on Heroku - -To deploy on Heroku: - -```bash -$ heroku create -$ heroku addons:add memcachedcloud -$ git push heroku master -``` diff --git a/_site/core/naming/architecture.html b/_site/core/naming/architecture.html deleted file mode 100644 index 43f1dde5af..0000000000 --- a/_site/core/naming/architecture.html +++ /dev/null @@ -1,547 +0,0 @@ - - - - - - - - -Understand the Architecture | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      Understand the Architecture

      - - - - - -
      - -

      The BNS node is the heart of the system. It is responsible for building up -and replicating global name state.

      - -

      There are three parts to BNS that developers should be aware of. They are:

      - -
        -
      • -

        The BNS indexer. This module crawls the blockchain and builds -up its name database. BNS indexers do not contain any private or sensitive -state, and can be deployed publicly. We maintain a fleet of them at -https://node.blockstack.org:6263 for developers to use to get started.

        -
      • -
      • -

        The BNS API. This module gives -developers a stable RESTful API for interacting with the BNS network. -We provide one for developers to experiment with at https://core.blockstack.org.

        -
      • -
      • -

        BNS clients. These communicate with the BNS API module in order to -resolve names. Internally, they generate and send transactions to register -and modify names.

        -
      • -
      - -

      The BNS indexer and BNS API comprise the BNS node. An architectural schematic appears below.

      - -
                            +-------------------------------------------------------+
      -            RESTful   | +----------------+             +--------------------+ |
      -+--------+   API      | |                | private API |                    | |
      -| client |<------------>| BNS API module |<----------->| BNS indexer module | |
      -+--------+            | |                |             |                    | |
      -    |                 | +----------------+             | +----------------+ | |
      -    |                 |                                | | name database  | | |
      -    |                 |                                | +----------------+ | |
      -    |                 |                                +--------------------+ |
      -    |                 | BNS node                                 ^            |
      -    |                 +------------------------------------------|------------+
      -    |                                                            |
      -    |                                                            v
      -    |       blockchain transactions                    +--------------------+
      -    +------------------------------------------------->|   blockchain peer  |
      -                                                       +--------------------+
      -
      -Figure 1: BNS architecture overview.  Clients talk to the BNS API module to
      -resolve names, and generate and send blockchain transactions to register and
      -modify names.   The API module talks to the indexer module and gives clients
      -a stable, Web-accessible interface for resolving names.  The indexer module reads
      -the blochchain via a blockchain peer, over the blockchain's peer network.
      -
      -Blockstack Core currently implements the API module and indexer module as separate
      -daemons (`blockstack api` and `blockstack-core`, respectively).  However, this
      -is an implementation detail, and may change in the future.
      -
      -
      - -

      The BNS indexer implements the blockchain consensus rules and network protocols. -Its main responsibility is to build up and replicate all of the name state. It does -not have any public APIs of its own.

      - -

      The BNS API modules allows users and developers to resolve names via a RESTful -interface. Resolution can be done with vanilla curl or wget. -BNS applications should use the BNS API module for name resolution. -They should not attempt to talk to a BNS indexer directly, because its API is not stable and is not meant -for consumption by any other process except for the API module.

      - -

      Registering and managing names require generating and sending blockchain -transactions, which requires running a BNS client. We provide two reference -BNS clients:

      - -
        -
      • The Blockstack Browser gives users -and developers a graphical UI to resolve, register and manage names. This is the recommended -way to interact with BNS.
      • -
      • The Blockstack CLI gives developers low-level -control over resolving, registering, and managing names. -A new CLI that uses blockstack.js -is under development, and will replace the existing CLI program.
      • -
      - -

      We recommend that new developers use the Blockstack -Browser.

      - -

      Developers who want to make their own client programs that do not use -the reference client library code should read the -BNS transaction wire format document for generating and -sending their own transactions.

      - -

      The examples in this document focus on resolving names using curl. We refer -the reader to client-specific documentation for registering and managing names.

      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/naming/comparison.html b/_site/core/naming/comparison.html deleted file mode 100644 index b9ab7b06b6..0000000000 --- a/_site/core/naming/comparison.html +++ /dev/null @@ -1,596 +0,0 @@ - - - - - - - - -Naming system feature comparison | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      Naming system feature comparison

      - - - - - -
      - -

      BNS is not the only naming system in wide-spread use, nor is it the only -decentralized naming system that implements human-readable, -globally-unique, and strongly-owned names. The following feature table -describes how BNS differs from other naming systems

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      FeatureBNSENSDNSNamecoin
      Globally unique namesXXXX
      Human-readable namesXXXX
      Strongly-owned namesXX X
      Names are enumerableX  X
      Registration times1-2 hours~1 week~1 day1-2 hours
      Subdomain registration times1 hour (instant with #750)variesinstant~1 hour
      Anyone can make a TLD/namespaceX[1] [1]
      TLD/Namespace owners get registration feesX X 
      TLD/Namespace can be seeded with initial namesX X 
      Portable across blockchainsX N/A 
      Off-chain namesX N/A 
      Off-chain name stateXXN/A 
      Name provenanceXX X
      DID supportX   
      Turing-complete namespace rules XX 
      Miners are rewarded for participating[1] N/AX
      - -

      [1] Requires support in higher-level applications. These systems are not aware -of the existence of namespaces/TLDs at the protocol level.

      - -

      [2] Blockstack Core destroys the underlying blockchain token to pay for -registration fees when there is no pay-to-namespace-creator address set in the -name’s namespace. This has the effect of making the blockchain miners’ holdings -slightly more valuable.

      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/naming/did.html b/_site/core/naming/did.html deleted file mode 100644 index 0ebefd65f8..0000000000 --- a/_site/core/naming/did.html +++ /dev/null @@ -1,500 +0,0 @@ - - - - - - - - -DID Encoding for Subdomains | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      DID Encoding for Subdomains

      - - - - - -
      - -

      Every name and subdomain in BNS has a DID. The encoding is slightly different -for subdomains, so the software can determine which code-path to take.

      - -
        -
      • -

        For on-chain BNS names, the {address} is the same as the Bitcoin address -that owns the name. Currently, both version byte 0 and version byte 5 -addresses are supported (i.e. addresses starting with 1 or 3, meaning p2pkh and -p2sh addresses).

        -
      • -
      • -

        For off-chain BNS subdomains, the {address} has version byte 63 for -subdomains owned by a single private key, and version byte 50 for subdomains -owned by a m-of-n set of private keys. That is, subdomain DID addresses start -with S or M, respectively.

        -
      • -
      - -

      The {index} field for a subdomain’s DID is distinct from the {index} field -for a BNS name’s DID, even if the same created both names and subdomains. -For example, the name abcdefgh123456.id has the DID did:stack:v0:16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg-0, -because it was the first name created by 16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg. -However, 16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg also created jude.statism.id -as its first subdomain name. The DID for jude.statism.id is -did:stack:v0:SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i-0. Note that the address -SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i encodes the same public key hash as the address -16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg (the only difference between these two -strings is that the first is base58check-encoded with version byte 0, and the -second is encoded with version byte 63).

      - -

      You can see this play out in practice with the following code snippit:

      - -
      >>> import blockstack
      ->>> blockstack.lib.client.get_name_record('jude.statism.id', hostport='https://node.blockstack.org:6263')['address']
      -u'16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg'
      ->>> import virtualchain
      ->>> virtualchain.address_reencode('16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg', version_byte=63)
      -'SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i'
      ->>> blockstack.lib.client.resolve_DID('did:stack:v0:SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i-0', hostport='https://node.blockstack.org:6263')
      -{'public_key': '020fadbbcea0ff3b05f03195b41cd991d7a0af8bd38559943aec99cbdaf0b22cc8'}
      -
      -
      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/naming/forks.html b/_site/core/naming/forks.html deleted file mode 100644 index 27db1eff2f..0000000000 --- a/_site/core/naming/forks.html +++ /dev/null @@ -1,589 +0,0 @@ - - - - - - - - -BNS Forks | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      BNS Forks

      - - - - - -
      - -

      BNS effectively uses a public blockchain to store a database log. A BNS peer -bootstraps itself by downloading and replaying the database log from the -blockchain, and in doing so, will calculate the same name database state as -every other (honest) BNS peer that has the same view of the blockchain.

      - -

      Crucially, BNS is built on top of a public blockchain that is unaware of BNS’s existence. -This means that the blockchain peers do not validate BNS transactions. Instead, -the BNS peer needs to do so, and must know how to reject both invalid transactions -as well as well-formed transactions from dishonest peers (i.e. peers that do not -follow the same consensus rules).

      - -

      BNS nodes do not directly communicate with one another—by design, the set of -BNS peers is not enumerable. The only shared communication medium between BNS -peers is the blockchain.

      - -

      To identify and reject invalid and malicious transactions without the blockchain’s help, -the log of name operations embedded in the blockchain is constructed as a -fork*-consistent database -log. Fork*-consistency is a consistency -model whereby the state -replicas in all of the nodes exhibit the following properties:

      - -
        -
      • -

        Each correct peer maintains a history of well-formed, valid state operations. In this -case, each correct BNS node maintains a copy of the history blockchain transactions -that encoded well-formed, valid name operations.

        -
      • -
      • -

        Each honest peer’s history contains the sequence of all operations that it -sent. That is, a user’s BNS peer’s transaction log will contain the sequence of all valid -transactions that the user’s client wrote to the blockchain.

        -
      • -
      • -

        If two peers accept operations op and op’ from the same correct client, -then both of their logs will have the both operations in the same order. If -op’ was accepted before op, then both peers’ logs are identical up to op’. -In BNS, this means that if two peers both accept a given transaction, then it -means that they have accepted the same sequence of prior transactions.

        -
      • -
      - -

      This means that unlike with blockchains, -there can be multiple long-lived conflicting forks of the BNS database log. -However, due to fork*-consistency, a correct BNS peer will only process one -of these forks, and will ignore transactions from peers in other forks. In other words, -fork*-consistency partitions the set of BNS peers into different fork-sets, -where all peers in a fork-set process each other’s transactions, but the -completely ignore peers in other fork-sets.

      - -

      BNS nodes identify which fork set they belong to using a consensus hash. The -consensus hash is a cryptographic digest of a node’s operation -history. Each BNS peer calculates a new consensus hash each time it processes a -new block, and stores the history of consensus hashes for each block it -processed.

      - -

      Two honest BNS peers can quickly determine if they are in the same fork-set by querying -each other’s consensus hashes for a given block. If they match, then they are -in the same fork-set (assming no hash collisions).

      - -

      A BNS client executes a name operation on a specific fork-set by including a -recent consensus hash from that fork-set in the blockchain transaction. -At the same time, the BNS consensus rules state that a transaction can only be -accepted if it included a recent valid consensus hash. -This means that all BNS nodes in the client’s desired fork-set will accept -the transaction, and all other BNS nodes not in the fork-set will ignore it. -You can see where the consensus hash is included in blockchain transactions by reading -the transaction wire format document.

      - -

      Fork-set Selection

      - -

      The blockchain linearizes the history of transactions, which means that -in general, there exists a fork-set for each distinct set of BNS -consensus rules. For example, the Blockstack Core 2016 hard fork -and 2017 hard fork both introduced new consensus -rules, which means at the time of this writing there are three possible fork-sets: -the pre-2016 fork-set, the 2016-2017 fork-set, and the post-2017 fork-set. -The public BNS nodes are always running -in the fork-set with the latest consensus rules.

      - -

      BNS clients are incentivized to communicate with peers in the fork-set that has -the most use, since this fork-set’s name database will encode name/state -bindings that are the most widely-accepted and understood by users. -To identify this fork-set, a BNS client needs to learn one of -its recent consensus hashes. Once it has a recent consensus hash, it can -query an untrusted BNS node for a copy of -its name database, and use the consensus hash to verify that the name database -was used to generate it.

      - -

      How does a BNS node determine whether or not a consensus hash corresponds to the -most widely-used fork-set? There are two strategies:

      - -
        -
      • -

        Determine whether or not a characteristic transaction was accepted by the -widely-used fork-set. If a client knows that a specific transaction belongs to -the widely-used fork-set and not others, then they can use the consensus hash to -efficiently determine whether or not a given node belongs to this fork-set.

        -
      • -
      • -

        Determine how much “economic activity” exists in a fork-set by inspecting -the blockchain for burned cryptocurrency tokens. Namespace and name -registrations are structured in a way that sends cryptocurrency tokens to either -a well-known burn address, or to an easily-queried pay-to-namespace-creator -address.

        -
      • -
      - -

      Both strategies rely on the fact that the consensus hash is calculated as a -Merkle skip-list -over the BNS node’s accepted transactions. A client can use a consensus hash to -determine whether or not a transaction T was accepted by a node with O(log -n) time and space complexity. We call the protocol for resolving a consensus hash to a specific transaction -Simplified Name Verification (SNV). See our paper on the subject -for details of how SNV works under the hood.

      - -

      If the client has a consensus hash and knows of a characteristic transaction in the widely-used fork-set, -it can use SNV to determine whether or not a node belongs to the fork-set that accepted it.

      - -

      If the client knows about multiple conflicting consensus hashes, -they can still use SNV to determine which one corresponds -to the most-used fork-set. To do so, the client would use a -blockchain explorer to find the -list of transactions that burned cryptocurrency tokens. Each of these -transactions will be treated as potential characteristic transactions: -the client would first select the subset of transactions that are well-formed -BNS transactions, and then use SNV to determine which of them correspond to which -consensus hashes. The client chooses the consensus hash that corresponds -to the fork-set with the highest cumulative burn.

      - -

      Work is currently underway to automate this process.

      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/naming/introduction.html b/_site/core/naming/introduction.html deleted file mode 100644 index 70ed470cc1..0000000000 --- a/_site/core/naming/introduction.html +++ /dev/null @@ -1,1961 +0,0 @@ - - - - - - - - -Blockstack Naming Service (BNS) | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      Blockstack Naming Service (BNS)

      - - - - - -
      - -

      This document gives an overview of how the Blockstack Naming Service work. This -document explains the following concepts:

      - -
        -
      • Why a secure decentralized naming service is an important building -block in decentralized systems
      • -
      • How Blockstack applications can leverage BNS to solve real-world problems
      • -
      • How the Blockstack Naming Service is different from other offerings in this -space.
      • -
      - -

      The (Blockstack Core) -repository is the reference implementation of the Blockstack Naming Service.

      - -

      Introduction

      - -

      The Blockstack Naming Service (BNS) is a network system that binds names -to off-chain state without relying on any central points of control. -It does so by embedding a log of its control-plane messages within a public blockchain, like Bitcoin.

      - -

      Each BNS peer determines the state of each name by indexing these specially-crafted -transactions. In doing so, each peer independently calculates the same global -name state.

      - -

      Names in BNS have three properties:

      - -
        -
      • Names are globally unique. The protocol does not allow name collisions, and all -well-behaved nodes resolve a given name to the same state.
      • -
      • Names are human-meaningful. Each name is chosen by its creator.
      • -
      • Names are strongly-owned. Only the name’s owner can change the state it -resolves to. Specifically, a name is owned by one or more ECDSA private keys.
      • -
      - -

      Internally, a BNS node implements a replicated name database. Each BNS node keeps itself -synchronized to all of the other ones in the world, so queries on one BNS node -will be the same on other nodes. BNS nodes allow a name’s owner to bind -up to 40Kb of off-chain state to their name, which will be replicated to all -BNS nodes via the Atlas network.

      - -

      BNS nodes extract the name database log from an underlying blockchain (Blockstack -Core currently uses Bitcoin, and had used Namecoin in the past). -BNS uses the blockchain to establish a shared “ground truth” for the system: as long as -two nodes have the same view of the blockchain, then they will build up the same -database.

      - -

      The biggest consequence for developers is that in BNS, reading name state is -fast and cheap but writing name state is slow and expensive. This is because -registering and modifying names requires one or more transactions to be sent to -the underlying blockchain, and BNS nodes will not process them until they are -sufficiently confirmed. Users and developers need to acquire and spend -the requisite cryptocurrency (i.e. Bitcoin) to send BNS transactions.

      - -

      Motivation

      - -

      We rely on naming systems in everyday life, and they play a critical -role in many different applications. For example, when you look up a -friend on social media, you are using the platform’s naming service to resolve -their name to their profile. When you look up a website, you are using the -Domain Name Service to -resolve the hostname to its host’s IP address. When you check out a Git branch, you -are using your Git client to resolve the branch name to a commit hash. -When you look up someone’s PGP key on a keyserver, you are resolving -their key ID to their public key.

      - -

      What kinds of things do we want to be true about names? In BNS, names are -globally unique, names are human-meaningful, and names are strongly-owned. -However, if you look at these examples, you’ll see that each of them only -guarantees two of these properties. This limits how useful they can be.

      - -
        -
      • In DNS and social media, names are globally unique and human-readable, but not -strongly-owned. The system operator has the -final say as to what each names resolves to. -
          -
        • Problem: Clients must trust the system to make the right -choice in what a given name resolves to. This includes trusting that -no one but the system administrators can make these changes.
        • -
        -
      • -
      • In Git, branch names are human-meaningful -and strongly-owned, but not globally unique. Two different Git nodes may resolve the same -branch name to different unrelated repository states. -
          -
        • Problem: Since names can refer to conflicting state, developers -have to figure out some other mechanism to resolve ambiguities. In Git’s -case, the user has to manually intervene.
        • -
        -
      • -
      • In PGP, names are key IDs. They are -are globally unique and cryptographically owned, but not human-readable. PGP -key IDs are derived from the keys they reference. -
          -
        • Problem: These names are difficult for most users to -remember since they do not carry semantic information relating to their use in the system.
        • -
        -
      • -
      - -

      BNS names have all three properties, and none of these problems. This makes it a -powerful tool for building all kinds of network applications. With BNS, we -can do the following and more:

      - -
        -
      • Build domain name services where hostnames can’t be hijacked.
      • -
      • Build social media platforms where user names can’t be stolen by phishers.
      • -
      • Build version control systems where repository branches do not conflict.
      • -
      • Build public-key infrastructure where it’s easy for users to discover and -remember each other’s keys.
      • -
      - -

      How to Use BNS

      - -

      BNS names are organized into a global name hierarchy. There are three different -layers in this hierarchy related to naming:

      - -
        -
      • -

        Namespaces. These are the top-level names in the hierarchy. An analogy -to BNS namespaces are DNS top-level domains. Existing BNS namespaces include -.id, .podcast, and .helloworld. All other names belong to exactly one -namespace. Anyone can create a namespace, but in order for the namespace -to be persisted, it must be launched so that anyone can register names in it. -Namespaces are not owned by their creators.

        -
      • -
      • -

        BNS names. These are names whose records are stored directly on the -blockchain. The ownership and state of these names are controlled by sending -blockchain transactions. Example names include verified.podcast and -muneeb.id. Anyone can create a BNS name, as long as the namespace that -contains it exists already. The state for BNS names is usually stored in the Atlas -network.

        -
      • -
      • -

        BNS subdomains. These are names whose records are stored off-chain, -but are collectively anchored to the blockchain. The ownership and state for -these names lives within the Atlas network. While BNS -subdomains are owned by separate private keys, a BNS name owner must -broadcast their subdomain state. Example subdomains include jude.personal.id -and podsaveamerica.verified.podcast. Unlike BNS namespaces and names, the -state of BNS subdomains is not part of the blockchain consensus rules.

        -
      • -
      - -

      A feature comparison matrix summarizing the similarities and differences -between these name objects is presented below:

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      FeatureNamespacesBNS namesBNS Subdomains
      Globally uniqueXXX
      Human-meaningfulXXX
      Owned by a private key XX
      Anyone can createXX[1]
      Owner can update X[1]
      State hosted on-chainXX 
      State hosted off-chain XX
      Behavior controlled by consensus rulesXX 
      May have an expiration date X 
      - -

      [1] Requires the cooperation of a BNS name owner to broadcast its transactions

      - -

      Creating a Namespace

      - -

      There are four steps to creating a namespace:

      - -
        -
      1. -

        Send a NAMESPACE_PREORDER transaction (live example). -This is the first step. This registers the salted hash of the namespace with BNS nodes, and burns the -requisite amount of cryptocurrency. In addition, it proves to the -BNS nodes that user has honored the BNS consensus rules by including -a recent consensus hash in the transaction -(see the section on BNS forks for details).

        -
      2. -
      3. -

        Send a NAMESPACE_REVEAL transaction (live example). -This is the second step. This reveals the salt and the namespace ID (pairing it with its -NAMESPACE_PREORDER), it reveals how long names last in this namespace before -they expire or must be renewed, and it sets a price function for the namespace -that determines how cheap or expensive names its will be. The price function takes -a name in this namespace as input, and outputs the amount of cryptocurrency the -name will cost (i.e. by examining how long the name is, and whether or not it -has any vowels or non-alphabet characters). The namespace creator -has the option to collect name registration fees for the first year of the -namespace’s existence by setting a namespace creator address.

        -
      4. -
      5. -

        Seed the namespace with NAME_IMPORT transactions (live example). -Once the namespace has been revealed, the user has the option to populate it with a set of -names. Each imported name is given both an owner and some off-chain state. -This step is optional—namespace creators are not required to import names.

        -
      6. -
      7. -

        Send a NAMESPACE_READY transaction (live example). -This is the final step of the process. It launches the namespace, which makes it available to the -public. Once a namespace is ready, anyone can register a name in it if they -pay the appropriate amount of cryptocurrency (according to the price funtion -revealed in step 2).

        -
      8. -
      - -

      The reason for the NAMESPACE_PREORDER/NAMESPACE_REVEAL pairing is to prevent -frontrunning. The BNS consensus rules require a NAMESPACE_REVEAL to be -paired with a previous NAMESPACE_PREORDER sent within the past 24 hours. -If it did not do this, then a malicious actor could watch the blockchain network -and race a victim to claim a namespace.

      - -

      Namespaces are created on a first-come first-serve basis. If two people try to -create the same namespace, the one that successfully confirms both the -NAMESPACE_PREORDER and NAMESPACE_REVEAL wins. The fee burned in the -NAMESPACE_PREORDER is spent either way.

      - -

      Once the user issues the NAMESPACE_PREORDER and NAMESPACE_REVEAL, they have -1 year before they must send the NAMESPACE_READY transaction. If they do not -do this, then the namespace they created disappears (along with all the names -they imported).

      - -

      Developers wanting to create their own namespaces should read the namespace -creation document. It is highly recommended that -developers follow this tutorial closely, given the large amount of -cryptocurrency at stake.

      - -

      Using Namespaces

      - -

      The intention is that each application can create its own BNS -namespace for its own purposes. Applications can use namespaces for things like:

      - -
        -
      • Giving users a SSO system, where each user registers their public key under a -username. Blockstack applications do this with names in the .id namespace, -for example.
      • -
      • Providing a subscription service, where each name is a 3rd party that provides -a service for users to subscribe to. For example, names in -.podcast point to podcasts that users of the -DotPodcast app can subscribe to.
      • -
      • Implementing software licenses, where each name corresponds to an access key. -Unlike conventional access keys, access keys implemented as names -can be sold and traded independently. The licensing fee (paid as a name -registration) would be set by the developer and sent to a developer-controlled -blockchain address.
      • -
      - -

      Names within a namespace can serve any purpose the developer wants. The ability -to collect registration fees for 1 year after creating the namespace not only -gives developers the incentive to get users to participate in the app, but also -gives them a way to measure economic activity.

      - -

      Developers can query individual namespaces and look up names within them using -the BNS API. The API offers routes to do the following:

      - -

      List all namespaces in existence (reference).

      - -
      $ curl https://core.blockstack.org/v1/namespaces
      -[
      -  "id",
      -  "helloworld",
      -  "podcast"
      -]
      -
      -
      - -

      List all names within a namespace (reference)

      - -
      $ curl https://core.blockstack.org/v1/namespaces/id/names?page=0
      -[
      -  "0.id",
      -  "0000.id",
      -  "000000.id",
      -  "000001.id",
      -  "00000111111.id",
      -  "000002.id",
      -  "000007.id",
      -  "0011sro.id",
      -  "007_007.id",
      -  "00n3w5.id",
      -  "00r4zr.id",
      -  "00w1k1.id",
      -  "0101010.id",
      -  "01jack.id",
      -  "06nenglish.id",
      -  "08.id",
      -  "0cool_f.id",
      -  "0dadj1an.id",
      -  "0nelove.id",
      -  "0nename.id"
      -...
      -]
      -
      -
      - -

      Each page returns a batch of 100 names.

      - -

      Get the Cost to Register a Namespace (reference)

      - -
      $ curl https://core.blockstack.org/v1/prices/namespaces/test
      -{
      -  "satoshis": 40000000
      -}
      -
      -
      - -

      If you want to register a namespace, please see the namespace creation tutorial.

      - -

      Getting the Current Consensus Hash (reference)

      - -
      $ curl -sL https://core.blockstack.org/v1/blockchains/bitcoin/consensus
      -{
      -  "consensus_hash": "98adf31989bd937576aa190cc9f5fa3a"
      -}
      -
      -
      - -

      A recent consensus hash is required to create a NAMESPACE_PREORDER transaction. The reference -BNS clients do this automatically. See the transaction format -document for details on how the consensus hash is used to construct the -transaction.

      - -

      Resolving BNS Names

      - -

      BNS names are bound to both public keys and to about 40Kb of off-chain state. -The off-chain state is encoded as a DNS zone file, -which contains routing information for discovering the user’s Blockstack data -(such as their profile and app data, which are hosted in the Gaia storage -system).

      - -

      The blockchain is not used to store this information directly. Instead, the -blockchain stores the public key hash and the zone file hash. When -indexing the blockchain, each BNS node builds a database with -three columns: all the on-chain BNS names that have been registered, each -name’s public key hash, and each name’s zone file’s hash. -In addition, each BNS node maintains the transaction history of each name. -A developer can resolve a name to any configuration it was in at any prior -point in time.

      - -

      Below is an example name table pulled from a live BNS node:

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      NamePublic key hashZone File Hash
      ryan.id15BcxePn59Y6mYD2fRLCLCaaHScefqW2Noa455954b3e38685e487efa41480beeb315f4ec65
      muneeb.id1J3PUxY5uDShUnHRrMyU6yKtoHEUPhKULs37aecf837c6ae9bdc9dbd98a268f263dacd00361
      jude.id16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJgb6e99200125e70d634b17fe61ce55b09881bfafd
      verified.podcast1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH6701ce856620d4f2f57cd23b166089759ef6eabd
      cicero.res_publica.id1EtE77Aa5AA8etzF2irk56vvkS4v7rZ7PE7e4ac75f9d79ba9d5d284fac19617497433b832d
      podsaveamerica.verified.podcast1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH0d6f090db8945aa0e60759f9c866b17645893a95
      - -

      In practice, the zone file hash is the RIPEMD160 hash of the SHA256 hash of -the zone file, and the public key is the base58check-encoded RIPEMD160 hash -of the double-SHA256 hash of the ECDSA public key (i.e. a Bitcoin address).

      - -

      The BNS consensus rules ensure that -a BNS name can only be registered if it is not already taken, and that only the -user who owns the name’s private key can change its public key hash or zone file -hash. This means that a name’s public key and zone file can be stored anywhere, -since they can be authenticated using the hashes discovered by indexing the -blockchain under the BNS consensus rules.

      - -

      BNS nodes implement a decentralized storage system for zone files called the -Atlas network. In this system, BNS nodes eagerly replicate -all the zone files they know about to one another, so that eventually every BNS -node has a full replica of all zone files.

      - -

      The public keys for names are stored off-chain in Gaia. -The user controls where their public keys are hosted using the zone file -contents (if they are hosted online anywhere at all).

      - -

      Developers can query this table via the BNS API. The API offers routes -to do the following:

      - -

      Look up a name’s public key and zone file (reference)

      - -
      $ curl https://core.blockstack.org/v1/names/muneeb.id
      -{
      -  "address": "1J3PUxY5uDShUnHRrMyU6yKtoHEUPhKULs",
      -  "blockchain": "bitcoin",
      -  "expire_block": 599266,
      -  "last_txid": "7e16e8688ca0413a398bbaf16ad4b10d3c9439555fc140f58e5ab4e50793c476",
      -  "status": "registered",
      -  "zonefile": "$ORIGIN muneeb.id\n$TTL 3600\n_http._tcp URI 10 1 \"https://gaia.blockstack.org/hub/1J3PUxY5uDShUnHRrMyU6yKtoHEUPhKULs/0/profile.json\"\n",
      -  "zonefile_hash": "37aecf837c6ae9bdc9dbd98a268f263dacd00361"
      -}
      -
      -
      - -

      Note that the zonefile field is given with the off-chain data that hashes -to the zonefile_hash field.

      - -

      List all names the node knows about (reference)

      - -
      $ curl https://core.blockstack.org/v1/names?page=0
      -[
      -  "judecn.id",
      -  "3.id",
      -  "4.id",
      -  "8.id",
      -  "e.id",
      -  "h.id",
      -  "5.id",
      -  "9.id",
      -  "i.id",
      -  "l.id",
      -  "p.id",
      -  "w.id",
      -  "ba.id",
      -  "df.id",
      -...
      -]
      -
      -
      - -

      Each page returns 100 names. While no specific ordering is mandated by the -protocol, the reference implementation orders names by their order of creation -in the blockchain.

      - -

      Look up the history of states a name was in (reference)

      - -
      $ curl https://core.blockstack.org/v1/names/patrickstanley.id/history
      -{
      -  "445838": [
      -    {
      -      "address": "1occgbip7tFDXX9MvzQhcnTUUjcVX2dYK",
      -      "block_number": 445838,
      -      "burn_address": "1111111111111111111114oLvT2",
      -      "consensus_hash": "7b696b6f4060b792d41912068944d73b",
      -      "op": "?",
      -      "op_fee": 25000,
      -      "opcode": "NAME_PREORDER",
      -      "preorder_hash": "26bf7874706ac761afdd403ed6b3b9578fb01a34",
      -      "sender": "76a91408d0dd44c1f0a3a4f0957ae95901929d7d66d55788ac",
      -      "sender_pubkey": "039a8948d339ecbff44cf426cb85d90fce876f1658d385cdc47f007f279be626ea",
      -      "txid": "6730ae09574d5935ffabe3dd63a9341ea54fafae62fde36c27738e9ee9c4e889",
      -      "vtxindex": 40
      -    }
      -  ],
      -  "445851": [
      -    {
      -      "address": "17CbHgTgBG3kLedXNneEKBkCTgW2fyrnUD",
      -      "block_number": 445838,
      -      "consensus_hash": null,
      -      "first_registered": 445851,
      -      "importer": null,
      -      "importer_address": null,
      -      "last_creation_op": "?",
      -      "last_renewed": 445851,
      -      "name": "patrickstanley.id",
      -      "name_hash128": "683a3e1ee5f0296833c56e481cf41b77",
      -      "namespace_block_number": 373601,
      -      "namespace_id": "id",
      -      "op": ":",
      -      "op_fee": 25000,
      -      "opcode": "NAME_REGISTRATION",
      -      "preorder_block_number": 445838,
      -      "preorder_hash": "26bf7874706ac761afdd403ed6b3b9578fb01a34",
      -      "revoked": false,
      -      "sender": "76a9144401f3be5311585ea519c1cb471a8dc7b02fd6ee88ac",
      -      "sender_pubkey": "039a8948d339ecbff44cf426cb85d90fce876f1658d385cdc47f007f279be626ea",
      -      "transfer_send_block_id": null,
      -      "txid": "55b8b42fc3e3d23cbc0f07d38edae6a451dfc512b770fd7903725f9e465b2925",
      -      "value_hash": null,
      -      "vtxindex": 54
      -    }
      -  ],
      -  "445873": [
      -    {
      -      "address": "17CbHgTgBG3kLedXNneEKBkCTgW2fyrnUD",
      -      "block_number": 445838,
      -      "consensus_hash": "18b8d69f0182b89ccb1aa536f83be18a",
      -      "first_registered": 445851,
      -      "importer": null,
      -      "importer_address": null,
      -      "last_creation_op": "?",
      -      "last_renewed": 445851,
      -      "name": "patrickstanley.id",
      -      "name_hash128": "683a3e1ee5f0296833c56e481cf41b77",
      -      "namespace_block_number": 373601,
      -      "namespace_id": "id",
      -      "op": "+",
      -      "op_fee": 25000,
      -      "opcode": "NAME_UPDATE",
      -      "preorder_block_number": 445838,
      -      "preorder_hash": "26bf7874706ac761afdd403ed6b3b9578fb01a34",
      -      "revoked": false,
      -      "sender": "76a9144401f3be5311585ea519c1cb471a8dc7b02fd6ee88ac",
      -      "sender_pubkey": "039a8948d339ecbff44cf426cb85d90fce876f1658d385cdc47f007f279be626ea",
      -      "transfer_send_block_id": null,
      -      "txid": "dc478659fc684a1a6e1e09901971e82de11f4dfe2b32a656700bf9a3b6030719",
      -      "value_hash": "02af0ef21161ad06b0923106f40b994b9e4c1614",
      -      "vtxindex": 95
      -    }
      -  ],
      -  "445884": [
      -    {
      -      "address": "1GZqrVbamkaE6YNveJFWK6cDrCy6bXyS6b",
      -      "block_number": 445838,
      -      "consensus_hash": "18b8d69f0182b89ccb1aa536f83be18a",
      -      "first_registered": 445851,
      -      "importer": null,
      -      "importer_address": null,
      -      "last_creation_op": "?",
      -      "last_renewed": 445851,
      -      "name": "patrickstanley.id",
      -      "name_hash128": "683a3e1ee5f0296833c56e481cf41b77",
      -      "namespace_block_number": 373601,
      -      "namespace_id": "id",
      -      "op": ">>",
      -      "op_fee": 25000,
      -      "opcode": "NAME_TRANSFER",
      -      "preorder_block_number": 445838,
      -      "preorder_hash": "26bf7874706ac761afdd403ed6b3b9578fb01a34",
      -      "revoked": false,
      -      "sender": "76a914aabffa6dd90d731d3a349f009323bb312483c15088ac",
      -      "sender_pubkey": null,
      -      "transfer_send_block_id": 445875,
      -      "txid": "7a0a3bb7d39b89c3638abc369c85b5c028d0a55d7804ba1953ff19b0125f3c24",
      -      "value_hash": "02af0ef21161ad06b0923106f40b994b9e4c1614",
      -      "vtxindex": 16
      -    }
      -  ]
      -}
      -
      -
      - -

      All of the above information is extracted from the blockchain. Each top-level -field encodes the states the name transitioned to at the given block height (e.g. -445838, 445851, 445873, adn 445884). At each block height, the name’s zone file -hashes are returned in the order they were discovered in the blockchain.

      - -

      Each name state contains a lot of ancillary data that is used internally by -other API calls and client libraries. The relevant fields for this document’s -scope are:

      - -
        -
      • address: This is the base58check-encoded public key hash.
      • -
      • name: This is the name queried.
      • -
      • value_hash: This is the zone file hash.
      • -
      • opcode: This is the type of transaction that was processed.
      • -
      • txid: This is the transaction ID in the underlying blockchain.
      • -
      - -

      The name’s entire history is returned. This includes the history of the name -under its previous owner, if the name expired and was reregistered.

      - -

      Look up the list of names owned by a given public key hash (reference)

      - -
      $ curl https://core.blockstack.org/v1/addresses/bitcoin/16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg
      -{
      -  "names": [
      -    "judecn.id",
      -    "patrickstanley1.id",
      -    "abcdefgh123456.id",
      -    "duckduckgo_tor.id",
      -    "jude.id",
      -    "blockstacknewyear2017.id",
      -    "jude.statism.id"
      -  ]
      -}
      -
      -
      - -

      Note that this API endpoint includes names and -subdomains.

      - -

      Registering BNS Names

      - -

      Registering a BNS name costs cryptocurrency. This cost comes from two sources:

      - -
        -
      • -

        Transaction fees: These are the fees imposed by the cost of storing the -transaction data to the blockchain itself. They are independent of BNS, since -all of the blockchain’s users are competing to have their transactions included -in the next block. The blockchain’s miners receive the transaction fee.

        -
      • -
      • -

        Registration fees: Each BNS namespace imposes an additional fee on how -much a name costs. The registration fee is sent to the namespace creator -during the first year that a namespace exists, and is sent to a burn address -afterwards. The registration fee is different for each name and is -determined by the namespace itself, but can be queried in advance by the user.

        -
      • -
      - -

      Registering a name takes two transactions. They are:

      - -
        -
      • -

        NAME_PREORDER transaction: This is the first transaction to be sent. -It tells all BNS nodes the salted hash of the BNS name, and it pays the -registration fee to the namespace owner’s designated address (or the burn -address). In addition, it proves to the BNS nodes that the client knows about -the current state of the system by including a recent consensus hash -in the transaction (see the section on BNS forks for details).

        -
      • -
      • -

        NAME_REGISTRATION transaction: This is the second transaction to be -sent. It reveals the salt and the name to all BNS nodes, and assigns the name -an initial public key hash and zone file hash

        -
      • -
      - -

      The reason this process takes two transactions is to prevent front-running. -The BNS consensus rules stipulate that a name can only be registered if its -matching preorder transaction was sent in the last 24 hours. Because a name -must be preordered before it is registered, someone watching the blockchain’s -peer network cannot race a victim to claim the name they were trying to -register (i.e. the attacker would have needed to send a NAME_PREORDER -transaction first, and would have had to have sent it no more than 24 hours -ago).

      - -

      Registering a name on top of the Bitcoin blockchain takes 1-2 hours. This is -because you need to wait for the NAME_PREORDER transaction to be sufficiently -confirmed before sending the NAME_REGISTRATION transaction. The BNS nodes -only register the name once both transactions have at least 6 confirmations -(which itself usually takes about an hour).

      - -

      Names are registered on a first-come first-serve basis. -If two different people try to register the same name at the same time, the -person who completes the two-step process earliest will receive the name. The -other person’s NAME_REGISTRATION transaction will be ignored, since it will -not be considered valid at this point. The registration fee paid by the -NAME_PREORDER will be lost. However, this situation is rare in practice— -as of early 2018, we only know of one confirmed instance in the system’s 3+ years -of operation.

      - -

      Fully-qualified names can be between 3 and 37 characters long, and consist of -the characters a-z, 0-9, +, -, _, and .. This is to prevent -homograph attacks. -NAME_REGISTRATION transactions that do not conform to this requirement will be -ignored.

      - -

      Getting a Name’s Registration Fee (reference)

      - -
      $ curl -sL https://core.blockstack.org/v1/prices/names/helloworld.id | jq -r ".name_price"
      -{
      -  "btc": 2.5e-05,
      -  "satoshis": 2500
      -}
      -
      -
      - -

      Note the use of jq -r to select the "name_price" field. This API -endpoint may return other ancilliary data regarding transaction fee estimation, -but this is the only field guaranteed by this specification to be present.

      - -

      Getting the Current Consensus Hash (reference)

      - -
      $ curl -sL https://core.blockstack.org/v1/blockchains/bitcoin/consensus
      -{
      -  "consensus_hash": "98adf31989bd937576aa190cc9f5fa3a"
      -}
      -
      -
      - -

      The consensus hash must be included in the NAME_PREORDER transaction. The BNS -clients do this automatically. See the transaction format -document for details as to how to include this in the -transaction.

      - -

      Registering a Name

      - -

      Registration happens through a BNS client, such as the Blockstack -Browser or -blockstack.js. -The reference BNS clients manage a local Bitcoin wallet, calculate transaction fees -dynamically and automatically, and broadcast both the NAME_PREORDER and -NAME_REGISTRATION transactions at the right times.

      - -

      If you want to make your own registration client, you should see the -transaction format document.

      - -

      Managing BNS Names

      - -

      Once you register a BNS name, you have the power to change its zone file hash, -change its public key hash, destroy it (i.e. render it unresolvable), -or renew it. The BNS consensus rules ensure that only you, as the owner of -the name’s private key, have the ability to carry out these operations.

      - -

      Each of these operations are executed by sending a specially-formatted -blockchain transaction to the blockchain, which BNS nodes read and process. -The operations are listed below:

      - - - - - - - - - - - - - - - - - - - - - - - - - - -
      Transaction TypeDescription
      NAME_UPDATEThis changes the name’s zone file hash. Any 20-byte string is allowed.
      NAME_TRANSFERThis changes the name’s public key hash. In addition, the current owner has the option to atomically clear the name’s zone file hash (so the new owner won’t “receive” the zone file).
      NAME_REVOKEThis renders a name unresolvable. You should do this if your private key is compromised.
      NAME_RENEWALThis pushes back the name’s expiration date (if it has one), and optionally both sets a new zone file hash and a new public key hash.
      - -

      The reference BNS clients— -blockstack.js and the Blockstack -Browser—can handle creating -and sending all of these transactions for you.

      - -

      NAME_UPDATE (live example)

      - -

      A NAME_UPDATE transaction changes the name’s zone file hash. You would send -one of these transactions if you wanted to change the name’s zone file contents. -For example, you would do this if you want to deploy your own Gaia -hub and want other people to read from it.

      - -

      A NAME_UPDATE transaction is generated from the name, a recent consensus -hash, and the new zone file hash. The reference clients gather -this information automatically. See the transaction format -document for details on how to construct this transaction.

      - -

      NAME_TRANSFER (live example)

      - -

      A NAME_TRANSFER transaction changes the name’s public key hash. You would -send one of these transactions if you wanted to:

      - -
        -
      • Change your private key
      • -
      • Send the name to someone else
      • -
      - -

      When transferring a name, you have the option to also clear the name’s zone -file hash (i.e. set it to null). -This is useful for when you send the name to someone else, so the -recipient’s name does not resolve to your zone file.

      - -

      The NAME_TRANSFER transaction is generated from the name, a recent consensus -hash, and the new public key hash. The reference clients gather -this information automatically. See the transaction format -document for details on how to construct this transaction.

      - -

      NAME_REVOKE (live example)

      - -

      A NAME_REVOKE transaction makes a name unresolvable. The BNS consensus rules -stipulate that once a name is revoked, no one can change its public key hash or -its zone file hash. The name’s zone file hash is set to null to prevent it -from resolving.

      - -

      You should only do this if your private key is compromised, or if you want to -render your name unusable for whatever reason. It is rarely used in practice.

      - -

      The NAME_REVOKE operation is generated using only the name. See the -transaction format document for details on how to construct -it.

      - -

      NAME_RENEWAL (live example)

      - -

      Depending in the namespace rules, a name can expire. For example, names in the -.id namespace expire after 2 years. You need to send a NAME_RENEWAL every -so often to keep your name.

      - -

      A NAME_RENEWAL costs both transaction fees and registration fees. You will -pay the registration cost of your name to the namespace’s designated burn address when you -renew it. You can find this fee using the /v1/prices/names/{name} endpoint.

      - -

      When a name expires, it enters a month-long “grace period” (5000 blocks). It -will stop resolving in the grace period, and all of the above operations will -cease to be honored by the BNS consensus rules. You may, however, send a -NAME_RENEWAL during this grace period to preserve your name.

      - -

      If your name is in a namespace where names do not expire, then you never need to -use this transaction.

      - -

      When you send a NAME_RENEWAL, you have the option of also setting a new public -key hash and a new zone file hash. See the transaction format -document for details on how to construct this transaction.

      - -

      BNS Subdomains

      - -

      BNS names are strongly-owned because the owner of its private key can generate -valid transactions that update its zone file hash and owner. However, this comes at the -cost of requiring a name owner to pay for the underlying transaction in the -blockchain. Moreover, this approach limits the rate of BNS name registrations -and operations to the underlying blockchain’s transaction bandwidth.

      - -

      BNS overcomes this with subdomains. A BNS subdomain is a type of BNS name whose state -and owner are stored outside of the blockchain, but whose existence and -operation history are anchored to the -blockchain. In the example table in the Resolving BNS -Names section, the names cicero.res_publica.id and -podsaveamerica.verified.podcast are subdomains.

      - -

      Like their on-chain counterparts, subdomains are globally -unique, strongly-owned, and human-readable. BNS gives them their own name state -and public keys.

      - -

      Unlike on-chain names, subdomains can be created and managed -cheaply, because they are broadcast to the -BNS network in batches. A single blockchain transaction can send up to 120 -subdomain operations.

      - -

      This is achieved by storing subdomain records in the Atlas Network. -An on-chain name owner broadcasts subdomain operations by encoding them as -TXT records within a DNS zone file. To broadcast the zone file, -the name owner sets the new zone file hash with a NAME_UPDATE transaction and -replicates the zone file via Atlas. This, in turn, replicates all subdomain -operations it contains, and anchors the set of subdomain operations to -an on-chain transaction. The BNS node’s consensus rules ensure that only -valid subdomain operations from valid NAME_UPDATE transactions will ever be -stored.

      - -

      For example, the name verified.podcast once wrote the zone file hash 247121450ca0e9af45e85a82e61cd525cd7ba023, -which is the hash of the following zone file:

      - -
      $ curl -sL https://core.blockstack.org/v1/names/verified.podcast/zonefile/247121450ca0e9af45e85a82e61cd525cd7ba023 | jq -r '.zonefile'
      -$ORIGIN verified.podcast
      -$TTL 3600
      -1yeardaily TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxeWVhcmRhaWx5CiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMXllYXJkYWlseS9oZWFkLmpzb24iCg=="
      -2dopequeens TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAyZG9wZXF1ZWVucwokVFRMIDM2MDAKX2h0dHAuX3RjcCBVUkkgMTAgMSAiaHR0cHM6Ly9waC5kb3Rwb2RjYXN0LmNvLzJkb3BlcXVlZW5zL2hlYWQuanNvbiIK"
      -10happier TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxMGhhcHBpZXIKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8xMGhhcHBpZXIvaGVhZC5qc29uIgo="
      -31thoughts TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzMXRob3VnaHRzCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzF0aG91Z2h0cy9oZWFkLmpzb24iCg=="
      -359 TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzNTkKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8zNTkvaGVhZC5qc29uIgo="
      -30for30 TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzMGZvcjMwCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzBmb3IzMC9oZWFkLmpzb24iCg=="
      -onea TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiBvbmVhCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vb25lYS9oZWFkLmpzb24iCg=="
      -10minuteteacher TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxMG1pbnV0ZXRlYWNoZXIKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8xMG1pbnV0ZXRlYWNoZXIvaGVhZC5qc29uIgo="
      -36questionsthepodcastmusical TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzNnF1ZXN0aW9uc3RoZXBvZGNhc3RtdXNpY2FsCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzZxdWVzdGlvbnN0aGVwb2RjYXN0bXVzaWNhbC9oZWFkLmpzb24iCg=="
      -_http._tcp URI 10 1 "https://dotpodcast.co/"
      -
      -
      - -

      Each TXT record in this zone file encodes a subdomain-creation. -For example, 1yeardaily.verified.podcast resolves to:

      - -
      $ curl https://core.blockstack.org/v1/names/1yeardaily.verified.podcast
      -{
      -  "address": "1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH",
      -  "blockchain": "bitcoin",
      -  "last_txid": "d87a22ebab3455b7399bfef8a41791935f94bc97aee55967edd5a87f22cce339",
      -  "status": "registered_subdomain",
      -  "zonefile_hash": "e7acc97fd42c48ed94fd4d41f674eddbee5557e3",
      -  "zonefile_txt": "$ORIGIN 1yeardaily\n$TTL 3600\n_http._tcp URI 10 1 \"https://ph.dotpodcast.co/1yeardaily/head.json\"\n"
      -}
      -
      -
      - -

      This information was extracted from the 1yeardaily TXT resource record in the zone -file for verified.podcast.

      - -

      Subdomain Lifecycle

      - -

      Note that 1yeardaily.verified.podcast has a different public key -hash (address) than verified.podcast. A BNS node will only process a -subsequent subdomain operation on 1yeardaily.verified.podcast if it includes a -signature from this address’s private key. verified.podcast cannot generate -updates; only the owner of 1yeardaily.verified.podcast can do so.

      - -

      The lifecycle of a subdomain and its operations is shown in Figure 2.

      - -
         subdomain                  subdomain                  subdomain
      -   creation                   update                     transfer
      -+----------------+         +----------------+         +----------------+
      -| cicero         |         | cicero         |         | cicero         |
      -| owner="1Et..." | signed  | owner="1Et..." | signed  | owner="1cJ..." |
      -| zf0="7e4..."   |<--------| zf0="111..."   |<--------| zf0="111..."   |<---- ...
      -| seqn=0         |         | seqn=1         |         | seqn=2         |
      -|                |         | sig="xxxx"     |         | sig="xxxx"     |
      -+----------------+         +----------------+         +----------------+
      -        |                          |                          |
      -        |        off-chain         |                          |
      -~ ~ ~ ~ | ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~|~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | ~ ~ ~ ~ ~ ~ ~ ...
      -        |         on-chain         |                          |
      -        V                          V (zone file hash    )     V
      -+----------------+         +----------------+         +----------------+
      -| res_publica.id |         |    jude.id     |         | res_publica.id |
      -|  NAME_UPDATE   |<--------|  NAME_UPDATE   |<--------|  NAME_UPDATE   |<---- ...
      -+----------------+         +----------------+         +----------------+
      -   blockchain                 blockchain                 blockchain
      -   block                      block                      block
      -
      -
      -Figure 2:  Subdomain lifetime with respect to on-chain name operations.  A new
      -subdomain operation will only be accepted if it has a later "sequence=" number,
      -and a valid signature in "sig=" over the transaction body.  The "sig=" field
      -includes both the public key and signature, and the public key must hash to
      -the previous subdomain operation's "addr=" field.
      -
      -Thesubdomain-creation and subdomain-transfer transactions for
      -"cicero.res_publica.id" are broadcast by the owner of "res_publica.id".
      -However, any on-chain name ("jude.id" in this case) can broadcast a subdomain
      -update for "cicero.res_publica.id".
      -
      -
      - -

      Subdomain operations are ordered by sequence number, starting at 0. Each new -subdomain operation must include:

      - -
        -
      • The next sequence number
      • -
      • The public key that hashes to the previous subdomain transaction’s address
      • -
      • A signature from the corresponding private key over the entire subdomain -operation.
      • -
      - -

      If two correctly-signed but conflicting subdomain operations are discovered -(i.e. they have the same sequence number), the one that occurs earlier in the -blockchain’s history is accepted. Invalid subdomain operations are ignored.

      - -

      Combined, this ensures that a BNS node with all of the zone files with a given -subdomain’s operations will be able to determine the valid sequence of -state-transitions it has undergone, and determine the current zone file and public -key hash for the subdomain.

      - -

      Resolving Subdomains

      - -

      Developers interact with subdomains the same way they interact with names. -Using the BNS API, a developer can:

      - -

      Look up a subdomain’s public key and zone file (reference)

      - -
      $ curl https://core.blockstack.org/v1/names/aaron.personal.id
      -{
      -  "address": "1PwztPFd1s2STMv4Ntq6UPBdYgHSBr5pdF",
      -  "blockchain": "bitcoin",
      -  "last_txid": "85e8273b0a38d3e9f0af7b4b72faf0907de9f4616afc101caac13e7bbc832394",
      -  "status": "registered_subdomain",
      -  "zonefile_hash": "a6dda6b74ffecf85f4a162627d8df59577243813",
      -  "zonefile_txt": "$ORIGIN aaron.personal.id\n$TTL 3600\n_https._tcp URI 10 1 \"https://gaia.blockstack.org/hub/1PwztPFd1s2STMv4Ntq6UPBdYgHSBr5pdF/profile.json\"\n"
      -}
      -
      -
      - -

      Look up a subdomain’s transaction history (reference)

      - -
      $ curl https://core.blockstack.org/v1/names/aaron.personal.id/history
      -{
      -  "509981": [
      -    {
      -      "address": "1PwztPFd1s2STMv4Ntq6UPBdYgHSBr5pdF",
      -      "block_number": 509981,
      -      "domain": "personal.id",
      -      "name": "aaron.personal.id",
      -      "sequence": 0,
      -      "txid": "85e8273b0a38d3e9f0af7b4b72faf0907de9f4616afc101caac13e7bbc832394",
      -      "value_hash": "a6dda6b74ffecf85f4a162627d8df59577243813",
      -      "zonefile": "JE9SSUdJTiBhYXJvbi5wZXJzb25hbC5pZAokVFRMIDM2MDAKX2h0dHBzLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vZ2FpYS5ibG9ja3N0YWNrLm9yZy9odWIvMVB3enRQRmQxczJTVE12NE50cTZVUEJkWWdIU0JyNXBkRi9wcm9maWxlLmpzb24iCg=="
      -    }
      -  ]
      -}
      -
      -
      - -

      Look up the list of names and subdomains owned by a given public key hash (reference)

      - -
      $ curl https://core.blockstack.org/v1/addresses/bitcoin/1PwztPFd1s2STMv4Ntq6UPBdYgHSBr5pdF
      -{
      -  "names": [
      -    "aaron.personal.id"
      -  ]
      -}
      -
      -
      - -

      Subdomain Creation and Management

      - -

      Unlike an on-chain name, a subdomain owner needs an on-chain name owner’s help -to broadcast their subdomain operations. In particular:

      -
        -
      • A subdomain-creation transaction can only be processed by the owner of the on-chain -name that shares its suffix. For example, only the owner of res_publica.id -can broadcast subdomain-creation transactions for subdomain names ending in -.res_publica.id.
      • -
      • A subdomain-transfer transaction can only be broadcast by the owner of the -on-chain name that created it. For example, the owner of -cicero.res_publica.id needs the owner of res_publica.id to broadcast a -subdomain-transfer transaction to change cicero.res_publica.id’s public key.
      • -
      • In order to send a subdomain-creation or subdomain-transfer, all -of an on-chain name owner’s zone files must be present in the Atlas network. -This lets the BNS node prove the absence of any conflicting subdomain-creation and -subdomain-transfer operations when processing new zone files.
      • -
      • A subdomain update transaction can be broadcast by any on-chain name owner, -but the subdomain owner needs to find one who will cooperate. For example, -the owner of verified.podcast can broadcast a subdomain-update transaction -created by the owner of cicero.res_publica.id.
      • -
      - -

      That said, to create a subdomain, the subdomain owner generates a -subdomain-creation operation for their desired name -and gives it to the on-chain name owner. -The on-chain name owner then uses Atlas to -broadcast it to all other BNS nodes.

      - -

      Once created, a subdomain owner can use any on-chain name owner to broadcast a -subdomain-update operation. To do so, they generate and sign the requisite -subdomain operation and give it to an on-chain name owner, who then packages it -with other subdomain operations into a DNS zone file -and sends them all out on the Atlas network.

      - -

      If the subdomain owner wants to change the address of their subdomain, they need -to sign a subdomain-transfer operation and give it to the on-chain name owner -who created the subdomain. They then package it into a zone file and broadcast -it.

      - -

      Subdomain Registrars

      - -

      Because subdomain names are cheap, developers may be inclined to run -subdomain registrars on behalf of their applications. For example, -the name personal.id is used to register Blockstack application users without -requiring them to spend any Bitcoin.

      - -

      We supply a reference -implementation of a BNS Subdomain Registrar -to help developers broadcast subdomain operations. Users would still own their -subdomain names; the registrar simply gives developers a convenient way for them -to register and manage them in the context of a particular application. -Please see the tutorial on running a subdomain registrar for -details on how to use it.

      - -

      BNS Forks

      - -

      BNS effectively uses a public blockchain to store a database log. A BNS peer -bootstraps itself by downloading and replaying the database log from the -blockchain, and in doing so, will calculate the same name database state as -every other (honest) BNS peer that has the same view of the blockchain.

      - -

      Crucially, BNS is built on top of a public blockchain that is unaware of BNS’s existence. -This means that the blockchain peers do not validate BNS transactions. Instead, -the BNS peer needs to do so, and must know how to reject both invalid transactions -as well as well-formed transactions from dishonest peers (i.e. peers that do not -follow the same consensus rules).

      - -

      BNS nodes do not directly communicate with one another—by design, the set of -BNS peers is not enumerable. The only shared communication medium between BNS -peers is the blockchain.

      - -

      To identify and reject invalid and malicious transactions without the blockchain’s help, -the log of name operations embedded in the blockchain is constructed as a -fork*-consistent database -log. Fork*-consistency is a consistency -model whereby the state -replicas in all of the nodes exhibit the following properties:

      - -
        -
      • -

        Each correct peer maintains a history of well-formed, valid state operations. In this -case, each correct BNS node maintains a copy of the history blockchain transactions -that encoded well-formed, valid name operations.

        -
      • -
      • -

        Each honest peer’s history contains the sequence of all operations that it -sent. That is, a user’s BNS peer’s transaction log will contain the sequence of all valid -transactions that the user’s client wrote to the blockchain.

        -
      • -
      • -

        If two peers accept operations op and op’ from the same correct client, -then both of their logs will have the both operations in the same order. If -op’ was accepted before op, then both peers’ logs are identical up to op’. -In BNS, this means that if two peers both accept a given transaction, then it -means that they have accepted the same sequence of prior transactions.

        -
      • -
      - -

      This means that unlike with blockchains, -there can be multiple long-lived conflicting forks of the BNS database log. -However, due to fork*-consistency, a correct BNS peer will only process one -of these forks, and will ignore transactions from peers in other forks. In other words, -fork*-consistency partitions the set of BNS peers into different fork-sets, -where all peers in a fork-set process each other’s transactions, but the -completely ignore peers in other fork-sets.

      - -

      BNS nodes identify which fork set they belong to using a consensus hash. The -consensus hash is a cryptographic digest of a node’s operation -history. Each BNS peer calculates a new consensus hash each time it processes a -new block, and stores the history of consensus hashes for each block it -processed.

      - -

      Two honest BNS peers can quickly determine if they are in the same fork-set by querying -each other’s consensus hashes for a given block. If they match, then they are -in the same fork-set (assming no hash collisions).

      - -

      A BNS client executes a name operation on a specific fork-set by including a -recent consensus hash from that fork-set in the blockchain transaction. -At the same time, the BNS consensus rules state that a transaction can only be -accepted if it included a recent valid consensus hash. -This means that all BNS nodes in the client’s desired fork-set will accept -the transaction, and all other BNS nodes not in the fork-set will ignore it. -You can see where the consensus hash is included in blockchain transactions by reading -the transaction wire format document.

      - -

      Fork-set Selection

      - -

      The blockchain linearizes the history of transactions, which means that -in general, there exists a fork-set for each distinct set of BNS -consensus rules. For example, the Blockstack Core 2016 hard fork -and 2017 hard fork both introduced new consensus -rules, which means at the time of this writing there are three possible fork-sets: -the pre-2016 fork-set, the 2016-2017 fork-set, and the post-2017 fork-set. -The public BNS nodes are always running -in the fork-set with the latest consensus rules.

      - -

      BNS clients are incentivized to communicate with peers in the fork-set that has -the most use, since this fork-set’s name database will encode name/state -bindings that are the most widely-accepted and understood by users. -To identify this fork-set, a BNS client needs to learn one of -its recent consensus hashes. Once it has a recent consensus hash, it can -query an untrusted BNS node for a copy of -its name database, and use the consensus hash to verify that the name database -was used to generate it.

      - -

      How does a BNS node determine whether or not a consensus hash corresponds to the -most widely-used fork-set? There are two strategies:

      - -
        -
      • -

        Determine whether or not a characteristic transaction was accepted by the -widely-used fork-set. If a client knows that a specific transaction belongs to -the widely-used fork-set and not others, then they can use the consensus hash to -efficiently determine whether or not a given node belongs to this fork-set.

        -
      • -
      • -

        Determine how much “economic activity” exists in a fork-set by inspecting -the blockchain for burned cryptocurrency tokens. Namespace and name -registrations are structured in a way that sends cryptocurrency tokens to either -a well-known burn address, or to an easily-queried pay-to-namespace-creator -address.

        -
      • -
      - -

      Both strategies rely on the fact that the consensus hash is calculated as a -Merkle skip-list -over the BNS node’s accepted transactions. A client can use a consensus hash to -determine whether or not a transaction T was accepted by a node with O(log -n) time and space complexity. We call the protocol for resolving a consensus hash to a specific transaction -Simplified Name Verification (SNV). See our paper on the subject -for details of how SNV works under the hood.

      - -

      If the client has a consensus hash and knows of a characteristic transaction in the widely-used fork-set, -it can use SNV to determine whether or not a node belongs to the fork-set that accepted it.

      - -

      If the client knows about multiple conflicting consensus hashes, -they can still use SNV to determine which one corresponds -to the most-used fork-set. To do so, the client would use a -blockchain explorer to find the -list of transactions that burned cryptocurrency tokens. Each of these -transactions will be treated as potential characteristic transactions: -the client would first select the subset of transactions that are well-formed -BNS transactions, and then use SNV to determine which of them correspond to which -consensus hashes. The client chooses the consensus hash that corresponds -to the fork-set with the highest cumulative burn.

      - -

      Work is currently underway to automate this process.

      - -

      Decentralized Identifiers (DIDs)

      - -

      BNS nodes are compliant with the emerging -Decentralized Identity Foundation protocol -specification for decentralized identifiers (DIDs).

      - -

      Each name in BNS has an associated DID. The DID format for BNS is:

      - -
          did:stack:v0:{address}-{index}
      -
      -
      - -

      Where:

      -
        -
      • {address} is an on-chain public key hash (e.g. a Bitcoin address).
      • -
      • {index} refers to the nth name this address created.
      • -
      - -

      For example, the DID for personal.id is -did:stack:v0:1dARRtzHPAFRNE7Yup2Md9w18XEQAtLiV-0, because the name -personal.id was the first-ever name created by -1dARRtzHPAFRNE7Yup2Md9w18XEQAtLiV.

      - -

      As another example, the DID for jude.id is did:stack:v0:16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg-1. -Here, the address 16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg had created one earlier -name in history prior to this one (which happens to be abcdefgh123456.id).

      - -

      The purpose of a DID is to provide an eternal identifier for a public key. -The public key may change, but the DID will not.

      - -

      Blockstack Core implements a DID method of its own -in order to be compatible with other systems that use DIDs for public key resolution. -In order for a DID to be resolvable, all of the following must be true for a -name:

      - -
        -
      • The name must exist
      • -
      • The name’s zone file hash must be the hash of a well-formed DNS zone file
      • -
      • The DNS zone file must be present in the BNS Atlas Network
      • -
      • The DNS zone file must contain a URI resource record that points to a signed -JSON Web Token
      • -
      • The public key that signed the JSON Web Token (and is included with it) must -hash to the address that owns the name
      • -
      - -

      Not all names will have DIDs that resolve to public keys. However, names created by the Blockstack -Browser will have DIDs that -do.

      - -

      Developers can programmatically resolve DIDs via the Python API:

      - -
      >>> import blockstack
      ->>> blockstack.lib.client.resolve_DID('did:stack:v0:16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg-1', hostport='https://node.blockstack.org:6263')
      -{'public_key': '020fadbbcea0ff3b05f03195b41cd991d7a0af8bd38559943aec99cbdaf0b22cc8'}
      -
      - -

      A RESTful API is under development.

      - -

      DID Encoding for Subdomains

      - -

      Every name and subdomain in BNS has a DID. The encoding is slightly different -for subdomains, so the software can determine which code-path to take.

      - -
        -
      • -

        For on-chain BNS names, the {address} is the same as the Bitcoin address -that owns the name. Currently, both version byte 0 and version byte 5 -addresses are supported (i.e. addresses starting with 1 or 3, meaning p2pkh and -p2sh addresses).

        -
      • -
      • -

        For off-chain BNS subdomains, the {address} has version byte 63 for -subdomains owned by a single private key, and version byte 50 for subdomains -owned by a m-of-n set of private keys. That is, subdomain DID addresses start -with S or M, respectively.

        -
      • -
      - -

      The {index} field for a subdomain’s DID is distinct from the {index} field -for a BNS name’s DID, even if the same created both names and subdomains. -For example, the name abcdefgh123456.id has the DID did:stack:v0:16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg-0, -because it was the first name created by 16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg. -However, 16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg also created jude.statism.id -as its first subdomain name. The DID for jude.statism.id is -did:stack:v0:SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i-0. Note that the address -SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i encodes the same public key hash as the address -16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg (the only difference between these two -strings is that the first is base58check-encoded with version byte 0, and the -second is encoded with version byte 63).

      - -

      You can see this play out in practice with the following code snippit:

      - -
      >>> import blockstack
      ->>> blockstack.lib.client.get_name_record('jude.statism.id', hostport='https://node.blockstack.org:6263')['address']
      -u'16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg'
      ->>> import virtualchain
      ->>> virtualchain.address_reencode('16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg', version_byte=63)
      -'SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i'
      ->>> blockstack.lib.client.resolve_DID('did:stack:v0:SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i-0', hostport='https://node.blockstack.org:6263')
      -{'public_key': '020fadbbcea0ff3b05f03195b41cd991d7a0af8bd38559943aec99cbdaf0b22cc8'}
      -
      -
      - -

      Summary

      - -

      BNS is a decentralized naming system that produces names that are -globally-unique, human-readable, and strongly-owned. It does so by embedding a -database log within a public blockchain like Bitcoin, and replaying it locally -to calculate the state and owner of each name. In doing so, a BNS node provides -a full replica of all of the network’s state, which gives it the ability to -service queries like prior name states and name historys.

      - -

      BNS groups names by namespaces in order to provide different ways to register -and use names, and furthermore implements subdomains to provide a cheap, -scalable way to register many names for the cost of a single blockchain -transaction.

      - -

      Blockstack Core is the -reference implementation of BNS, and provides a public BNS node with online -documentation at https://core.blockstack.org.

      - -

      Appendix 1: Feature Comparison

      - -

      BNS is not the only naming system in wide-spread use, nor is it the only -decentralized naming system that implements human-readable, -globally-unique, and strongly-owned names. The following feature table -describes how BNS differs from other naming systems

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      FeatureBNSENSDNSNamecoin
      Globally unique namesXXXX
      Human-readable namesXXXX
      Strongly-owned namesXX X
      Names are enumerableX  X
      Registration times1-2 hours~1 week~1 day1-2 hours
      Subdomain registration times1 hour (instant with #750)variesinstant~1 hour
      Anyone can make a TLD/namespaceX[1] [1]
      TLD/Namespace owners get registration feesX X 
      TLD/Namespace can be seeded with initial namesX X 
      Portable across blockchainsX N/A 
      Off-chain namesX N/A 
      Off-chain name stateXXN/A 
      Name provenanceXX X
      DID supportX   
      Turing-complete namespace rules XX 
      Miners are rewarded for participating[1] N/AX
      - -

      [1] Requires support in higher-level applications. These systems are not aware -of the existence of namespaces/TLDs at the protocol level.

      - -

      [2] Blockstack Core destroys the underlying blockchain token to pay for -registration fees when there is no pay-to-namespace-creator address set in the -name’s namespace. This has the effect of making the blockchain miners’ holdings -slightly more valuable.

      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/naming/manage.html b/_site/core/naming/manage.html deleted file mode 100644 index 9c8e1b5197..0000000000 --- a/_site/core/naming/manage.html +++ /dev/null @@ -1,579 +0,0 @@ - - - - - - - - -Manage BNS Names | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      Manage BNS Names

      - - - - - -
      - -

      This section teaches you how to manage your namespace, it contains the -following sections:

      - - - -

      Overview of management

      - -

      Once you register a BNS name, you have the power to change its zone file hash, -change its public key hash, destroy it (i.e. render it unresolvable), -or renew it. The BNS consensus rules ensure that only you, as the owner of -the name’s private key, have the ability to carry out these operations.

      - -

      Each of these operations are executed by sending a specially-formatted -blockchain transaction to the blockchain, which BNS nodes read and process. -The operations are listed below:

      - - - - - - - - - - - - - - - - - - - - - - - - - - -
      Transaction TypeDescription
      NAME_UPDATEThis changes the name’s zone file hash. Any 20-byte string is allowed.
      NAME_TRANSFERThis changes the name’s public key hash. In addition, the current owner has the option to atomically clear the name’s zone file hash (so the new owner won’t “receive” the zone file).
      NAME_REVOKEThis renders a name unresolvable. You should do this if your private key is compromised.
      NAME_RENEWALThis pushes back the name’s expiration date (if it has one), and optionally both sets a new zone file hash and a new public key hash.
      - -

      The reference BNS clients— -blockstack.js and the Blockstack -Browser—can handle creating -and sending all of these transactions for you.

      - -

      NAME_UPDATE (live example)

      - -

      A NAME_UPDATE transaction changes the name’s zone file hash. You would send -one of these transactions if you wanted to change the name’s zone file contents. -For example, you would do this if you want to deploy your own Gaia -hub and want other people to read from it.

      - -

      A NAME_UPDATE transaction is generated from the name, a recent consensus -hash, and the new zone file hash. The reference clients gather -this information automatically. See the transaction format -document for details on how to construct this transaction.

      - -

      NAME_TRANSFER (live example)

      - -

      A NAME_TRANSFER transaction changes the name’s public key hash. You would -send one of these transactions if you wanted to:

      - -
        -
      • Change your private key
      • -
      • Send the name to someone else
      • -
      - -

      When transferring a name, you have the option to also clear the name’s zone -file hash (i.e. set it to null). -This is useful for when you send the name to someone else, so the -recipient’s name does not resolve to your zone file.

      - -

      The NAME_TRANSFER transaction is generated from the name, a recent consensus -hash, and the new public key hash. The reference clients gather -this information automatically. See the transaction format -document for details on how to construct this transaction.

      - -

      NAME_REVOKE (live example)

      - -

      A NAME_REVOKE transaction makes a name unresolvable. The BNS consensus rules -stipulate that once a name is revoked, no one can change its public key hash or -its zone file hash. The name’s zone file hash is set to null to prevent it -from resolving.

      - -

      You should only do this if your private key is compromised, or if you want to -render your name unusable for whatever reason. It is rarely used in practice.

      - -

      The NAME_REVOKE operation is generated using only the name. See the -transaction format document for details on how to construct -it.

      - -

      NAME_RENEWAL (live example)

      - -

      Depending in the namespace rules, a name can expire. For example, names in the -.id namespace expire after 2 years. You need to send a NAME_RENEWAL every -so often to keep your name.

      - -

      A NAME_RENEWAL costs both transaction fees and registration fees. You will -pay the registration cost of your name to the namespace’s designated burn address when you -renew it. You can find this fee using the /v1/prices/names/{name} endpoint.

      - -

      When a name expires, it enters a month-long “grace period” (5000 blocks). It -will stop resolving in the grace period, and all of the above operations will -cease to be honored by the BNS consensus rules. You may, however, send a -NAME_RENEWAL during this grace period to preserve your name.

      - -

      If your name is in a namespace where names do not expire, then you never need to -use this transaction.

      - -

      When you send a NAME_RENEWAL, you have the option of also setting a new public -key hash and a new zone file hash. See the transaction format -document for details on how to construct this transaction.

      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/naming/namespaces.html b/_site/core/naming/namespaces.html deleted file mode 100644 index a6d0c5fb1e..0000000000 --- a/_site/core/naming/namespaces.html +++ /dev/null @@ -1,593 +0,0 @@ - - - - - - - - -Understand Namespaces | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      Understand Namespaces

      - - - - - -
      - -

      Namespaces are the top-level naming objects in BNS.

      - -

      They control a few properties about the names within them:

      -
        -
      • How expensive they are to register
      • -
      • How long they last before they have to be renewed
      • -
      • Who (if anyone) receives the name registration fees
      • -
      • Who is allowed to seed the namespace with its initial names.
      • -
      - -

      At the time of this writing, by far the largest BNS namespace is the .id -namespace. Names in the .id namespace are meant for resolving user -identities. Short names in .id are more expensive than long names, and have -to be renewed by their owners every two years. Name registration fees are not -paid to anyone in particular—they are instead sent to a “black hole” where -they are rendered unspendable (the intention is to discourage ID sqautters).

      - -

      Unlike DNS, anyone can create a namespace and set its properties. Namespaces -are created on a first-come first-serve basis, and once created, they last -forever.

      - -

      However, creating a namespace is not free. The namespace creator must burn -cryptocurrency to do so. The shorter the namespace, the more cryptocurrency -must be burned (i.e. short namespaces are more valuable than long namespaces). -For example, it cost Blockstack PBC 40 BTC to create the .id namespace in 2015 -(in transaction -5f00b8e609821edd6f3369ee4ee86e03ea34b890e242236cdb66ef6c9c6a1b281).

      - -

      Namespaces can be between 1 and 19 characters long, and are composed of the -characters a-z, 0-9, -, and _.

      - -

      Namespace Organization

      - -

      BNS names are organized into a global name hierarchy. There are three different -layers in this hierarchy related to naming:

      - -
        -
      • -

        Namespaces. These are the top-level names in the hierarchy. An analogy -to BNS namespaces are DNS top-level domains. Existing BNS namespaces include -.id, .podcast, and .helloworld. All other names belong to exactly one -namespace. Anyone can create a namespace, but in order for the namespace -to be persisted, it must be launched so that anyone can register names in it. -Namespaces are not owned by their creators.

        -
      • -
      • -

        BNS names. These are names whose records are stored directly on the -blockchain. The ownership and state of these names are controlled by sending -blockchain transactions. Example names include verified.podcast and -muneeb.id. Anyone can create a BNS name, as long as the namespace that -contains it exists already. The state for BNS names is usually stored in the Atlas -network.

        -
      • -
      • -

        BNS subdomains. These are names whose records are stored off-chain, -but are collectively anchored to the blockchain. The ownership and state for -these names lives within the Atlas network. While BNS -subdomains are owned by separate private keys, a BNS name owner must -broadcast their subdomain state. Example subdomains include jude.personal.id -and podsaveamerica.verified.podcast. Unlike BNS namespaces and names, the -state of BNS subdomains is not part of the blockchain consensus rules.

        -
      • -
      - -

      A feature comparison matrix summarizing the similarities and differences -between these name objects is presented below:

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      FeatureNamespacesBNS namesBNS Subdomains
      Globally uniqueXXX
      Human-meaningfulXXX
      Owned by a private key XX
      Anyone can createXX[1]
      Owner can update X[1]
      State hosted on-chainXX 
      State hosted off-chain XX
      Behavior controlled by consensus rulesXX 
      May have an expiration date X 
      - -

      [1] Requires the cooperation of a BNS name owner to broadcast its transactions

      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/naming/pickname.html b/_site/core/naming/pickname.html deleted file mode 100644 index 4d45fdee06..0000000000 --- a/_site/core/naming/pickname.html +++ /dev/null @@ -1,625 +0,0 @@ - - - - - - - - -Choose a name | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      Choose a name

      - - - - - -
      - -

      This section explains how to choose and create a namespace, it contains the -following sections:

      - - - -

      Intended uses for a namespace

      - -

      The intention is that each application can create its own BNS -namespace for its own purposes. Applications can use namespaces for things like:

      - -
        -
      • Giving users a SSO system, where each user registers their public key under a -username. Blockstack applications do this with names in the .id namespace, -for example.
      • -
      • Providing a subscription service, where each name is a 3rd party that provides -a service for users to subscribe to. For example, names in -.podcast point to podcasts that users of the -DotPodcast app can subscribe to.
      • -
      • Implementing software licenses, where each name corresponds to an access key. -Unlike conventional access keys, access keys implemented as names -can be sold and traded independently. The licensing fee (paid as a name -registration) would be set by the developer and sent to a developer-controlled -blockchain address.
      • -
      - -

      Names within a namespace can serve any purpose the developer wants. The ability -to collect registration fees for 1 year after creating the namespace not only -gives developers the incentive to get users to participate in the app, but also -gives them a way to measure economic activity.

      - -

      Developers can query individual namespaces and look up names within them using -the BNS API.

      - -

      List all namespaces in existence (reference).

      - -
      $ curl https://core.blockstack.org/v1/namespaces
      -[
      -  "id",
      -  "helloworld",
      -  "podcast"
      -]
      -
      -
      - -

      List all names within a namespace (reference)

      - -
      $ curl https://core.blockstack.org/v1/namespaces/id/names?page=0
      -[
      -  "0.id",
      -  "0000.id",
      -  "000000.id",
      -  "000001.id",
      -  "00000111111.id",
      -  "000002.id",
      -  "000007.id",
      -  "0011sro.id",
      -  "007_007.id",
      -  "00n3w5.id",
      -  "00r4zr.id",
      -  "00w1k1.id",
      -  "0101010.id",
      -  "01jack.id",
      -  "06nenglish.id",
      -  "08.id",
      -  "0cool_f.id",
      -  "0dadj1an.id",
      -  "0nelove.id",
      -  "0nename.id"
      -...
      -]
      -
      -
      - -

      Each page returns a batch of 100 names.

      - -

      Get the Cost to Register a Namespace (reference)

      - -
      $ curl https://core.blockstack.org/v1/prices/namespaces/test
      -{
      -  "satoshis": 40000000
      -}
      -
      -
      - -

      If you want to register a namespace, please see the namespace creation tutorial.

      - -

      Getting the Current Consensus Hash (reference)

      - -
      $ curl -sL https://core.blockstack.org/v1/blockchains/bitcoin/consensus
      -{
      -  "consensus_hash": "98adf31989bd937576aa190cc9f5fa3a"
      -}
      -
      -
      - -

      A recent consensus hash is required to create a NAMESPACE_PREORDER transaction. The reference -BNS clients do this automatically. See the transaction format -document for details on how the consensus hash is used to construct the -transaction.

      - -

      Create a namespace

      - -

      There are four steps to creating a namespace:

      - -
        -
      1. -

        Send a NAMESPACE_PREORDER transaction (live example). -This is the first step. This registers the salted hash of the namespace with BNS nodes, and burns the -requisite amount of cryptocurrency. In addition, it proves to the -BNS nodes that user has honored the BNS consensus rules by including -a recent consensus hash in the transaction -(see the section on BNS forks for details).

        -
      2. -
      3. -

        Send a NAMESPACE_REVEAL transaction (live example). -This is the second step. This reveals the salt and the namespace ID (pairing it with its -NAMESPACE_PREORDER), it reveals how long names last in this namespace before -they expire or must be renewed, and it sets a price function for the namespace -that determines how cheap or expensive names its will be. The price function takes -a name in this namespace as input, and outputs the amount of cryptocurrency the -name will cost (i.e. by examining how long the name is, and whether or not it -has any vowels or non-alphabet characters). The namespace creator -has the option to collect name registration fees for the first year of the -namespace’s existence by setting a namespace creator address.

        -
      4. -
      5. -

        Seed the namespace with NAME_IMPORT transactions (live example). -Once the namespace has been revealed, the user has the option to populate it with a set of -names. Each imported name is given both an owner and some off-chain state. -This step is optional—namespace creators are not required to import names.

        -
      6. -
      7. -

        Send a NAMESPACE_READY transaction (live example). -This is the final step of the process. It launches the namespace, which makes it available to the -public. Once a namespace is ready, anyone can register a name in it if they -pay the appropriate amount of cryptocurrency (according to the price funtion -revealed in step 2).

        -
      8. -
      - -

      The reason for the NAMESPACE_PREORDER/NAMESPACE_REVEAL pairing is to prevent -frontrunning. The BNS consensus rules require a NAMESPACE_REVEAL to be -paired with a previous NAMESPACE_PREORDER sent within the past 24 hours. -If it did not do this, then a malicious actor could watch the blockchain network -and race a victim to claim a namespace.

      - -

      Namespaces are created on a first-come first-serve basis. If two people try to -create the same namespace, the one that successfully confirms both the -NAMESPACE_PREORDER and NAMESPACE_REVEAL wins. The fee burned in the -NAMESPACE_PREORDER is spent either way.

      - -

      Once the user issues the NAMESPACE_PREORDER and NAMESPACE_REVEAL, they have -1 year before they must send the NAMESPACE_READY transaction. If they do not -do this, then the namespace they created disappears (along with all the names -they imported).

      - -

      Developers wanting to create their own namespaces should read the namespace -creation document. It is highly recommended that -developers follow this tutorial closely, given the large amount of -cryptocurrency at stake.

      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/naming/register.html b/_site/core/naming/register.html deleted file mode 100644 index 22a458ce2d..0000000000 --- a/_site/core/naming/register.html +++ /dev/null @@ -1,575 +0,0 @@ - - - - - - - - -Register a name | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      Register a name

      - - - - - -
      - -

      This section explains registering BNS names and provides instructions for methods -you can use to understandt the cost of namespace registration.

      - - - -

      Understand registration

      - -

      Registering a BNS name costs cryptocurrency. This cost comes from two sources:

      - -
        -
      • -

        Transaction fees: These are the fees imposed by the cost of storing the -transaction data to the blockchain itself. They are independent of BNS, since -all of the blockchain’s users are competing to have their transactions included -in the next block. The blockchain’s miners receive the transaction fee.

        -
      • -
      • -

        Registration fees: Each BNS namespace imposes an additional fee on how -much a name costs. The registration fee is sent to the namespace creator -during the first year that a namespace exists, and is sent to a burn address -afterwards. The registration fee is different for each name and is -determined by the namespace itself, but can be queried in advance by the user.

        -
      • -
      - -

      Registering a name takes two transactions. They are:

      - -
        -
      • -

        NAME_PREORDER transaction: This is the first transaction to be sent. -It tells all BNS nodes the salted hash of the BNS name, and it pays the -registration fee to the namespace owner’s designated address (or the burn -address). In addition, it proves to the BNS nodes that the client knows about -the current state of the system by including a recent consensus hash -in the transaction (see the section on BNS forks for details).

        -
      • -
      • -

        NAME_REGISTRATION transaction: This is the second transaction to be -sent. It reveals the salt and the name to all BNS nodes, and assigns the name -an initial public key hash and zone file hash

        -
      • -
      - -

      The reason this process takes two transactions is to prevent front-running. -The BNS consensus rules stipulate that a name can only be registered if its -matching preorder transaction was sent in the last 24 hours. Because a name -must be preordered before it is registered, someone watching the blockchain’s -peer network cannot race a victim to claim the name they were trying to -register (i.e. the attacker would have needed to send a NAME_PREORDER -transaction first, and would have had to have sent it no more than 24 hours -ago).

      - -

      Registering a name on top of the Bitcoin blockchain takes 1-2 hours. This is -because you need to wait for the NAME_PREORDER transaction to be sufficiently -confirmed before sending the NAME_REGISTRATION transaction. The BNS nodes -only register the name once both transactions have at least 6 confirmations -(which itself usually takes about an hour).

      - -

      Names are registered on a first-come first-serve basis. -If two different people try to register the same name at the same time, the -person who completes the two-step process earliest will receive the name. The -other person’s NAME_REGISTRATION transaction will be ignored, since it will -not be considered valid at this point. The registration fee paid by the -NAME_PREORDER will be lost. However, this situation is rare in practice— -as of early 2018, we only know of one confirmed instance in the system’s 3+ years -of operation.

      - -

      Fully-qualified names can be between 3 and 37 characters long, and consist of -the characters a-z, 0-9, +, -, _, and .. This is to prevent -homograph attacks. -NAME_REGISTRATION transactions that do not conform to this requirement will be -ignored.

      - -

      Getting a Name’s Registration Fee (reference)

      - -
      $ curl -sL https://core.blockstack.org/v1/prices/names/helloworld.id | jq -r ".name_price"
      -{
      -  "btc": 2.5e-05,
      -  "satoshis": 2500
      -}
      -
      -
      - -

      Note the use of jq -r to select the "name_price" field. This API -endpoint may return other ancilliary data regarding transaction fee estimation, -but this is the only field guaranteed by this specification to be present.

      - -

      Getting the Current Consensus Hash (reference)

      - -
      $ curl -sL https://core.blockstack.org/v1/blockchains/bitcoin/consensus
      -{
      -  "consensus_hash": "98adf31989bd937576aa190cc9f5fa3a"
      -}
      -
      -
      - -

      The consensus hash must be included in the NAME_PREORDER transaction. The BNS -clients do this automatically. See the transaction format -document for details as to how to include this in the -transaction.

      - -

      Registering a Name

      - -

      Registration happens through a BNS client, such as the Blockstack -Browser or -blockstack.js. -The reference BNS clients manage a local Bitcoin wallet, calculate transaction fees -dynamically and automatically, and broadcast both the NAME_PREORDER and -NAME_REGISTRATION transactions at the right times.

      - -

      If you want to make your own registration client, you should see the -transaction format document.

      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/naming/resolving.html b/_site/core/naming/resolving.html deleted file mode 100644 index c88e7d7e23..0000000000 --- a/_site/core/naming/resolving.html +++ /dev/null @@ -1,745 +0,0 @@ - - - - - - - - -Resolve a name | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      Resolve a name

      - - - - - -
      - -

      This section explains resolving BNS names and provides instructions for methods -you can use to accomplish namespace resolution.

      - - - -

      Understand resolution

      - -

      BNS names are bound to both public keys and to about 40Kb of off-chain state. -The off-chain state is encoded as a DNS zone file, -which contains routing information for discovering the user’s Blockstack data -(such as their profile and app data, which are hosted in the Gaia storage -system).

      - -

      The blockchain is not used to store this information directly. Instead, the -blockchain stores the public key hash and the zone file hash. When -indexing the blockchain, each BNS node builds a database with -three columns: all the on-chain BNS names that have been registered, each -name’s public key hash, and each name’s zone file’s hash. -In addition, each BNS node maintains the transaction history of each name. -A developer can resolve a name to any configuration it was in at any prior -point in time.

      - -

      Below is an example name table pulled from a live BNS node:

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      NamePublic key hashZone File Hash
      ryan.id15BcxePn59Y6mYD2fRLCLCaaHScefqW2Noa455954b3e38685e487efa41480beeb315f4ec65
      muneeb.id1J3PUxY5uDShUnHRrMyU6yKtoHEUPhKULs37aecf837c6ae9bdc9dbd98a268f263dacd00361
      jude.id16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJgb6e99200125e70d634b17fe61ce55b09881bfafd
      verified.podcast1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH6701ce856620d4f2f57cd23b166089759ef6eabd
      cicero.res_publica.id1EtE77Aa5AA8etzF2irk56vvkS4v7rZ7PE7e4ac75f9d79ba9d5d284fac19617497433b832d
      podsaveamerica.verified.podcast1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH0d6f090db8945aa0e60759f9c866b17645893a95
      - -

      In practice, the zone file hash is the RIPEMD160 hash of the SHA256 hash of -the zone file, and the public key is the base58check-encoded RIPEMD160 hash -of the double-SHA256 hash of the ECDSA public key (i.e. a Bitcoin address).

      - -

      The BNS consensus rules ensure that -a BNS name can only be registered if it is not already taken, and that only the -user who owns the name’s private key can change its public key hash or zone file -hash. This means that a name’s public key and zone file can be stored anywhere, -since they can be authenticated using the hashes discovered by indexing the -blockchain under the BNS consensus rules.

      - -

      BNS nodes implement a decentralized storage system for zone files called the -Atlas network. In this system, BNS nodes eagerly replicate -all the zone files they know about to one another, so that eventually every BNS -node has a full replica of all zone files.

      - -

      The public keys for names are stored off-chain in Gaia. -The user controls where their public keys are hosted using the zone file -contents (if they are hosted online anywhere at all).

      - -

      Developers can query this table via the BNS API. The API offers routes -to do the following:

      - -

      Look up a name’s public key and zone file (reference)

      - -
      $ curl https://core.blockstack.org/v1/names/muneeb.id
      -{
      -  "address": "1J3PUxY5uDShUnHRrMyU6yKtoHEUPhKULs",
      -  "blockchain": "bitcoin",
      -  "expire_block": 599266,
      -  "last_txid": "7e16e8688ca0413a398bbaf16ad4b10d3c9439555fc140f58e5ab4e50793c476",
      -  "status": "registered",
      -  "zonefile": "$ORIGIN muneeb.id\n$TTL 3600\n_http._tcp URI 10 1 \"https://gaia.blockstack.org/hub/1J3PUxY5uDShUnHRrMyU6yKtoHEUPhKULs/0/profile.json\"\n",
      -  "zonefile_hash": "37aecf837c6ae9bdc9dbd98a268f263dacd00361"
      -}
      -
      -
      - -

      Note that the zonefile field is given with the off-chain data that hashes -to the zonefile_hash field.

      - -

      List all names the node knows about (reference)

      - -
      $ curl https://core.blockstack.org/v1/names?page=0
      -[
      -  "judecn.id",
      -  "3.id",
      -  "4.id",
      -  "8.id",
      -  "e.id",
      -  "h.id",
      -  "5.id",
      -  "9.id",
      -  "i.id",
      -  "l.id",
      -  "p.id",
      -  "w.id",
      -  "ba.id",
      -  "df.id",
      -...
      -]
      -
      -
      - -

      Each page returns 100 names. While no specific ordering is mandated by the -protocol, the reference implementation orders names by their order of creation -in the blockchain.

      - -

      Look up the history of states a name was in (reference)

      - -
      $ curl https://core.blockstack.org/v1/names/patrickstanley.id/history
      -{
      -  "445838": [
      -    {
      -      "address": "1occgbip7tFDXX9MvzQhcnTUUjcVX2dYK",
      -      "block_number": 445838,
      -      "burn_address": "1111111111111111111114oLvT2",
      -      "consensus_hash": "7b696b6f4060b792d41912068944d73b",
      -      "op": "?",
      -      "op_fee": 25000,
      -      "opcode": "NAME_PREORDER",
      -      "preorder_hash": "26bf7874706ac761afdd403ed6b3b9578fb01a34",
      -      "sender": "76a91408d0dd44c1f0a3a4f0957ae95901929d7d66d55788ac",
      -      "sender_pubkey": "039a8948d339ecbff44cf426cb85d90fce876f1658d385cdc47f007f279be626ea",
      -      "txid": "6730ae09574d5935ffabe3dd63a9341ea54fafae62fde36c27738e9ee9c4e889",
      -      "vtxindex": 40
      -    }
      -  ],
      -  "445851": [
      -    {
      -      "address": "17CbHgTgBG3kLedXNneEKBkCTgW2fyrnUD",
      -      "block_number": 445838,
      -      "consensus_hash": null,
      -      "first_registered": 445851,
      -      "importer": null,
      -      "importer_address": null,
      -      "last_creation_op": "?",
      -      "last_renewed": 445851,
      -      "name": "patrickstanley.id",
      -      "name_hash128": "683a3e1ee5f0296833c56e481cf41b77",
      -      "namespace_block_number": 373601,
      -      "namespace_id": "id",
      -      "op": ":",
      -      "op_fee": 25000,
      -      "opcode": "NAME_REGISTRATION",
      -      "preorder_block_number": 445838,
      -      "preorder_hash": "26bf7874706ac761afdd403ed6b3b9578fb01a34",
      -      "revoked": false,
      -      "sender": "76a9144401f3be5311585ea519c1cb471a8dc7b02fd6ee88ac",
      -      "sender_pubkey": "039a8948d339ecbff44cf426cb85d90fce876f1658d385cdc47f007f279be626ea",
      -      "transfer_send_block_id": null,
      -      "txid": "55b8b42fc3e3d23cbc0f07d38edae6a451dfc512b770fd7903725f9e465b2925",
      -      "value_hash": null,
      -      "vtxindex": 54
      -    }
      -  ],
      -  "445873": [
      -    {
      -      "address": "17CbHgTgBG3kLedXNneEKBkCTgW2fyrnUD",
      -      "block_number": 445838,
      -      "consensus_hash": "18b8d69f0182b89ccb1aa536f83be18a",
      -      "first_registered": 445851,
      -      "importer": null,
      -      "importer_address": null,
      -      "last_creation_op": "?",
      -      "last_renewed": 445851,
      -      "name": "patrickstanley.id",
      -      "name_hash128": "683a3e1ee5f0296833c56e481cf41b77",
      -      "namespace_block_number": 373601,
      -      "namespace_id": "id",
      -      "op": "+",
      -      "op_fee": 25000,
      -      "opcode": "NAME_UPDATE",
      -      "preorder_block_number": 445838,
      -      "preorder_hash": "26bf7874706ac761afdd403ed6b3b9578fb01a34",
      -      "revoked": false,
      -      "sender": "76a9144401f3be5311585ea519c1cb471a8dc7b02fd6ee88ac",
      -      "sender_pubkey": "039a8948d339ecbff44cf426cb85d90fce876f1658d385cdc47f007f279be626ea",
      -      "transfer_send_block_id": null,
      -      "txid": "dc478659fc684a1a6e1e09901971e82de11f4dfe2b32a656700bf9a3b6030719",
      -      "value_hash": "02af0ef21161ad06b0923106f40b994b9e4c1614",
      -      "vtxindex": 95
      -    }
      -  ],
      -  "445884": [
      -    {
      -      "address": "1GZqrVbamkaE6YNveJFWK6cDrCy6bXyS6b",
      -      "block_number": 445838,
      -      "consensus_hash": "18b8d69f0182b89ccb1aa536f83be18a",
      -      "first_registered": 445851,
      -      "importer": null,
      -      "importer_address": null,
      -      "last_creation_op": "?",
      -      "last_renewed": 445851,
      -      "name": "patrickstanley.id",
      -      "name_hash128": "683a3e1ee5f0296833c56e481cf41b77",
      -      "namespace_block_number": 373601,
      -      "namespace_id": "id",
      -      "op": ">>",
      -      "op_fee": 25000,
      -      "opcode": "NAME_TRANSFER",
      -      "preorder_block_number": 445838,
      -      "preorder_hash": "26bf7874706ac761afdd403ed6b3b9578fb01a34",
      -      "revoked": false,
      -      "sender": "76a914aabffa6dd90d731d3a349f009323bb312483c15088ac",
      -      "sender_pubkey": null,
      -      "transfer_send_block_id": 445875,
      -      "txid": "7a0a3bb7d39b89c3638abc369c85b5c028d0a55d7804ba1953ff19b0125f3c24",
      -      "value_hash": "02af0ef21161ad06b0923106f40b994b9e4c1614",
      -      "vtxindex": 16
      -    }
      -  ]
      -}
      -
      -
      - -

      All of the above information is extracted from the blockchain. Each top-level -field encodes the states the name transitioned to at the given block height (e.g. -445838, 445851, 445873, adn 445884). At each block height, the name’s zone file -hashes are returned in the order they were discovered in the blockchain.

      - -

      Each name state contains a lot of ancillary data that is used internally by -other API calls and client libraries. The relevant fields for this document’s -scope are:

      - -
        -
      • address: This is the base58check-encoded public key hash.
      • -
      • name: This is the name queried.
      • -
      • value_hash: This is the zone file hash.
      • -
      • opcode: This is the type of transaction that was processed.
      • -
      • txid: This is the transaction ID in the underlying blockchain.
      • -
      - -

      The name’s entire history is returned. This includes the history of the name -under its previous owner, if the name expired and was reregistered.

      - -

      Look up the list of names owned by a given public key hash (reference)

      - -
      $ curl https://core.blockstack.org/v1/addresses/bitcoin/16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg
      -{
      -  "names": [
      -    "judecn.id",
      -    "patrickstanley1.id",
      -    "abcdefgh123456.id",
      -    "duckduckgo_tor.id",
      -    "jude.id",
      -    "blockstacknewyear2017.id",
      -    "jude.statism.id"
      -  ]
      -}
      -
      -
      - -

      Note that this API endpoint includes names and -subdomains.

      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/naming/subdomains.html b/_site/core/naming/subdomains.html deleted file mode 100644 index ea8d246713..0000000000 --- a/_site/core/naming/subdomains.html +++ /dev/null @@ -1,715 +0,0 @@ - - - - - - - - -BNS Subdomains | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      BNS Subdomains

      - - - - - -
      - -

      This section explains BNS subdomains and provides instructions for methods -you can use to work with them. The following topics are included:

      - - - -

      Overview of subdomains

      - -

      BNS names are strongly-owned because the owner of its private key can generate -valid transactions that update its zone file hash and owner. However, this comes at the -cost of requiring a name owner to pay for the underlying transaction in the -blockchain. Moreover, this approach limits the rate of BNS name registrations -and operations to the underlying blockchain’s transaction bandwidth.

      - -

      BNS overcomes this with subdomains. A BNS subdomain is a type of BNS name whose state -and owner are stored outside of the blockchain, but whose existence and -operation history are anchored to the -blockchain. In the example table in the Resolving BNS -Names section, the names cicero.res_publica.id and -podsaveamerica.verified.podcast are subdomains.

      - -

      Like their on-chain counterparts, subdomains are globally -unique, strongly-owned, and human-readable. BNS gives them their own name state -and public keys.

      - -

      Unlike on-chain names, subdomains can be created and managed -cheaply, because they are broadcast to the -BNS network in batches. A single blockchain transaction can send up to 120 -subdomain operations.

      - -

      This is achieved by storing subdomain records in the Atlas Network. -An on-chain name owner broadcasts subdomain operations by encoding them as -TXT records within a DNS zone file. To broadcast the zone file, -the name owner sets the new zone file hash with a NAME_UPDATE transaction and -replicates the zone file via Atlas. This, in turn, replicates all subdomain -operations it contains, and anchors the set of subdomain operations to -an on-chain transaction. The BNS node’s consensus rules ensure that only -valid subdomain operations from valid NAME_UPDATE transactions will ever be -stored.

      - -

      For example, the name verified.podcast once wrote the zone file hash 247121450ca0e9af45e85a82e61cd525cd7ba023, -which is the hash of the following zone file:

      - -
      $ curl -sL https://core.blockstack.org/v1/names/verified.podcast/zonefile/247121450ca0e9af45e85a82e61cd525cd7ba023 | jq -r '.zonefile'
      -$ORIGIN verified.podcast
      -$TTL 3600
      -1yeardaily TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxeWVhcmRhaWx5CiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMXllYXJkYWlseS9oZWFkLmpzb24iCg=="
      -2dopequeens TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAyZG9wZXF1ZWVucwokVFRMIDM2MDAKX2h0dHAuX3RjcCBVUkkgMTAgMSAiaHR0cHM6Ly9waC5kb3Rwb2RjYXN0LmNvLzJkb3BlcXVlZW5zL2hlYWQuanNvbiIK"
      -10happier TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxMGhhcHBpZXIKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8xMGhhcHBpZXIvaGVhZC5qc29uIgo="
      -31thoughts TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzMXRob3VnaHRzCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzF0aG91Z2h0cy9oZWFkLmpzb24iCg=="
      -359 TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzNTkKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8zNTkvaGVhZC5qc29uIgo="
      -30for30 TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzMGZvcjMwCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzBmb3IzMC9oZWFkLmpzb24iCg=="
      -onea TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiBvbmVhCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vb25lYS9oZWFkLmpzb24iCg=="
      -10minuteteacher TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxMG1pbnV0ZXRlYWNoZXIKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8xMG1pbnV0ZXRlYWNoZXIvaGVhZC5qc29uIgo="
      -36questionsthepodcastmusical TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzNnF1ZXN0aW9uc3RoZXBvZGNhc3RtdXNpY2FsCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzZxdWVzdGlvbnN0aGVwb2RjYXN0bXVzaWNhbC9oZWFkLmpzb24iCg=="
      -_http._tcp URI 10 1 "https://dotpodcast.co/"
      -
      -
      - -

      Each TXT record in this zone file encodes a subdomain-creation. -For example, 1yeardaily.verified.podcast resolves to:

      - -
      $ curl https://core.blockstack.org/v1/names/1yeardaily.verified.podcast
      -{
      -  "address": "1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH",
      -  "blockchain": "bitcoin",
      -  "last_txid": "d87a22ebab3455b7399bfef8a41791935f94bc97aee55967edd5a87f22cce339",
      -  "status": "registered_subdomain",
      -  "zonefile_hash": "e7acc97fd42c48ed94fd4d41f674eddbee5557e3",
      -  "zonefile_txt": "$ORIGIN 1yeardaily\n$TTL 3600\n_http._tcp URI 10 1 \"https://ph.dotpodcast.co/1yeardaily/head.json\"\n"
      -}
      -
      -
      - -

      This information was extracted from the 1yeardaily TXT resource record in the zone -file for verified.podcast.

      - -

      Subdomain Lifecycle

      - -

      Note that 1yeardaily.verified.podcast has a different public key -hash (address) than verified.podcast. A BNS node will only process a -subsequent subdomain operation on 1yeardaily.verified.podcast if it includes a -signature from this address’s private key. verified.podcast cannot generate -updates; only the owner of 1yeardaily.verified.podcast can do so.

      - -

      The lifecycle of a subdomain and its operations is shown in Figure 2.

      - -
         subdomain                  subdomain                  subdomain
      -   creation                   update                     transfer
      -+----------------+         +----------------+         +----------------+
      -| cicero         |         | cicero         |         | cicero         |
      -| owner="1Et..." | signed  | owner="1Et..." | signed  | owner="1cJ..." |
      -| zf0="7e4..."   |<--------| zf0="111..."   |<--------| zf0="111..."   |<---- ...
      -| seqn=0         |         | seqn=1         |         | seqn=2         |
      -|                |         | sig="xxxx"     |         | sig="xxxx"     |
      -+----------------+         +----------------+         +----------------+
      -        |                          |                          |
      -        |        off-chain         |                          |
      -~ ~ ~ ~ | ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~|~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | ~ ~ ~ ~ ~ ~ ~ ...
      -        |         on-chain         |                          |
      -        V                          V (zone file hash    )     V
      -+----------------+         +----------------+         +----------------+
      -| res_publica.id |         |    jude.id     |         | res_publica.id |
      -|  NAME_UPDATE   |<--------|  NAME_UPDATE   |<--------|  NAME_UPDATE   |<---- ...
      -+----------------+         +----------------+         +----------------+
      -   blockchain                 blockchain                 blockchain
      -   block                      block                      block
      -
      -
      -Figure 2:  Subdomain lifetime with respect to on-chain name operations.  A new
      -subdomain operation will only be accepted if it has a later "sequence=" number,
      -and a valid signature in "sig=" over the transaction body.  The "sig=" field
      -includes both the public key and signature, and the public key must hash to
      -the previous subdomain operation's "addr=" field.
      -
      -Thesubdomain-creation and subdomain-transfer transactions for
      -"cicero.res_publica.id" are broadcast by the owner of "res_publica.id".
      -However, any on-chain name ("jude.id" in this case) can broadcast a subdomain
      -update for "cicero.res_publica.id".
      -
      -
      - -

      Subdomain operations are ordered by sequence number, starting at 0. Each new -subdomain operation must include:

      - -
        -
      • The next sequence number
      • -
      • The public key that hashes to the previous subdomain transaction’s address
      • -
      • A signature from the corresponding private key over the entire subdomain -operation.
      • -
      - -

      If two correctly-signed but conflicting subdomain operations are discovered -(i.e. they have the same sequence number), the one that occurs earlier in the -blockchain’s history is accepted. Invalid subdomain operations are ignored.

      - -

      Combined, this ensures that a BNS node with all of the zone files with a given -subdomain’s operations will be able to determine the valid sequence of -state-transitions it has undergone, and determine the current zone file and public -key hash for the subdomain.

      - -

      Resolving Subdomains

      - -

      Developers interact with subdomains the same way they interact with names. -Using the BNS API, a developer can:

      - -

      Look up a subdomain’s public key and zone file (reference)

      - -
      $ curl https://core.blockstack.org/v1/names/aaron.personal.id
      -{
      -  "address": "1PwztPFd1s2STMv4Ntq6UPBdYgHSBr5pdF",
      -  "blockchain": "bitcoin",
      -  "last_txid": "85e8273b0a38d3e9f0af7b4b72faf0907de9f4616afc101caac13e7bbc832394",
      -  "status": "registered_subdomain",
      -  "zonefile_hash": "a6dda6b74ffecf85f4a162627d8df59577243813",
      -  "zonefile_txt": "$ORIGIN aaron.personal.id\n$TTL 3600\n_https._tcp URI 10 1 \"https://gaia.blockstack.org/hub/1PwztPFd1s2STMv4Ntq6UPBdYgHSBr5pdF/profile.json\"\n"
      -}
      -
      -
      - -

      Look up a subdomain’s transaction history (reference)

      - -
      $ curl https://core.blockstack.org/v1/names/aaron.personal.id/history
      -{
      -  "509981": [
      -    {
      -      "address": "1PwztPFd1s2STMv4Ntq6UPBdYgHSBr5pdF",
      -      "block_number": 509981,
      -      "domain": "personal.id",
      -      "name": "aaron.personal.id",
      -      "sequence": 0,
      -      "txid": "85e8273b0a38d3e9f0af7b4b72faf0907de9f4616afc101caac13e7bbc832394",
      -      "value_hash": "a6dda6b74ffecf85f4a162627d8df59577243813",
      -      "zonefile": "JE9SSUdJTiBhYXJvbi5wZXJzb25hbC5pZAokVFRMIDM2MDAKX2h0dHBzLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vZ2FpYS5ibG9ja3N0YWNrLm9yZy9odWIvMVB3enRQRmQxczJTVE12NE50cTZVUEJkWWdIU0JyNXBkRi9wcm9maWxlLmpzb24iCg=="
      -    }
      -  ]
      -}
      -
      -
      - -

      Look up the list of names and subdomains owned by a given public key hash (reference)

      - -
      $ curl https://core.blockstack.org/v1/addresses/bitcoin/1PwztPFd1s2STMv4Ntq6UPBdYgHSBr5pdF
      -{
      -  "names": [
      -    "aaron.personal.id"
      -  ]
      -}
      -
      -
      - -

      Subdomain Creation and Management

      - -

      Unlike an on-chain name, a subdomain owner needs an on-chain name owner’s help -to broadcast their subdomain operations. In particular:

      -
        -
      • A subdomain-creation transaction can only be processed by the owner of the on-chain -name that shares its suffix. For example, only the owner of res_publica.id -can broadcast subdomain-creation transactions for subdomain names ending in -.res_publica.id.
      • -
      • A subdomain-transfer transaction can only be broadcast by the owner of the -on-chain name that created it. For example, the owner of -cicero.res_publica.id needs the owner of res_publica.id to broadcast a -subdomain-transfer transaction to change cicero.res_publica.id’s public key.
      • -
      • In order to send a subdomain-creation or subdomain-transfer, all -of an on-chain name owner’s zone files must be present in the Atlas network. -This lets the BNS node prove the absence of any conflicting subdomain-creation and -subdomain-transfer operations when processing new zone files.
      • -
      • A subdomain update transaction can be broadcast by any on-chain name owner, -but the subdomain owner needs to find one who will cooperate. For example, -the owner of verified.podcast can broadcast a subdomain-update transaction -created by the owner of cicero.res_publica.id.
      • -
      - -

      That said, to create a subdomain, the subdomain owner generates a -subdomain-creation operation for their desired name -and gives it to the on-chain name owner. -The on-chain name owner then uses Atlas to -broadcast it to all other BNS nodes.

      - -

      Once created, a subdomain owner can use any on-chain name owner to broadcast a -subdomain-update operation. To do so, they generate and sign the requisite -subdomain operation and give it to an on-chain name owner, who then packages it -with other subdomain operations into a DNS zone file -and sends them all out on the Atlas network.

      - -

      If the subdomain owner wants to change the address of their subdomain, they need -to sign a subdomain-transfer operation and give it to the on-chain name owner -who created the subdomain. They then package it into a zone file and broadcast -it.

      - -

      Subdomain Registrars

      - -

      Because subdomain names are cheap, developers may be inclined to run -subdomain registrars on behalf of their applications. For example, -the name personal.id is used to register Blockstack application users without -requiring them to spend any Bitcoin.

      - -

      We supply a reference -implementation of a BNS Subdomain Registrar -to help developers broadcast subdomain operations. Users would still own their -subdomain names; the registrar simply gives developers a convenient way for them -to register and manage them in the context of a particular application. -Please see the tutorial on running a subdomain registrar for -details on how to use it.

      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/naming/tutorial_creation.html b/_site/core/naming/tutorial_creation.html deleted file mode 100644 index 3ab7dc0b75..0000000000 --- a/_site/core/naming/tutorial_creation.html +++ /dev/null @@ -1,1098 +0,0 @@ - - - - - - - - -Creating a Namespace | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      Creating a Namespace

      - - - - - -
      - -

      This tutorial teaches you how to create your namespace, it contains the -following sections:

      - - - -

      Creating namespaces is expensive. -Be sure to test your namespace in our integration test -framework -first! It will let you simulate any valid namespace configuration -you want at no risk to you.

      - -
      -

      WARNING: If you intend to create a namespace, you must read this document -in its entirety. You should also install the test framework and experiment -with your namespace’s parameters. FAILURE TO DO SO MAY RESULT IN IRRECOVERABLE -LOSS OF FUNDS.

      -
      - -

      Before you begin

      - -

      Some basic familiarity with how Bitcoin works is required to -understand this tutorial. This includes:

      - -
        -
      • knowing the difference between mainnet, testnet, and regtest
      • -
      • knowing about compressed and uncompressed ECDSA public keys
      • -
      • knowing about base58-check encoding
      • -
      • knowing how Bitcoin transactions are structured
      • -
      • knowing how UTXOs work
      • -
      - -

      Creating a namespace is a three-step process. The first step is to preorder -the namespace, which broadcasts a salted hash of the namespace ID. The second -step is to reveal the namespace, which exposes the namespace ID and price -function to the blockchain. The final step is to ready the namespace, which -allows anyone to register names within it.

      - -

      In between the reveal and ready steps, the namespace creator will have a -“lock” on the namespace that lasts for about 1 year. During this time period, -the namespace creator can import names. The import transaction lets the -namespace creator assign the name a zone file and an owner in one step.

      - -

      Before Trying This in Production…

      - -

      Setting up the Test Environment

      - -

      In this example, we will use the test framework to create a private Bitcoin -blockchain on your computer, and then create a Blockstack namespace on it. -This will let you experiment with different namespace parameters -without spending actual BTC. The test framework uses bitcoind -regtest, -so all of the commands you’ll run here will work identically on -mainnet.

      - -

      To install the test framework, please follow these -instructions. -Once you have the test framework installed, you should run the namespace_check test in --interactive-web mode. -This will create an empty .test namespace and leave the test scenario running -once it finishes. You will be able to fund addresses and create new blocks via -your Web browser or via curl, as will be explained below. Also, you’ll be able to use the -blockstack utility to interact with your private blockchain and namespaces.

      - -

      The test setup command is as follows. This will launch the namespace_check -test scenario, and open a web server on port 3001.

      -
          $ blockstack-test-scenario --interactive-web 3001 blockstack_integration_tests.scenarios.namespace_check
      -
      -
      - -

      When the test is ready for us to experiment, you should see the following:

      - -
          An empty namespace called 'test' has been created
      -    Feel free to experiment with other namespaces
      -
      -    Available keys with a balance:
      -    *  6e50431b955fe73f079469b24f06480aee44e4519282686433195b3c4b5336ef01
      -    *  c244642ce0b4eb68da8e098facfcad889e3063c36a68b7951fb4c085de49df1b01
      -    *  f4c3907cb5769c28ff603c145db7fc39d7d26f69f726f8a7f995a40d3897bb5201
      -    *  8f87d1ea26d03259371675ea3bd31231b67c5df0012c205c154764a124f5b8fe01
      -    *  bb68eda988e768132bc6c7ca73a87fb9b0918e9a38d3618b74099be25f7cab7d01
      -    *  2,3,6f432642c087c2d12749284d841b02421259c4e8178f25b91542c026ae6ced6d01,65268e6267b14eb52dc1ccc500dc2624a6e37d0a98280f3275413eacb1d2915d01,cdabc10f1ff3410082448b708c0f860a948197d55fb612cb328d7a5cc07a6c8a01
      -    *  2,3,4c3ab2a0704dfd9fdc319cff2c3629b72ebda1580316c7fddf9fad1baa323e9601,75c9f091aa4f0b1544a59e0fce274fb1ac29d7f7e1cd020b66f941e5d260617b01,d62af1329e541871b244c4a3c69459e8666c40b683ffdcb504aa4adc6a559a7701
      -    *  2,3,4b396393ca030b21bc44a5eba1bb557d04be1bfe974cbebc7a2c82b4bdfba14101,d81d4ef8123852403123d416b0b4fb25bcf9fa80e12aadbc08ffde8c8084a88001,d0482fbe39abd9d9d5c7b21bb5baadb4d50188b684218429f3171da9de206bb201
      -    *  2,3,836dc3ac46fbe2bcd379d36b977969e5b6ef4127e111f2d3e2e7fb6f0ff1612e01,1528cb864588a6a5d77eda548fe81efc44180982e180ecf4c812c6be9788c76a01,9955cfdac199b8451ccd63ec5377a93df852dc97ea01afc47db7f870a402ff0501
      -
      -
      - -

      You can determine that the test framework is live by going to -http://localhost:3001 in your Web browser. From there, you can generate -blocks in the test framework’s bitcoind node and you can fund any address in -the test framework.

      - -

      Finally, you can use the blockstack-test-env command to set up your shell -environment variables so blockstack will interact with this test (instead of -mainnet). To do so, run the following in your shell:

      - -
          $ . $(which blockstack-test-env) namespace_check
      -    |blockstack-test namespace_check| $
      -
      -
      - -

      You can verify that the environment variables by verifying that your $PS1 -variable includes the name of your test (as shown above), and that some other -BLOCKSTACK_-prefixed variables are set:

      - -
          |blockstack-test namespace_check| $ env | grep BLOCKSTACK
      -    BLOCKSTACK_OLD_PS1=\u@\h:\w$
      -    BLOCKSTACK_TESTNET=1
      -    BLOCKSTACK_EPOCH_1_END_BLOCK=1
      -    BLOCKSTACK_EPOCH_2_END_BLOCK=2
      -    BLOCKSTACK_TEST=1
      -    BLOCKSTACK_DEBUG=1
      -    BLOCKSTACK_CLIENT_CONFIG=/tmp/blockstack-run-scenario.blockstack_integration_tests.scenarios.namespace_check/client/client.ini
      -
      -
      - -

      Registering a Namespace

      - -

      Suppose we’re going to create the hello namespace. The key -6e50431b955fe73f079469b24f06480aee44e4519282686433195b3c4b5336ef01 will be the key that -pays for the namespace. The key -c244642ce0b4eb68da8e098facfcad889e3063c36a68b7951fb4c085de49df1b01 will be the key that -creates the namespace. The creator key will be used to import names and -declare the namespace ready. The payment key will be used to both pay for the -namespace and receive name registration and renewal fees for the first year of -the namespace’s lifetime.

      - -

      In this example, we will set these keys as environment variables:

      - -
          |blockstack-test namespace_check| $ export PAYMENT_PKEY="6e50431b955fe73f079469b24f06480aee44e4519282686433195b3c4b5336ef01"
      -    |blockstack-test namespace_check| $ export CREATOR_PKEY="c244642ce0b4eb68da8e098facfcad889e3063c36a68b7951fb4c085de49df1b01"
      -
      -
      - -

      Multisig Namespace Payment

      - -

      If you want to use a multisig address to pay for your namespace (and collect -name registration fees), then instead of using -6e50431b955fe73f079469b24f06480aee44e4519282686433195b3c4b5336ef01, you should -use a string formatted as m,n,pk1,pk2,...,pk_n. m is the number of -signatures required, n is the number of private keys, and pk1,pk2,...,pk_n -are the private keys.

      - -

      For example, you can use the following as your PAYMENT_PKEY to have a 2-of-3 -multisig script pay for your namespace and collect name registration fees:

      - -
          |blockstack-test namespace_check| $ export PAYMENT_PKEY="2,3,6f432642c087c2d12749284d841b02421259c4e8178f25b91542c026ae6ced6d01,65268e6267b14eb52dc1ccc500dc2624a6e37d0a98280f3275413eacb1d2915d01,cdabc10f1ff3410082448b708c0f860a948197d55fb612cb328d7a5cc07a6c8a01"
      -
      -
      - -

      Namespace preorder

      - -

      The command to preorder the namespace would be:

      - -
          |blockstack-test namespace_check| $ blockstack namespace_preorder hello "$PAYMENT_PKEY" "$CREATOR_PKEY"
      -
      -
      - -

      You will be given a set of instructions on how to proceed to reveal and -launch the namespace. READ THEM CAREFULLY. You will be prompted to -explicitly acknowledge that you understand the main points of the instructions, -and that you understand the risks.

      - -

      The command outputs some necessary information at the very end of its execution. -In particular, you will need to remember the transaction ID of the namespace -preorder. The command will help you do so.

      - -

      Here is a sample output:

      - -
          |blockstack-test namespace_check| $ blockstack namespace_preorder hello "$PAYMENT_PKEY" "$CREATOR_PKEY"
      -
      -    <...snip...>
      -
      -    Remember this transaction ID: b40dd1375ef63e5a40ee60d790ec6dccd06efcbac99d0cd5f3b07502a4ab05ac
      -    You will need it for `blockstack namespace_reveal`
      -
      -    Wait until b40dd1375ef63e5a40ee60d790ec6dccd06efcbac99d0cd5f3b07502a4ab05ac has six (6) confirmations.  Then, you can reveal `hello` with:
      -
      -        $ blockstack namespace_reveal "hello" "6e50431b955fe73f079469b24f06480aee44e4519282686433195b3c4b5336ef01" "c244642ce0b4eb68da8e098facfcad889e3063c36a68b7951fb4c085de49df1b01" "b40dd1375ef63e5a40ee60d790ec6dccd06efcbac99d0cd5f3b07502a4ab05ac"
      -
      -    {
      -        "status": true,
      -        "success": true,
      -        "transaction_hash": "b40dd1375ef63e5a40ee60d790ec6dccd06efcbac99d0cd5f3b07502a4ab05ac"
      -    }
      -
      -
      - -

      If all goes well, you will get back a transaction hash (in this case, b40dd1375ef63e5a40ee60d790ec6dccd06efcbac99d0cd5f3b07502a4ab05ac). -To get Blockstack to process it, you will need to mine some blocks in the test framework (by default, -Blockstack will only accept a transaction that has 6 confirmations). To do -this, simply go to http://localhost:3001 and generate at least 6 blocks. If you -observe the test log, you will see the Blockstack node process and accept it.

      - -

      Note that when you do this live, you should wait for -at least 10 confirmations before sending the reveal transaction, just to be -safe.

      - -

      Namespace reveal

      - -

      The command to reveal a preordered namespace is more complicated, since it -describes the price curve.

      - -

      This command is interactive. The command to invoke it is as follows:

      - -
          |blockstack-test namespace_check| $ blockstack namespace_reveal hello "$PAYMENT_PKEY" "$CREATOR_PKEY" "b40dd1375ef63e5a40ee60d790ec6dccd06efcbac99d0cd5f3b07502a4ab05ac"
      -
      -
      - -

      When running the command, you will see the namespace creation wizard prompt you -with the price curve and the current values:

      - -
      Name lifetimes (blocks): infinite
      -Price coefficient:       4
      -Price base:              4
      -Price bucket exponents:  [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
      -Non-alpha discount:      2
      -No-vowel discount:       5
      -Burn or receive fees?    Receive to mr6nrMvvh44sR5MiX929mMXP5hqgaTr6fx
      -
      -Name price formula:
      -(UNIT_COST = 10.0 satoshi):
      -                                     buckets[min(len(name)-1, 15)]
      -             UNIT_COST * coeff * base
      -cost(name) = -----------------------------------------------------
      -                   max(nonalpha_discount, no_vowel_discount)
      -
      -
      -Name price table:
      -| length | price       | price, nonalpha | price, no vowel | price, both |
      ---------------------------------------------------------------------------
      -|      1 | 42949672960 |      8589934592 |      8589934592 |  8589934592 |
      -|      2 | 10737418240 |      5368709120 |      2147483648 |  2147483648 |
      -|      3 |  2684354560 |      1342177280 |       536870912 |   536870912 |
      -|      4 |   671088640 |       335544320 |       134217728 |   134217728 |
      -|      5 |   167772160 |        83886080 |        33554432 |    33554432 |
      -|      6 |    41943040 |        20971520 |         8388608 |     8388608 |
      -|      7 |    10485760 |         5242880 |         2097152 |     2097152 |
      -|      8 |     2621440 |         1310720 |          524288 |      524288 |
      -|      9 |      655360 |          327680 |          131072 |      131072 |
      -|     10 |      163840 |           81920 |           32768 |       32768 |
      -|     11 |       40960 |           20480 |            8192 |        8192 |
      -|     12 |       10240 |            5120 |            2048 |        2048 |
      -|     13 |        2560 |            1280 |             512 |         512 |
      -|     14 |         640 |             320 |             128 |         128 |
      -|     15 |         160 |              80 |              32 |          32 |
      -|    16+ |          40 |              20 |              10 |          10 |
      -
      -
      -What would you like to do?
      -(0) Set name lifetime in blocks             (positive integer between 1 and 4294967295, or "infinite")
      -(1) Set price coefficient                   (positive integer between 1 and 255)
      -(2) Set base price                          (positive integer between 1 and 255)
      -(3) Set price bucket exponents              (16 comma-separated integers, each between 1 and 15)
      -(4) Set non-alphanumeric character discount (positive integer between 1 and 15)
      -(5) Set no-vowel discount                   (positive integer between 1 and 15)
      -(6) Toggle collecting name fees             (True: receive fees; False: burn fees)
      -(7) Show name price formula
      -(8) Show price table
      -(9) Done
      -
      -(1-9)
      -
      -
      - -

      All prices are in the “fundamental unit” of the underlying blockchain (i.e. -satoshis).

      - -

      As the formula describes, the name’s price is a function of:

      - -
        -
      • a fixed unit cost (UNIT_COST)
      • -
      • a multiplicative constant coefficient (coeff)
      • -
      • a fixed exponential base (base)
      • -
      • a 16-element list of price buckets, indexed by the length of the name (buckets)
      • -
      • a discount for having non-alphnumeric letters (nonalpha_discount)
      • -
      • a discount for having no vowels in the name (no_vowel_discount)
      • -
      - -

      You can use options 1 through 8 to play with the pricing function and examine -the name costs in the price table. Enter 9 to send the transaction itself.

      - -

      Once you’re happy, you can issue the namespace-reveal transaction. As with the -namespace-preorder transaction, you will get back a transaction hash, and your transaction will be -unconfirmed. Simply go to http://localhost:3001 to generate some more blocks -to confirm your namespace-reveal.

      - -

      Once you have confirmed your namespace-reveal transaction, you can -begin to populate your namespace with some initial names.

      - -

      Collecting Name Fees

      - -

      Blockstack 0.17 introduced the ability to create a namespace such that for the -first year of its existence (54595 blocks), all name registration and renewal -fees will be sent to the address of the payment key. In this example, -this is the address mr6nrMvvh44sR5MiX929mMXP5hqgaTr6fx.

      - -

      The alternative is to -have all namespace fees sent to an unspendable burn address -(1111111111111111111114oLvT2). This is the case for the .id namespace, -for example.

      - -

      After the year has passed, all future name registrations and renewal fees -will be sent to the unspendable burn address. This is to disincentivize -namespace squatters.

      - -

      Warnings

      - -
        -
      • You must issue this command within 144 blocks of the namespace-preorder transaction. Otherwise, the preorder will expire and you will need to start over from scratch.
      • -
      - -

      Importing names

      - -

      After sending the reveal transaction, you can populate your namespace with -some initial names. You can do so with the name_import command.

      - -

      Suppose we want to import the name example.hello and assign it to an owner -whose public key address is ms6Y32bcL5zhA57e8tf7awgVZuPxV8Xg8N. Suppose also -that you wanted to give example.hello an initial zone file stored at -/var/blockstack/zone_files/example.hello. To do so, you would issue the -following command:

      - -
          |blockstack-test namespace_check| $ blockstack name_import example.hello ms6Y32bcL5zhA57e8tf7awgVZuPxV8Xg8N /var/blockstack/zone_files/example.hello "$CREATOR_PKEY"
      -
      -
      - -

      By default, you must use the private key you used to reveal the namespace -to import names (this is $CREATOR_PKEY in this example).

      - -

      As with namespace-preorder and namespace-reveal, the transaction this command -generates will be unconfirmed. Simply go to http://localhost:3001 to generate -some blocks to confirm it.

      - -

      You can check the progress of the transaction with blockstack info, as follows:

      - -
          |blockstack-test namespace_check| $ blockstack info
      -    {
      -        "cli_version": "0.17.0.8",
      -        "consensus_hash": "b10fdd38a20a7e46555ce3a7f68cf95c",
      -        "last_block_processed": 694,
      -        "last_block_seen": 694,
      -        "queues": {
      -            "name_import": [
      -                {
      -                    "confirmations": 1,
      -                    "name": "example.hello",
      -                    "tx_hash": "10f7dcd9d6963ef5d20d010f731d5d2ddb76163a083b9d7a2b9fd4515c7fe58c"
      -                }
      -            ]
      -        },
      -        "server_alive": true,
      -        "server_host": "localhost",
      -        "server_port": 16264,
      -        "server_version": "0.17.0.8"
      -    }
      -
      -
      - -

      The confirmation field indicates how deep in the blockchain the transaction is -at the time. Generating more blocks will increase its number of confirmations.

      - -

      When you do this live, -YOU SHOULD LEAVE YOUR COMPUTER RUNNING UNTIL THE name_import QUEUE IS EMPTY. -Blockstack’s background API daemon will monitor the transactions and upload the -name’s zone file to the Blockstack Atlas network once it is confirmed. -But to do so, your computer must remain online. If you do not do this, then -the name will not have a zone file and will be unusable in the higher layers of -Blockstack-powered software (including Blockstack applications). However, -if your computer does go offline or reboots, you can recover by -restarting the Blockstack API daemon (with -blockstack api start). The daemon itself will pick up where it left off, and -replicate all zone files that have confirmed transactions.

      - -

      After the zone file is uploaded, the name will be public and resolvable. You can re-import the -same name over and over, and give it a different address and/or zone file. Like -all other names, the Blockstack Atlas network will accept and propagate zone -files for imported names.

      - -

      The owner of the address ms6Y32bcL5zhA57e8tf7awgVZuPxV8Xg8N will not be -able to issue any transactions for the name example.hello until the namespace -creator has sent the ready transaction.

      - -

      Using multiple private keys for NAME_IMPORT

      - -

      Bitcoin itself imposes limits on how fast you can send transactions from the -same key (limited by a maximum UTXO-chain length). To work around this, -Blockstack lets you import names by using up to 300 private keys. The private -keys you can use are BIP32 unhardened children of the namespace reveal key (i.e. -$CREATOR_PKEY in this example).

      - -

      The first name you import must use the namespace reveal private key -($CREATOR_PKEY in this example). However, all future names you import in this -namespace can use one of the 300 BIP32 keys.

      - -

      To get the list of keys you can use, you can use the make_import_keys command:

      - -
          |blockstack-test namespace_check| $ blockstack make_import_keys example hello "$CREATOR_PKEY"
      -    aeda50305ada40aaf53f2d8921aa717f1ec71a0a3b9b4c6397b3877f6d45c46501 (n4DVTuLLv5J1Yc17AoRYY1GtxDAuLGAESr)
      -    92ff179901819a1ec7d32997ce3bb0d9a913895d5850cc05146722847128549201 (mib2KNBGR4az8GiUmusBZexVBqb9YB2gm5)
      -    cc5b6a454e2b614bfa18f4deb9a8e179ab985634d63b7fedfaa59573472d209b01 (mxE2iqV4jdpn4K349Gy424TvZp6MPqSXve)
      -    9b0265e0ac8c3c24fe1d79a734b3661ec2b5c0c2619bb6342356572b8235910101 (n4rGz8hkXTscUGWCwZvahrkEh6LHZVQUoa)
      -    e2585af250404b7918cf6c91c6fa67f3401c0d1ae66df2fafa8fa132f4b9350f01 (moGNpEpighqc6FnkqyNVJA9xtfTiStr5YU)
      -    {
      -        "status": true
      -    }
      -
      -
      - -

      (NOTE: in the test environment, you get only 5 keys in order to save time).

      - -

      You can use any of these keys to import names.

      - -

      Trying it out

      - -

      Here’s an example walkthrough of how to try this out in the test framework:

      - -
        -
      1. Import the first name, creating a zone file in the process:
      2. -
      - -
          |blockstack-test namespace_check| $ cat > /var/blockstack/zone_files/example.hello <<EOF
      -    > $ORIGIN example.hello
      -    > $TTL 3600
      -    > _file URI 10 1 "file:///home/blockstack-test/example.hello"
      -    > EOF
      -    |blockstack-test namespace_check| $ blockstack name_import example.hello ms6Y32bcL5zhA57e8tf7awgVZuPxV8Xg8N /var/blockstack/zone_files/example.hello "$CREATOR_PKEY"
      -    Import cost breakdown:
      -    {
      -        "name_import_tx_fee": {
      -            "btc": 0.0003342,
      -            "satoshis": 33420
      -        },
      -        "total_estimated_cost": {
      -            "btc": 0.0003342,
      -            "satoshis": 33420
      -        },
      -        "total_tx_fees": 33420
      -    }
      -    Importing name 'example.hello' to be owned by 'ms6Y32bcL5zhA57e8tf7awgVZuPxV8Xg8N' with zone file hash '05c302430f4ed0a24470abf9df7e264d517fd389'
      -    Proceed? (y/N) y
      -    {
      -        "status": true,
      -        "success": true,
      -        "transaction_hash": "bd875f00f63bcb718bb22782c88c3edcbed79663f2f9152deab328c48746f103",
      -        "value_hash": "05c302430f4ed0a24470abf9df7e264d517fd389"
      -    }
      -
      -
      - -
        -
      1. Advance the test framework blockchain, so the indexer knows which import keys to expect:
      2. -
      - -
          # NOTE: you can also do this by going to http://localhost:3001 in your Web browser
      -    |blockstack-test namespace_check| $ curl -X POST http://localhost:3001/nextblock
      -
      -
      - -
        -
      1. Make import keys:
      2. -
      - -
          |blockstack-test namespace_check| $ blocksatck make_import_keys hello "$CREATOR_PKEY"
      -    aeda50305ada40aaf53f2d8921aa717f1ec71a0a3b9b4c6397b3877f6d45c46501 (n4DVTuLLv5J1Yc17AoRYY1GtxDAuLGAESr)
      -    92ff179901819a1ec7d32997ce3bb0d9a913895d5850cc05146722847128549201 (mib2KNBGR4az8GiUmusBZexVBqb9YB2gm5)
      -    cc5b6a454e2b614bfa18f4deb9a8e179ab985634d63b7fedfaa59573472d209b01 (mxE2iqV4jdpn4K349Gy424TvZp6MPqSXve)
      -    9b0265e0ac8c3c24fe1d79a734b3661ec2b5c0c2619bb6342356572b8235910101 (n4rGz8hkXTscUGWCwZvahrkEh6LHZVQUoa)
      -    e2585af250404b7918cf6c91c6fa67f3401c0d1ae66df2fafa8fa132f4b9350f01 (moGNpEpighqc6FnkqyNVJA9xtfTiStr5YU)
      -    {
      -        "status": true
      -    }
      -
      -
      - -
        -
      1. Fill up one of the addresses in the test framework, so we can fund NAME_IMPORT transactions with it:
      2. -
      - -
          # NOTE: you can also do this by going to http://localhost:3001 in your Web browser
      -    |blockstack-test namespace_check| $ curl -X POST -F 'addr=n4DVTuLLv5J1Yc17AoRYY1GtxDAuLGAESr' -F 'value=100000000' 'http://localhost:3001/sendfunds'
      -
      -
      - -
        -
      1. Import another name, with the child private key we just funded:
      2. -
      - -
          |blockstack-test namespace_check| $ cat > /tmp/example.hello.zonefile <<EOF
      -    > $ORIGIN example2.hello
      -    > $TTL 3600
      -    > _file URI 10 1 "file:///home/blockstack-test/example2.hello"
      -    > EOF
      -    |blockstack-test namespace_check| $ blockstack name_import example2.hello n3sFkNfBQPWS25G12DqDEqHRPiqHotAkEb /tmp/example.hello.zonefile aeda50305ada40aaf53f2d8921aa717f1ec71a0a3b9b4c6397b3877f6d45c46501
      -    Import cost breakdown:
      -    {
      -        "name_import_tx_fee": {
      -            "btc": 0.0003342,
      -            "satoshis": 33420
      -        },
      -        "total_estimated_cost": {
      -            "btc": 0.0003342,
      -            "satoshis": 33420
      -        },
      -        "total_tx_fees": 33420
      -    }
      -    Importing name 'example2.hello' to be owned by 'n3sFkNfBQPWS25G12DqDEqHRPiqHotAkEb' with zone file hash '0649bc0b457f54c564d054ce20dc3745a0c4f0c0'
      -    Proceed? (y/N) y
      -    {
      -        "status": true,
      -        "success": true,
      -        "transaction_hash": "496a6c2aaccedd98a8403c2e61ff3bdeff221a58bf0e9c362fcae981353f459f",
      -        "value_hash": "0649bc0b457f54c564d054ce20dc3745a0c4f0c0"
      -    }
      -
      -
      - -
        -
      1. Advance the blockchain again:
      2. -
      - -
          # NOTE: you can also do this by going to http://localhost:3001 in your Web browser
      -    |blockstack-test namespace_check| $ curl -X POST http://localhost:3001/nextblock
      -
      -
      - -
        -
      1. See that the names are processing:
      2. -
      - -
          |blockstack-test namespace_check| $ blockstack info
      -    {
      -        "cli_version": "0.17.0.8",
      -        "consensus_hash": "2a055beeaedcaa1365ab2671a0254a03",
      -        "last_block_processed": 711,
      -        "last_block_seen": 711,
      -        "queues": {
      -            "name_import": [
      -                {
      -                    "confirmations": 2,
      -                    "name": "example.hello",
      -                    "tx_hash": "bd875f00f63bcb718bb22782c88c3edcbed79663f2f9152deab328c48746f103",
      -                },
      -                {
      -                    "confirmations": 1,
      -                    "name": "example2.hello",
      -                    "tx_hash": "496a6c2aaccedd98a8403c2e61ff3bdeff221a58bf0e9c362fcae981353f459f"
      -                }
      -            ]
      -        },
      -        "server_alive": true,
      -        "server_host": "localhost",
      -        "server_port": 16264,
      -        "server_version": "0.17.0.8"
      -    }
      -
      -
      - -
        -
      1. Confirm all the transactions:
      2. -
      - -
          # NOTE: you can also do this by going to http://localhost:3001 in your Web browser
      -    |blockstack-test namespace_check| $ for i in $(seq 1 10); do curl -X POST http://localhost:3001/nextblock
      -
      -
      - -
        -
      1. Look up name zone files to confirm they were replicated to the test framework’s Atlas network:
      2. -
      - -
          |blockstack-test namespace_check| $ blockstack info
      -    {
      -        "cli_version": "0.17.0.8",
      -        "consensus_hash": "ad247c1d5ff239a65db0736951078f17",
      -        "last_block_processed": 721,
      -        "last_block_seen": 721,
      -        "queues": {},
      -        "server_alive": true,
      -        "server_host": "localhost",
      -        "server_port": 16264,
      -        "server_version": "0.17.0.8"
      -    }
      -    |blockstack-test namespace_check| $ blockstack get_name_zonefile example.hello
      -    $ORIGIN example.hello
      -    $TTL 3600
      -    _file URI 10 1 "file:///home/blockstack-test/example.hello"
      -
      -    |blockstack-test namespace_check| $ blockstack get_name_zonefile example2.hello
      -    $ORIGIN example2.hello
      -    $TTL 3600
      -    _file URI 10 1 "file:///home/blockstack-test/example2.hello"
      -
      -
      - -

      Now, these names are imported and once the NAMESPACE_READY transaction is -sent, the name owners can proceed to issue name operations.

      - -

      Warnings

      - -
        -
      • The first private key you use must be the same one you used to create the namespace ($CREATOR_KEY).
      • -
      • You may only use the 300 private keys described above to import names.
      • -
      • You must complete all NAME_IMPORT transactions within 52595 blocks of the NAMESPACE_REVEAL transaction (about 1 year).
      • -
      - -

      Launching a Namespace

      - -

      Once you have pre-populated your namespace with all of the initial names, you -have to make it ready so anyone can register a name. If you do not do this -within 1 year of the reveal transaction, then your namespace and all of the -names will disappear, and someone else will be able to register it.

      - -

      To make a namespace ready, you use the creator private key as follows:

      - -
           |blockstack-test namespace_check| $ blockstack namespace_ready hello "$CREATOR_PKEY"
      -
      -
      - -

      Warnings

      - -
        -
      • You must send the NAMESPACE_READY transaction within 52595 blocks (about 1 year) of the NAMESPACE_REVEAL transaction.
      • -
      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/naming/tutorial_subdomains.html b/_site/core/naming/tutorial_subdomains.html deleted file mode 100644 index d9999244d8..0000000000 --- a/_site/core/naming/tutorial_subdomains.html +++ /dev/null @@ -1,792 +0,0 @@ - - - - - - - - -Subdomain Design and Implementation | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      Subdomain Design and Implementation

      - - - - - -
      - -

      Subdomains allow us to provide names to end users cheaply (and quickly). This -tutorial explains you how to create, register, and run a subdomain register, it -contains the following sections:

      - - - -

      Strong subdomain ownership

      - -

      For those who are new to this concept, it’s a model where domains can -permanently, cryptographically delegate subdomains to particular keys, -relinquishing their ability to revoke the names or change the name -resolution details.

      - -

      These names will be indicated with an ., e.g., foo.bar.id

      - -

      Overall Design

      - -

      We can do this today with a special indexer & resolver endpoint and -without any changes to the core protocol.

      - -

      We can do this by having a zone file record for each subdomain i -containing the following information:

      - -
        -
      1. An owner address addr
      2. -
      3. A sequence number N
      4. -
      5. A zonefile
      6. -
      7. A signature S of the above
      8. -
      - -

      The signature S_i must be verifiable with the address in the -(N-1)th entry for subdomain i.

      - -

      Zonefile Format

      - -

      For now, the resolver will use an TXT record per subdomain to define -this information. The entry name will be $(subdomain).

      - -

      We’ll use the format of RFC 1464 -for the TXT entry. We’ll have the following strings with identifiers:

      - -
        -
      1. parts : this specifies the number of pieces that the -zonefile has been chopped into. TXT strings can only be 255 bytes, -so we chop up the zonefile.
      2. -
      3. zf{n}: part n of the zonefile, base64 encoded
      4. -
      5. owner: the owner address delegated to operate the subdomain
      6. -
      7. seqn: the sequence number
      8. -
      9. sig: signature of the above data.
      10. -
      - -
      $ORIGIN bar.id
      -$TTL 3600
      -pubkey TXT "pubkey:data:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
      -registrar URI 10 1 "bsreg://foo.com:8234"
      -aaron TXT "owner=33VvhhSQsYQyCVE2VzG3EHa9gfRCpboqHy" "seqn=0" "parts=1" "zf0=JE9SSUdJTiBhYXJvbgokVFRMIDM2MDAKbWFpbiBVUkkgMSAxICJwdWJrZXk6ZGF0YTowMzAyYWRlNTdlNjNiMzc1NDRmOGQ5Nzk4NjJhNDlkMDBkYmNlMDdmMjkzYmJlYjJhZWNmZTI5OTkxYTg3Mzk4YjgiCg=="
      -
      -
      - -

      The registrar entry indicates how to contact the registrar service -for clients of the domain wishing to register or modify their entry.

      - -

      Operations per Zonefile

      - -

      At 4kb zonefile size, we can only fit around 20 updates per zonefile.

      - -

      Domain Operator Endpoint

      - -

      The directory subdomain_registrar/ contains our code for running a -subdomain registrar. It can be executed by running:

      - -
      $ blockstack-subdomain-registrar start foo.id
      -
      -
      - -

      Here, foo.id is the domain for which subdomains will be associated.

      - -

      Configuration and Registration Files

      - -

      Configuration of the subdomain registrar is done through ~/.blockstack_subdomains/config.ini

      - -

      The sqlite database which stores the registrations is located alongside the config ~/.blockstack_subdomains/registrar.db.

      - -

      You can change the location of the config file (and the database), by setting the environment variable BLOCKSTACK_SUBDOMAIN_CONFIG

      - -

      Register Subdomain

      - -

      Subdomain registrations can be submitted to this endpoint using a REST -API.

      - -
      POST /register
      -
      -
      - -

      The schema for registration is:

      - -
      {
      -        'type' : 'object',
      -        'properties' : {
      -            'name' : {
      -                'type': 'string',
      -                'pattern': '([a-z0-9\-_+]{3,36})$'
      -            },
      -            'owner_address' : {
      -                'type': 'string',
      -                'pattern': schemas.OP_ADDRESS_PATTERN
      -            },
      -            'zonefile' : {
      -                'type' : 'string',
      -                'maxLength' : blockstack_constants.RPC_MAX_ZONEFILE_LEN
      -            }
      -        },
      -        'required':[
      -            'name', 'owner_address', 'zonefile'
      -        ],
      -        'additionalProperties' : True
      -}
      -
      -
      - -

      The registrar will:

      - -
        -
      1. Check if the subdomain foo exists already on the domain.
      2. -
      3. Add the subdomain to the queue.
      4. -
      - -

      On success, this returns 202 and the message

      - -
      {"status": "true", "message": "Subdomain registration queued."}
      -
      -
      - -

      When the registrar wakes up to prepare a transaction, it packs the queued -registrations together and issues an UPDATE.

      - -

      Check subdomain registration status

      - -

      A user can check on the registration status of their name via querying the -registrar.

      - -

      This is an API call:

      -
      GET /status/{subdomain}
      -
      -
      - -

      The registrar checks if the subdomain has propagated (i.e., the -registration is completed), in which case the following is returned:

      - -
      {"status": "Subdomain already propagated"}
      -
      -
      - -

      Or, if the subdomain has already been submitted in a transaction:

      - -
      {"status": "Your subdomain was registered in transaction 09a40d6ea362608c68da6e1ebeb3210367abf7aa39ece5fd57fd63d269336399 -- it should propagate on the network once it has 6 confirmations."}
      -
      -
      - -

      If the subdomain still hasn’t been submitted yet:

      - -
      {"status": "Subdomain is queued for update and should be announced within the next few blocks."}
      -
      -
      - -

      If an error occurred trying to submit the UPDATE transaction, this endpoint will return an error -message in the "error" key of a JSON object.

      - -

      Updating Entries

      - -

      The subdomain registrar does not currently support updating subdomain entries.

      - -

      Resolver Behavior

      - -

      When a lookup like foo.bar.id hits the resolver, the resolver will need to:

      - -
        -
      1. Lookup the zonefile history of bar.id
      2. -
      3. Fetch all these zonefiles and filter by operations on foo
      4. -
      5. Verify that all foo operations are correct
      6. -
      7. Return the latest record for foo
      8. -
      9. Do a profile lookup for foo.bar.id by fetching the URLs in the entry. -Note, this spec does not define a priority order for fetching those URLs.
      10. -
      - -

      Supported Core / Resolver Endpoints

      - -

      Generally, domain endpoints are not aware of subdomains (only endpoints -aware of subdomains is /v1/users/<foo.bar.tld>, -/v1/names/<foo.bar.tld>, and /v1/addresses/bitcoin/<foo.bar.tld>) -The endpoints which are subdomain aware are marked as such in -[api-specs.md], the cli command blockstack lookup is subdomain -aware.

      - -

      This means that search is not yet supported.

      - -

      The lookups work just like normal – it returns the user’s -profile object:

      - -
      $ curl -H "Authorization: bearer blockstack_integration_test_api_password" -H "Origin: http://localhost:3000" http://localhost:16268/v1/users/bar.foo.id -v -s | python -m json.tool
      -*   Trying 127.0.0.1...
      -* Connected to localhost (127.0.0.1) port 16268 (#0)
      -> GET /v1/users/bar.foo.id HTTP/1.1
      -> Host: localhost:16268
      -> User-Agent: curl/7.50.1
      -> Accept: */*
      -> Authorization: bearer blockstack_integration_test_api_password
      -> Origin: http://localhost:3000
      ->
      -* HTTP 1.0, assume close after body
      -< HTTP/1.0 200 OK
      -< Server: SimpleHTTP/0.6 Python/2.7.12+
      -< Date: Thu, 03 Aug 2017 14:39:16 GMT
      -< content-type: application/json
      -< Access-Control-Allow-Origin: *
      -<
      -{ [66 bytes data]
      -* Closing connection 0
      -{
      -    "bar": {
      -        "@type": "Person",
      -        "description": "Lorem Ipsum Bazorem"
      -    }
      -}
      -
      -
      - -

      Name info lookups are also supported (this should enable authenticating logins -with blockstack.js, but I will need to double check).

      - -
      $ curl -H "Authorization: bearer XXXX" -H "Origin: http://localhost:3000" http://localhost:6270/v1/names/created_equal.self_evident_truth.id -s | python -m json.tool
      -{
      -    "address": "1AYddAnfHbw6bPNvnsQFFrEuUdhMhf2XG9",
      -    "blockchain": "bitcoin",
      -    "expire_block": -1,
      -    "last_txid": "0bacfd5a3e0ec68723d5948d6c1a04ad0de1378c872d45fa2276ebbd7be230f7",
      -    "satus": "registered_subdomain",
      -    "zonefile_hash": "48fc1b351ce81cf0a9fd9b4eae7a3f80e93c0451",
      -    "zonefile_txt": "$ORIGIN created_equal\n$TTL 3600\n_https._tcp URI 10 1 \"https://www.cs.princeton.edu/~ablankst/created_equal.json\"\n_file URI 10 1 \"file:///tmp/created_equal.json\"\n"
      -}
      -
      -
      - -

      Subdomain Caching

      - -

      A resolver caches a subdomain’s state by keeping a database of all -the current subdomain records. This database is automatically updated -when a new zonefile for a particularly domain is seen by the resolver -(this is performed lazily).

      - -

      Testing Subdomain Registrar and Resolution

      - -

      You can run a subdomain registrar and resolver with blockstack-core in -regtest mode as follows:

      - -
      IMAGE=$(docker run -dt -p 3000:3000 -p 6270:6270 -p 16269:16269 -p 18332:18332 -e BLOCKSTACK_TEST_CLIENT_RPC_PORT=6270 -e BLOCKSTACK_TEST_CLIENT_BIND=0.0.0.0 -e BLOCKSTACK_TEST_BITCOIND_ALLOWIP=172.17.0.0/16 quay.io/blockstack/integrationtests:master blockstack-test-scenario --interactive 2 blockstack_integration_tests.scenarios.browser_env)
      -
      -
      - -

      Once you see Test finished; doing checks in that container’s logs, the -registrar has started and is ready to accept requests. (We recommend -following the docker instructions below for running this test in -Docker, as it will fetch the source code for the registrar and set the -correct environment variables for it to run).

      - -

      Once this environment has started, you can issue a registration request from curl:

      - -
      curl -X POST -H 'Content-Type: application/json' --data '{"zonefile": "$ORIGIN baz\n$TTL 3600\n_file URI 10 1 \"file:///tmp/baz.profile.json\"\n", "name": "baz", "owner_address": "14x2EMRz1gf16UzGbxZh2c6sJg4A8wcHLD"}' http://localhost:3000/register/
      -
      -
      - -

      This registers baz.foo.id – you can check the registrar’s status with

      - -
      curl http://localhost:3000/status/baz
      -
      -
      - -

      The API endpoints /v1/users/<foo.bar.tld>, -/v1/names/<foo.bar.tld>, and /v1/addresses/bitcoin/<foo.bar.tld> all work, so if you query the core API, you’ll get a response.

      - -

      For example:

      - -
      curl http://localhost:6270/v1/names/baz.foo.id | python -m json.tool
      -
      -
      - -

      Will return:

      -
      {
      -    "address": "1Nup2UcbVuVoDZeZCtR4vjSkrvTi8toTqc",
      -    "blockchain": "bitcoin",
      -    "expire_block": -1,
      -    "last_txid": "43bbcbd8793cdc52f1b0bd2713ed136f4f104a683a9fd5c89911a57a8c4b28b6",
      -    "satus": "registered_subdomain",
      -    "zonefile_hash": "e7e3aada18c9ac5189f1c54089e987f58c0fa51e",
      -    "zonefile_txt": "$ORIGIN bar\n$TTL 3600\n_file URI 10 1 \"file:///tmp/baz.profile.json\"\n"
      -}
      -
      -
      - -

      Running an interactive testing environment with the Subdomain Registrar service

      - -

      Follow the instructions here to download the regtesting Docker image.

      - -

      Since the subdomain registrar service runs on port 3000, we need to do two things to expose this endpoint to interact with it from the browser:

      -
        -
      • Open port 3000 with -p 3000:3000
      • -
      - -

      Here’s the full command you’d run to start the interactive testing scenario:

      - -
      IMAGE=$(docker run -dt -p 3000:3000 -p 6270:6270 -p 16269:16269 -p 18332:18332 -e BLOCKSTACK_TEST_CLIENT_RPC_PORT=6270 -e BLOCKSTACK_TEST_CLIENT_BIND=0.0.0.0 -e BLOCKSTACK_TEST_BITCOIND_ALLOWIP=172.17.0.0/16 quay.io/blockstack/integrationtests:master blockstack-test-scenario --interactive 2 blockstack_integration_tests.scenarios.browser_env)
      -
      -
      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/core/resolver.md b/_site/core/resolver.md deleted file mode 100644 index 50b00abb46..0000000000 --- a/_site/core/resolver.md +++ /dev/null @@ -1,33 +0,0 @@ -# Blockstack Resolver - -During 2014-2016, Bockstack resolver was a separate service (like DNS resolvers). -It was merged into the Blockstack API in early 2017. - -The following (legacy) API call is still being supported by the Blockstack API: - -``` -http://localhost:5000/v2/users/fredwilson -``` - -And you can see a legacy resolver in action at http://resolver.onename.com/v2/users/fredwilson - -## Cron Job for Namespaces - -**Note: the instructions below need updating.** - -Currently, the resolver indexes all valid names in a local file which can be -populated by running -> $ ./refresh_names.sh - -On a production deployment, you should add a crond job to periodically run this -script. You can edit your crontab file by: -> $ crontab -e - -Here is a sample crontab file that runs the refresh script every two hours: -``` -SHELL=/bin/bash -HOME=/home/ubuntu - -#This is a comment -0 */2 * * * /home/ubuntu/resolver/resolver/refresh_names.sh -``` diff --git a/_site/core/search.md b/_site/core/search.md deleted file mode 100644 index 73b480686b..0000000000 --- a/_site/core/search.md +++ /dev/null @@ -1,184 +0,0 @@ -# Search - -The search subsystem for Blockstack Core creates an index for data associated -with registered names in namespaces and makes that data searchable. - -The search subsystem is currently meant to index the .id namespace but can -be easily expanded to include other namespaces. - -Currently there are two types of indexes to handle search queries: - -* Substring search on usernames, full names, twitter_handle (powered by MongoDB) -* Raw Lucene index which handles searching extended data e.g., bio. - -Search will currently return upto a max of 20 results (can be less depending on the query) -with data that follows structure of [blockstack IDs](https://github.com/blockstack/blockstack): - -In early 2017, the search subsystem was ported over to the new API system, where support for search is provided by the endpoint: - -``` -http://localhost:5000/search?query= -``` - -This document describes how to setup the search subsystem to respond at that endpoint. - -# Installation - -- **Step 1:** First, make sure you have [virtualenv installed](http://docs.python-guide.org/en/latest/dev/virtualenvs/). -Then, setup the API and search subsystem: -``` -$ sudo apt-get install -y mongodb memcached python-dev libmemcached-dev zlib1g-dev nginx -$ sudo pip install uwsgi -$ git clone https://github.com/blockstack/blockstack-core.git --branch api -$ cd blockstack-core/ -$ sudo pip install . -$ sudo pip install -r api/requirements.txt -$ sudo mkdir /var/blockstack-search && sudo chown $USER:$USER /var/blockstack-search -``` - -- **Step 2:** Make sure you have Blockstack Core running locally (see [instructions](https://github.com/blockstack/blockstack-core/blob/master/README.md#quick-start)). We highly -recommend using a local node because the search subsystem issues thousands of calls to -Blockstack Core for re-indexing and remote nodes can slow down performance. - -- **Step 3:** Fetch the data for the .id namespace and respective profiles. Note, you may want to redirect stderr to a file, as there is a lot of debug output. - -``` -$ cd api/ - -$ python -m search.fetch_data --fetch_namespace - -$ python -m search.fetch_data --fetch_profiles -``` - -- **Step 4:** Create the search index: - -``` -python -m search.basic_index --refresh -``` - -- **Step 5:** Enable search API endpoint: - -``` -$ sed -i 's/SEARCH_API_ENDPOINT_ENABLED \= False/SEARCH_API_ENDPOINT_ENABLED \= True/' config.py -``` - -# Usage - -You can quickly test the search index from the command line: - -``` -python -m search.substring_search --search_name "Fred Wil" -python -m search.substring_search --search_twitter fredwil -``` - -You can also use the search API end-point: - -> curl -G {machine_ip}:port/search/name -d "query=muneeb" - -Sample Response: - -``` -{ - "people": [ - { - "profile": { - "website": [ - { - "url": "http://muneebali.com", - "@type": "WebSite" - } - ], - "name": "Muneeb Ali", - "address": { - "addressLocality": "New York, NY", - "@type": "PostalAddress" - }, - "image": [ - { - "contentUrl": "https://s3.amazonaws.com/dx3/muneeb", - "@type": "ImageObject", - "name": "cover" - }, - { - "contentUrl": "https://s3.amazonaws.com/kd4/muneeb", - "@type": "ImageObject", - "name": "avatar" - } - ], - "@type": "Person", - "description": "Co-founder of Blockstack. Interested in distributed systems and blockchains. Previously, PhD at Princeton." - }, - "username": "muneeb" - }, - { - "profile": { - "message": "This blockchain ID is reserved for Muneeb Ali. If this is you, please email support@onename.com to claim it for free.", - "status": "reserved" - }, - "username": "muneebali" - }, - { - "profile": { - "cover": { - "url": "https://s3.amazonaws.com/97p/HHE.jpg" - }, - "v": "0.2" - }, - "username": "muneebali1" - } - - ] -} -``` - -## Enabling Elastic Search - -### Requirements: - -``` -sudo apt-get install mongodb -sudo apt-get install memcached libmemcached-dev -sudo apt-get install python2.7-dev -pip install -r requirements.txt -``` - -### Elastic Search - -Elastic Search library is not in github and resides at unix/lib/elastic - -the current version we're using is *0.90.2*. Download from: - -> wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-0.90.2.zip - -before installing pylimbmc make sure [memcached](memcached.md) is installed. - -Ensure that mongodb and elastic search are running -starting elastic search: - -``` -$elasticsearch (on mac) -bin/elasticsearch -d (on linux) -``` - -To test if elastic search is running: - -> curl -X GET http://localhost:9200/ - -returns: - -``` -{ - "ok" : true, - "status" : 200, - "name" : "Angler", - "version" : { - "number" : "0.90.2", - "snapshot_build" : false, - "lucene_version" : "4.3.1" - }, -``` - -Create Index: - -> python create_search_index.py --create_index - diff --git a/_site/core/setup_core_portal.md b/_site/core/setup_core_portal.md deleted file mode 100644 index bd1dc80e48..0000000000 --- a/_site/core/setup_core_portal.md +++ /dev/null @@ -1,114 +0,0 @@ -# Install Script - -We provide a [script](../images/scripts/ubuntu-17.04.sh) which will -perform all the steps outlined in this doc (except for creating a protocol handler -- see the bottom of the doc). The script creates a virtualenv of -Blockstack Core and installs Browser in a subdirectory. It additionally creates some -scripts for starting Core and Browser together. - -However, if you'd like to customize your install, step through it -yourself, or you are on a different distro, continue on with this doc! - -# Setting up Blockstack Core API Service - -Install required binaries in Ubuntu: - -``` -sudo apt update && sudo apt-get install -y python-pip python-dev libssl-dev libffi-dev rng-tools curl build-essential -``` - - -If you'd like to use a virtualenv to install Blockstack, you can do that - -``` -pip install virtualenv -virtualenv --python=python2.7 ~/.blockstack.venv/ && source ~/.blockstack.venv/bin/activate -``` - -Let's install virtualchain 0.14.3 and blockstack 0.14.3 - -``` -sudo apt install git -pip install git+https://github.com/blockstack/virtualchain.git@rc-0.14.3 -pip install git+https://github.com/blockstack/blockstack-core.git@rc-0.14.3 -``` - -Get Blockstack core configured with default settings and choose your Bitcoin wallet password -``` -blockstack setup -y --password BITCOIN_WALLET_PASSWORD --debug -``` - -# Setting up Blockstack Browser Node Application - -Install NodeJS through NodeSource PPA - -``` -curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash - -sudo apt install -y nodejs -``` - -Download Blockstack Browser and install its dependencies - -``` -git clone https://github.com/blockstack/blockstack-browser.git -bv0.11.1 -cd blockstack-browser -npm install node-sass -npm install -``` - -Note that `blockstack-browser` depends on `node-sass` which can sometimes install strangely on Linux, running `npm install node-sass` before trying to install the other dependencies solves that problem. - -# Running Blockstack Browser - -Now we're ready to run our Core API service and start the Browser node app. - -First, start the Core API service. - -``` -blockstack api start -y --password BITCOIN_WALLET_PASSWORD --debug -``` - -Start the CORS proxy. - -``` -npm run dev-proxy & -``` - -Start the Node Application - -``` -npm run dev -``` - -Then you can open `http://localhost:3000/` in your browser to get to the Blockstack Browser. - - -You can copy your api password to your clipboard with this command: -``` -grep api_password ~/.blockstack/client.ini | sed 's/api_password = //g' | xclip -selection clipboard -``` - -## Setting up a protocol handler - -If you'd like your browser to automatically handle links with the `blockstack:` protocol specifier, you will need to register a protocol handler with your desktop environment. In Ubuntu/Gnome, this can be done by creating a file - -`~/.local/share/applications/blockstack.desktop` - -With the following contents: - -``` -[Desktop Entry] -Type=Application -Terminal=false -Exec=bash -c 'xdg-open http://localhost:3000/auth?authRequest=$(echo "%u" | sed s,blockstack:////*,,)' -Name=Blockstack-Browser -MimeType=x-scheme-handler/blockstack; -``` - -Then you need to make this file executable, and register it as a protocol handler. - -``` -$ chmod +x ~/.local/share/applications/blockstack.desktop -$ xdg-mime default blockstack.desktop x-scheme-handler/blockstack -``` - -Now, `blockstack:` protocol URLs should get handled by your Blockstack Browser. If you're running Browser in your browser's private mode, you may have to copy and paste the link, as this protocol handler will try to open in a regular browser window. diff --git a/_site/core/wire-format.md b/_site/core/wire-format.md deleted file mode 100644 index 5f45ee8c1b..0000000000 --- a/_site/core/wire-format.md +++ /dev/null @@ -1,492 +0,0 @@ -This page is for organizations who want to be able to create and send name operation transactions to the blockchain(s) Blockstack supports. - -# Bitcoin - -This section describes the transaction formats for the Bitcoin blockchain. - -## Transaction format - -Each Bitcoin transaction for Blockstack contains signatures from two sets of keys: the name owner, and the payer. The owner `scriptSig` and `scriptPubKey` fields are generated from the key(s) that own the given name. The payer `scriptSig` and `scriptPubKey` fields are used to *subsidize* the operation. The owner keys do not pay for any operations; the owner keys only control the minimum amount of BTC required to make the transaction standard. The payer keys only pay for the transaction's fees, and (when required) they pay the name fee. - -This construction is meant to allow the payer to be wholly separate from the owner. The principal that owns the name can fund their own transactions, or they can create a signed transaction that carries out the desired operation and request some other principal (e.g. a parent organization) to actually pay for and broadcast the transaction. - -The general transaction layout is as follows: - -| **Inputs** | **Outputs** | -| ------------------------ | ----------------------- | -| Owner scriptSig (1) | `OP_RETURN ` (2) | -| Payment scriptSig | Owner scriptPubKey (3) | -| Payment scriptSig... (4) | -| ... (4) | ... (5) | - -(1) The owner `scriptSig` is *always* the first input. -(2) The `OP_RETURN` script that describes the name operation is *always* the first output. -(3) The owner `scriptPubKey` is *always* the second output. -(4) The payer can use as many payment inputs as (s)he likes. -(5) At most one output will be the "change" `scriptPubKey` for the payer. -Different operations require different outputs. - -## Payload Format - -Each Blockstack transaction in Bitcoin describes the name operation within an `OP_RETURN` output. It encodes name ownership, name fees, and payments as `scriptPubKey` outputs. The specific operations are described below. - -Each `OP_RETURN` payload *always* starts with the two-byte string `id` (called the "magic" bytes in this document), followed by a one-byte `op` that describes the operation. - -### NAME_PREORDER - -Op: `?` - -Description: This transaction commits to the *hash* of a name. It is the first -transaction of two transactions that must be sent to register a name in BNS. - -Example: [6730ae09574d5935ffabe3dd63a9341ea54fafae62fde36c27738e9ee9c4e889](https://www.blocktrail.com/BTC/tx/6730ae09574d5935ffabe3dd63a9341ea54fafae62fde36c27738e9ee9c4e889) - -`OP_RETURN` wire format: -``` - 0 2 3 23 39 - |-----|--|--------------------------------------------------|--------------| - magic op hash_name(name.ns_id,script_pubkey,register_addr) consensus hash -``` - -Inputs: -* Payment `scriptSig`'s - -Outputs: -* `OP_RETURN` payload -* Payment `scriptPubkey` script for change -* `p2pkh` `scriptPubkey` to the burn address (0x00000000000000000000000000000000000000) - -Notes: -* `register_addr` is a base58check-encoded `ripemd160(sha256(pubkey))` (i.e. an address). This address **must not** have been used before in the underlying blockchain. -* `script_pubkey` is either a `p2pkh` or `p2sh` compiled Bitcoin script for the payer's address. - -### NAME_REGISTRATION - -Op: `:` - -Description: This transaction reveals the name whose hash was announced by a -previous `NAME_PREORDER`. It is the second of two transactions that must be -sent to register a name in BNS. - -Example: [55b8b42fc3e3d23cbc0f07d38edae6a451dfc512b770fd7903725f9e465b2925](https://www.blocktrail.com/BTC/tx/55b8b42fc3e3d23cbc0f07d38edae6a451dfc512b770fd7903725f9e465b2925) - -`OP_RETURN` wire format (2 variations allowed): - -Variation 1: -``` - 0 2 3 39 - |----|--|-----------------------------| - magic op name.ns_id (37 bytes) -``` - -Variation 2: -``` - 0 2 3 39 59 - |----|--|----------------------------------|-------------------| - magic op name.ns_id (37 bytes, 0-padded) value -``` - -Inputs: -* Payer `scriptSig`'s - -Outputs: -* `OP_RETURN` payload -* `scriptPubkey` for the owner's address -* `scriptPubkey` for the payer's change - -Notes: - -* Variation 1 simply registers the name. Variation 2 will register the name and -set a name value simultaneously. This is used in practice to set a zone file -hash for a name without the extra `NAME_UPDATE` transaction. -* Both variations are supported. Variation 1 was designed for the time when - Bitcoin only supported 40-byte `OP_RETURN` outputs. - -### NAME_RENEWAL - -Op: `:` - -Description: This transaction renews a name in BNS. The name must still be -registered and not expired, and owned by the transaction sender. - -Example: [e543211b18e5d29fd3de7c0242cb017115f6a22ad5c6d51cf39e2b87447b7e65](https://www.blocktrail.com/BTC/tx/e543211b18e5d29fd3de7c0242cb017115f6a22ad5c6d51cf39e2b87447b7e65) - -`OP_RETURN` wire format (2 variations allowed): - -Variation 1: -``` - 0 2 3 39 - |----|--|-----------------------------| - magic op name.ns_id (37 bytes) -``` - -Variation 2: -``` - 0 2 3 39 59 - |----|--|----------------------------------|-------------------| - magic op name.ns_id (37 bytes, 0-padded) value -``` - -Inputs: - -* Payer `scriptSig`'s - -Outputs: - -* `OP_RETURN` payload -* `scriptPubkey` for the owner's addess. This can be a different address than - the current name owner (in which case, the name is renewed and transferred). -* `scriptPubkey` for the payer's change -* `scriptPubkey` for the burn address (to pay the name cost) - -Notes: - -* This transaction is identical to a `NAME_REGISTRATION`, except for the presence of the fourth output that pays for the name cost (to the burn address). -* Variation 1 simply renews the name. Variation 2 will both renew the name and - set a new name value (in practice, the hash of a new zone file). -* Both variations are supported. Variation 1 was designed for the time when - Bitcoin only supported 40-byte `OP_RETURN` outputs. -* This operation can be used to transfer a name to a new address by setting the - second output (the first `scriptPubkey`) to be the `scriptPubkey` of the new - owner key. - -### NAME_UPDATE - -Op: `+` - -Description: This transaction sets the name state for a name to the given -`value`. In practice, this is used to announce new DNS zone file hashes to the [Atlas -network](atlas_network.md). - -Example: [e2029990fa75e9fc642f149dad196ac6b64b9c4a6db254f23a580b7508fc34d7](https://www.blocktrail.com/BTC/tx/e2029990fa75e9fc642f149dad196ac6b64b9c4a6db254f23a580b7508fc34d7) - -`OP_RETURN` wire format: -``` - 0 2 3 19 39 - |-----|--|-----------------------------------|-----------------------| - magic op hash128(name.ns_id,consensus hash) zone file hash -``` - -Note that `hash128(name.ns_id, consensus hash)` is the first 16 bytes of a SHA256 hash over the name concatenated to the hexadecimal string of the consensus hash (not the bytes corresponding to that hex string). -See the [Method Glossary](#method-glossary) below. - -Example: `hash128("jude.id" + "8d8762c37d82360b84cf4d87f32f7754") == "d1062edb9ec9c85ad1aca6d37f2f5793"`. - -Inputs: -* owner `scriptSig` -* payment `scriptSig`'s - -Outputs: -* `OP_RETURN` payload -* owner's `scriptPubkey` -* payment `scriptPubkey` change - -### NAME_TRANSFER - -Op: `>` - -Description: This transaction changes the public key hash that owns the name in -BNS. - -Example: [7a0a3bb7d39b89c3638abc369c85b5c028d0a55d7804ba1953ff19b0125f3c24](https://www.blocktrail.com/BTC/tx/7a0a3bb7d39b89c3638abc369c85b5c028d0a55d7804ba1953ff19b0125f3c24) - -`OP_RETURN` wire format: -``` - 0 2 3 4 20 36 - |-----|--|----|-------------------|---------------| - magic op keep hash128(name.ns_id) consensus hash - data? -``` - -Inputs: - -* Owner `scriptSig` -* Payment `scriptSig`'s - -Outputs: - -* `OP_RETURN` payload -* new name owner's `scriptPubkey` -* old name owner's `scriptPubkey` -* payment `scriptPubkey` change - -Notes: - -* The `keep data?` byte controls whether or not the name's 20-byte value is preserved. This value is either `>` to preserve it, or `~` to delete it. - -### NAME_REVOKE - -Op: `~` - -Description: This transaction destroys a registered name. Its name state value -in BNS will be cleared, and no further transactions will be able to affect the -name until it expires (if its namespace allows it to expire at all). - -Example: [eb2e84a45cf411e528185a98cd5fb45ed349843a83d39fd4dff2de47adad8c8f](https://www.blocktrail.com/BTC/tx/eb2e84a45cf411e528185a98cd5fb45ed349843a83d39fd4dff2de47adad8c8f) - -`OP_RETURN` wire format: -``` - 0 2 3 39 - |----|--|-----------------------------| - magic op name.ns_id (37 bytes) -``` - -Inputs: - -* owner `scriptSig` -* payment `scriptSig`'s - -Outputs: - -* `OP_RETURN` payload -* owner `scriptPubkey` -* payment `scriptPubkey` change - -### ANNOUNCE - -Op: `#` - -Description: This transaction does not affect any names in BNS, but it allows a -user to send a message to other BNS nodes. In order for the message to be -received, the following must be true: - -* The sender must have a BNS name -* The BNS nodes must list the sender's BNS name as being a "trusted message - sender" -* The message must have already been propagated through the [Atlas - network](atlas_network.md). This transaction references it by content hash. - -`OP_RETURN` wire format: - -``` - 0 2 3 23 - |----|--|-----------------------------| - magic op ripemd160(sha256(message)) -``` - -Inputs: - -* The payer `scriptSig`'s - -Outputs: - -* `OP_RETURN` payload -* change `scriptPubKey` - -Notes: - -* The payer key should be an owner key for an existing name, since Blockstack users can subscribe to announcements from specific name-owners. - -### NAMESPACE_PREORDER - -Op: `*` - -Description: This transaction announces the *hash* of a new namespace. It is the -first of three transactions that must be sent to create a namespace. - -Example: [5f00b8e609821edd6f3369ee4ee86e03ea34b890e242236cdb66ef6c9c6a1b28](https://www.blocktrail.com/BTC/tx/5f00b8e609821edd6f3369ee4ee86e03ea34b890e242236cdb66ef6c9c6a1b28) - -`OP_RETURN` wire format: -``` - 0 2 3 23 39 - |-----|---|-----------------------------------------|----------------| - magic op hash_name(ns_id,script_pubkey,reveal_addr) consensus hash -``` - -Inputs: - -* Namespace payer `scriptSig` - -Outputs: - -* `OP_RETURN` payload -* Namespace payer `scriptPubkey` change address -* `p2pkh` script to the burn address `1111111111111111111114oLvT2`, whose public key hash is 0x00000000000000000000000000000000 - -Notes: - -* The `reveal_addr` field is the address of the namespace revealer public key. The revealer private key will be used to generate `NAME_IMPORT` transactions. - -### NAMESPACE_REVEAL - -Op: `&` - -Description: This transaction reveals the namespace ID and namespace rules -for a previously-anounced namespace hash (sent by a previous `NAMESPACE_PREORDER`). - -Example: [ab54b1c1dd5332dc86b24ca2f88b8ca0068485edf0c322416d104c5b84133a32](https://www.blocktrail.com/BTC/tx/ab54b1c1dd5332dc86b24ca2f88b8ca0068485edf0c322416d104c5b84133a32) - -`OP_RETURN` wire format: -``` - 0 2 3 7 8 9 10 11 12 13 14 15 16 17 18 20 39 - |-----|---|--------|-----|-----|----|----|----|----|----|-----|-----|-----|--------|----------|-------------------------| - magic op life coeff. base 1-2 3-4 5-6 7-8 9-10 11-12 13-14 15-16 nonalpha version namespace ID - bucket exponents no-vowel - discounts -``` - -Inputs: - -* Namespace payer `scriptSig`s - -Outputs: - -* `OP_RETURN` payload -* namespace revealer `scriptPubkey` -* namespace payer change `scriptPubkey` - -Notes: - -* This transaction must be sent within 1 day of the `NAMESPACE_PREORDER` -* The second output (with the namespace revealer) **must** be a `p2pkh` script -* The address of the second output **must** be the `reveal_addr` in the `NAMESPACE_PREORDER` - -Pricing: - -The rules for a namespace are as follows: - - * a name can fall into one of 16 buckets, measured by length. Bucket 16 incorporates all names at least 16 characters long. - * the pricing structure applies a multiplicative penalty for having numeric characters, or punctuation characters. - * the price of a name in a bucket is ((coeff) * (base) ^ (bucket exponent)) / ((numeric discount multiplier) * (punctuation discount multiplier)) - -Example: -* base = 10 -* coeff = 2 -* nonalpha discount: 10 -* no-vowel discount: 10 -* buckets 1, 2: 9 -* buckets 3, 4, 5, 6: 8 -* buckets 7, 8, 9, 10, 11, 12, 13, 14: 7 -* buckets 15, 16+: - -With the above example configuration, the following are true: - -* The price of "john" would be 2 * 10^8, since "john" falls into bucket 4 and has no punctuation or numerics. -* The price of "john1" would be 2 * 10^6, since "john1" falls into bucket 5 but has a number (and thus receives a 10x discount) -* The price of "john_1" would be 2 * 10^6, since "john_1" falls into bucket 6 but has a number and punctuation (and thus receives a 10x discount) -* The price of "j0hn_1" would be 2 * 10^5, since "j0hn_1" falls into bucket 6 but has a number and punctuation and lacks vowels (and thus receives a 100x discount) - - -### NAME_IMPORT - -Op: `;` - -Description: This transaction registers a name and some name state into a -namespace that has been revealed, but not been launched. Only the namespace -creator can import names. See the [namespace creation -tutorial](namespace_creation.md) for details. - -Example: [c698ac4b4a61c90b2c93dababde867dea359f971e2efcf415c37c9a4d9c4f312](https://www.blocktrail.com/BTC/tx/c698ac4b4a61c90b2c93dababde867dea359f971e2efcf415c37c9a4d9c4f312) - -`OP_RETURN` wire format: -``` - 0 2 3 39 - |----|--|-----------------------------| - magic op name.ns_id (37 bytes) -``` - -Inputs: - -* The namespace reveal `scriptSig` (with the namespace revealer's public key), or one of its first 300 extended public keys -* Any payment inputs - -Outputs: - -* `OP_RETURN` payload -* recipient `scriptPubKey` -* zone file hash (using the 20-byte hash in a standard `p2pkh` script) -* payment change `scriptPubKey` - -Notes: - -* These transactions can only be sent between the `NAMESPACE_REVEAL` and `NAMESPACE_READY`. -* The first `NAME_IMPORT` transaction **must** have a `scriptSig` input that matches the `NAMESPACE_REVEAL`'s second output (i.e. the reveal output). -* Any subsequent `NAME_IMPORT` transactions **may** have a `scriptSig` input whose public key is one of the first 300 extended public keys from the `NAMESPACE_REVEAL`'s `scriptSig` public key. - -### NAMESPACE_READY - -Op: `!` - -Description: This transaction launches a namesapce. Only the namespace creator -can send this transaction. Once sent, anyone can register names in the -namespace. - -Example: [2bf9a97e3081886f96c4def36d99a677059fafdbd6bdb6d626c0608a1e286032](https://www.blocktrail.com/BTC/tx/2bf9a97e3081886f96c4def36d99a677059fafdbd6bdb6d626c0608a1e286032) - -`OP_RETURN` wire format: -``` - - 0 2 3 4 23 - |-----|--|--|------------| - magic op . ns_id -``` - -Inputs: -* Namespace revealer's `scriptSig`s - -Outputs: -* `OP_RETURN` payload -* Change output to the namespace revealer's `p2pkh` script - -Notes: -* This transaction must be sent within 1 year of the corresponding `NAMESPACE_REVEAL` to be accepted. - -## Method Glossary - -Some hashing primitives are used to construct the wire-format representation of each name operation. They are enumerated here: - -``` -B40_REGEX = '^[a-z0-9\-_.+]*$' - -def is_b40(s): - return isinstance(s, str) and re.match(B40_REGEX, s) is not None - -def b40_to_bin(s): - if not is_b40(s): - raise ValueError('{} must only contain characters in the b40 char set'.format(s)) - return unhexlify(charset_to_hex(s, B40_CHARS)) - -def hexpad(x): - return ('0' * (len(x) % 2)) + x - -def charset_to_hex(s, original_charset): - return hexpad(change_charset(s, original_charset, B16_CHARS)) - -def bin_hash160(s, hex_format=False): - """ s is in hex or binary format - """ - if hex_format and is_hex(s): - s = unhexlify(s) - return hashlib.new('ripemd160', bin_sha256(s)).digest() - -def hex_hash160(s, hex_format=False): - """ s is in hex or binary format - """ - if hex_format and is_hex(s): - s = unhexlify(s) - return hexlify(bin_hash160(s)) - -def hash_name(name, script_pubkey, register_addr=None): - """ - Generate the hash over a name and hex-string script pubkey. - Returns the hex-encoded string RIPEMD160(SHA256(x)), where - x is the byte string composed of the concatenation of the - binary - """ - bin_name = b40_to_bin(name) - name_and_pubkey = bin_name + unhexlify(script_pubkey) - - if register_addr is not None: - name_and_pubkey += str(register_addr) - - # make hex-encoded hash - return hex_hash160(name_and_pubkey) - -def hash128(data): - """ - Hash a string of data by taking its 256-bit sha256 and truncating it to the - first 16 bytes - """ - return hexlify(bin_sha256(data)[0:16]) -``` - diff --git a/_site/feed.xml b/_site/feed.xml deleted file mode 100644 index 5760fa90b9..0000000000 --- a/_site/feed.xml +++ /dev/null @@ -1,49 +0,0 @@ -Jekyll2018-09-04T10:14:49-07:00https://zbabystack.netlify.com/BlockstackDocsBlockstackSite tags2017-05-25T00:00:00-07:002017-05-25T00:00:00-07:00https://zbabystack.netlify.com/2017/05/25/post63<p>https://zbabystack.netlify.com/assets/posts/</p> - -<p>/2017/05/25/post63.html</p> - -<p>Musce libero nunc, dignissim quis turpis quis, semper vehicula dolor. Suspendisse tincidunt consequat quam, ac posuere leo dapibus id. Cras fringilla convallis elit, at eleifend mi interam.</p> - -<p>Nulla non sollicitudin. Morbi sit amet laoreet ipsum, vel pretium mi. Morbi varius, tellus in accumsan blandit, elit ligula eleifend velit, luctus mattis ante nulla condimentum nulla. Etiam vestibulum risus vel arcu elementum eleifend. Cras at dolor eget urna varius faucibus tempus in elit.</p> - -<h2 id="image-lightbox-example">Image Lightbox Example</h2> -<p>Nunc porta malesuada porta. Etiam tristique vestibulum dolor at ultricies. Proin hendrerit sapien sed erat fermentum, at commodo velit consectetur.</p> - -<figure data-uk-lightbox="animation: slide"> - <!-- <a class="uk-inline" href="https://zbabystack.netlify.com/assets/posts/image1.png" caption="Image in lightbox"> --> - <a class="uk-inline" href="/assets/posts/image1.png" caption="Image in lightbox"> - <!-- <img src="https://zbabystack.netlify.com/assets/posts/image1.png" alt="Alt for image"> --> - <img src="/assets/posts/image1.png" alt="Alt for image" /> - <div class="uk-position-center"> - <span data-uk-overlay-icon=""></span> - </div> - </a> - <figcaption data-uk-grid="" class="uk-flex-right"><span class="uk-width-auto">Image in lightbox</span></figcaption> -</figure> - -<p>Etiam vestibulum risus vel arcu elementum eleifend. Cras at dolor eget urna varius faucibus tempus in elit. Cras a dui imperdiet, tempus metus quis, pharetra turpis. Phasellus at massa sit amet ante semper fermentum sed eget lectus. Quisque id dictum magna, et dapibus turpis.</p> - -<h2 id="example-of-code-block">Example Of Code Block</h2> -<p>In accumsan lacus ac neque maximus dictum. Phasellus eleifend leo id mattis bibendum. Curabitur et purus turpis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;</p> - -<div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="nt">&lt;head&gt;</span> - <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"utf-8"</span><span class="nt">&gt;</span> - <span class="nt">&lt;meta</span> <span class="na">http-equiv=</span><span class="s">"X-UA-Compatible"</span> <span class="na">content=</span><span class="s">"IE=edge"</span><span class="nt">&gt;</span> - <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1"</span><span class="nt">&gt;</span> - <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"/assets/css/main.css"</span><span class="nt">&gt;</span> - <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"shortcut icon"</span> <span class="na">type=</span><span class="s">"image/png"</span> <span class="na">href=</span><span class="s">"/assets/img/favicon.png"</span> <span class="nt">&gt;</span> - <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"/assets/js/main.js"</span><span class="nt">&gt;&lt;/script&gt;</span> -<span class="nt">&lt;/head&gt;</span> -</code></pre> -</div> - -<h2 id="text-and-quote">Text and Quote</h2> -<p>Cras at dolor eget urna varius faucibus tempus in elit. Cras a dui imperdiet, tempus metus quis, pharetra turpis. Phasellus at massa sit amet ante semper fermentum sed eget lectus. Quisque id dictum magna turpis.</p> - -<blockquote> - <p>Etiam vestibulum risus vel arcu elementum eleifend. Cras at dolor eget urna varius faucibus tempus in elit. Cras a dui imperdiet</p> -</blockquote> - -<p>In accumsan lacus ac neque maximus dictum. Phasellus eleifend leo id mattis bibendum. Curabitur et purus turpis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;</p> - -<p>Etiam in fermentum mi. Sed et tempor felis, eu aliquet nisi. Nam eget ullamcorper arcu. Nunc porttitor nisl a dolor blandit, eget consequat sem maximus. Phasellus lacinia quam porta orci malesuada, vel tincidunt.</p>John BlackSite tags \ No newline at end of file diff --git a/_site/index.html b/_site/index.html deleted file mode 100644 index d09b38fbd9..0000000000 --- a/_site/index.html +++ /dev/null @@ -1,422 +0,0 @@ - - - - - - - - -Blockstack | Docs - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - -
      -
      - - -

      Hero

      - - - -

      Welcome to the documentation on everything Blockstack.

      - - - - - -
      -
      - - - - - - -
      -
      - - -

      Browse by topic or technology category

      - - - - -
      - - - - -
      -
      - - - - -

      Evaluate Blockstack

      - -

      Learn about the technology behind Blockstack. Understand the value it offers and how it provides it.

      - -
      -
      - - - - - -
      -
      - - - - -

      Use the New Internet

      - -

      Learn about the New Internet and its applications. Create an identity and learn how to use it.

      - -
      -
      - - - - - -
      -
      - - - - -

      Build Apps and Earn Money

      - -

      Learn how to build an application that earns with Blockstack.

      - -
      -
      - - - - - -
      -
      - - - - -

      Use the Naming Service

      - -

      Managing your account, create new users and exporting data

      - -
      -
      - - - - - -
      -
      - - - - -

      Implement Authentication

      - -

      Managing your account, creating new users and exporting data

      - -
      -
      - - - - - -
      -
      - - - - -

      Implement Storage with GAIA

      - -

      Backend storage drivers and interactions between developer APIs and the Gaia service.

      - -
      -
      - - -
      - -
      -
      - - - - - - - - -
      -
      - - - - - -
      -
      -
      -
      - - - - - - -
      -
      -
      -

      Didn't find an answer to your question?

      -

      Get in touch with us and we will be happy to respond!

      - Contact Us -
      -
      -
      - - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/ios/images/add-action.gif b/_site/ios/images/add-action.gif deleted file mode 100644 index 6854281cb8..0000000000 Binary files a/_site/ios/images/add-action.gif and /dev/null differ diff --git a/_site/ios/images/app-flow.png b/_site/ios/images/app-flow.png deleted file mode 100644 index fa8b85a3bd..0000000000 Binary files a/_site/ios/images/app-flow.png and /dev/null differ diff --git a/_site/ios/images/blockstack-icon.png b/_site/ios/images/blockstack-icon.png deleted file mode 100644 index 243615bb9f..0000000000 Binary files a/_site/ios/images/blockstack-icon.png and /dev/null differ diff --git a/_site/ios/images/blockstack-signin.png b/_site/ios/images/blockstack-signin.png deleted file mode 100644 index 081d505034..0000000000 Binary files a/_site/ios/images/blockstack-signin.png and /dev/null differ diff --git a/_site/ios/images/choose-new-options.png b/_site/ios/images/choose-new-options.png deleted file mode 100644 index 9f1876fd88..0000000000 Binary files a/_site/ios/images/choose-new-options.png and /dev/null differ diff --git a/_site/ios/images/create-restore.png b/_site/ios/images/create-restore.png deleted file mode 100644 index 914b26e5b3..0000000000 Binary files a/_site/ios/images/create-restore.png and /dev/null differ diff --git a/_site/ios/images/final-app.png b/_site/ios/images/final-app.png deleted file mode 100644 index 81f7997e46..0000000000 Binary files a/_site/ios/images/final-app.png and /dev/null differ diff --git a/_site/ios/images/image-set-0.png b/_site/ios/images/image-set-0.png deleted file mode 100644 index ed8d7fa77e..0000000000 Binary files a/_site/ios/images/image-set-0.png and /dev/null differ diff --git a/_site/ios/images/image-set-1.png b/_site/ios/images/image-set-1.png deleted file mode 100644 index e8f271fae6..0000000000 Binary files a/_site/ios/images/image-set-1.png and /dev/null differ diff --git a/_site/ios/images/indicator.png b/_site/ios/images/indicator.png deleted file mode 100644 index 96282709ca..0000000000 Binary files a/_site/ios/images/indicator.png and /dev/null differ diff --git a/_site/ios/images/main-storyboard.png b/_site/ios/images/main-storyboard.png deleted file mode 100644 index e00e985b0e..0000000000 Binary files a/_site/ios/images/main-storyboard.png and /dev/null differ diff --git a/_site/ios/images/open-as.png b/_site/ios/images/open-as.png deleted file mode 100644 index 00e77f6989..0000000000 Binary files a/_site/ios/images/open-as.png and /dev/null differ diff --git a/_site/ios/images/open-xcworkspace.png b/_site/ios/images/open-xcworkspace.png deleted file mode 100644 index c8d8f4be4e..0000000000 Binary files a/_site/ios/images/open-xcworkspace.png and /dev/null differ diff --git a/_site/ios/images/running-app.png b/_site/ios/images/running-app.png deleted file mode 100644 index 9838c05f8c..0000000000 Binary files a/_site/ios/images/running-app.png and /dev/null differ diff --git a/_site/ios/images/single-view-app.png b/_site/ios/images/single-view-app.png deleted file mode 100644 index f2c7f08dea..0000000000 Binary files a/_site/ios/images/single-view-app.png and /dev/null differ diff --git a/_site/ios/images/splash.png b/_site/ios/images/splash.png deleted file mode 100644 index 134d062d86..0000000000 Binary files a/_site/ios/images/splash.png and /dev/null differ diff --git a/_site/ios/images/url-type.png b/_site/ios/images/url-type.png deleted file mode 100644 index b222e37c09..0000000000 Binary files a/_site/ios/images/url-type.png and /dev/null differ diff --git a/_site/ios/images/view-editors.gif b/_site/ios/images/view-editors.gif deleted file mode 100644 index 2003f79342..0000000000 Binary files a/_site/ios/images/view-editors.gif and /dev/null differ diff --git a/_site/ios/tutorial.html b/_site/ios/tutorial.html deleted file mode 100644 index ba9d43f482..0000000000 --- a/_site/ios/tutorial.html +++ /dev/null @@ -1,1180 +0,0 @@ - - - - - - - - -iOS SDK Tutorial (Pre-release) | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      -
      - - - -
      - -
      - -

      iOS SDK Tutorial (Pre-release)

      - - - - - -
      - -

      This tutorial teaches you how to create a decentralized application using -Blockstack’s iOS SDK using the following content:

      - - - -

      This tutorial was extensively tested using XCode 9.3 on a MacBook Air -running High Sierra 10.13.4. If your environment is different, you may encounter -slight or even major discrepancies when performing the procedures in this -tutorial. Please join the Blockstack community Slack and post questions or comments to -the #support channel.

      - -

      Finally, this tutorial is written for all levels from the beginner to the most -experienced. For best results, beginners should follow the guide as written. It -is expected that the fast or furiously brilliant will skip ahead and improvise -on this material at will. God speed one and all.

      - -

      If you prefer, you can skip working through the tutorial all together. Instead, -you can download the final project code and import it -into XCode to review it.

      - -

      Understand the sample application flow

      - -

      When complete, the sample application is a simple hello-world display. It is -intended for user on an iOS phone.

      - -

      - -

      Only users with an existing blockstack.id can run your -final sample application. When complete, users interact with the sample -application by doing the following:

      - -

      - -

      Set up your environment

      - -

      This sample application requires two code bases, a BlockStack hello-blockstack web -application and a hello-ios iOS application. You use the iOS application to run the -web application on an iOS device.

      - -

      Before you start developing the sample, there are a few elements you need in -your environment.

      - -

      Install XCode

      - -

      If you are an experienced iOS developer and already have an iOS -development environment on your workstation, you can use that and skip this -step. However, you may need to adjust the remaining instructions for your -environment.

      - -

      Follow the installation instructions to download and XCode for your operating system. -Depending on your network connection, this can take between 15-30 minutes.

      - -

      Do you have npm?

      - -

      The Blockstack code in this tutorial relies on the npm dependency manager. -Before you begin, verify you have installed npm using the which command to -verify.

      - -
      $ which npm
      -/usr/local/bin/npm
      -
      -
      - -

      If you don’t find npm in your system, install -it.

      - -

      Install the CocoaPods 1.6.0.beta.1 dependency manager

      - -

      The sample application only runs on devices with iOS 11.0 or higher. You install -the Blockstack iOS SDK through the CocoaPods dependency manager. You must use -the 1.6.0.beta.1 version of CocoaPods or newer to avoid an incapability -between Cocoapods and XCode.

      - -

      Before starting the tutorial, confirm you have installed CocoaPods.

      - -
      $ pod --version
      -1.6.0.beta.1
      -
      -
      - -

      If you don’t have the CocoaPods beta version, install it:

      - -
      sudo gem install cocoapods -v 1.6.0.beta.1
      -
      -
      - -

      Use npm to install Yeoman and the Blockstack App Generator

      - -

      You use npm to install Yeoman. Yeoman is a generic scaffolding system that -helps users rapidly start new projects and streamline the maintenance of -existing projects.

      - -
        -
      1. -

        Install Yeoman.

        - -
         npm install -g yo
        -
        -
        -
      2. -
      3. -

        Install the Blockstack application generator.

        - -
         npm install -g generator-blockstack
        -
        -
        -

        Build the Blockstack hello-world

        -
      4. -
      - -

      In this section, you build a Blockstack hello-world application. Then, you -modify the hello-world to interact with the iOS app via a redirect.

      - -

      Generate and launch your hello-blockstack application

      - -

      In this section, you build an initial React.js application called -hello-blockstack.

      - -
        -
      1. -

        Create a hello-blockstack directory.

        - -
         mkdir hello-blockstack
        -
        -
        -
      2. -
      3. -

        Change into your new directory.

        - -
         cd hello-blockstack
        -
        -
        -
      4. -
      5. -

        Use Yeoman and the Blockstack application generator to create your initial hello-blockstack application.

        - -
         yo blockstack:react
        -
        -
        - -

        You should see several interactive prompts.

        - -
         $ yo blockstack:react
        - ==========================================================================
        - We are constantly looking for ways to make yo better!
        - May we anonymously report usage statistics to improve the tool over time?
        - More info: https://github.com/yeoman/insight & http://yeoman.io
        - ========================================================================== No
        -
        -      _-----_     ╭──────────────────────────╮
        -     |       |    │      Welcome to the      │
        -     |--(o)--|    │      Blockstack app      │
        -    `---------´   │        generator!        │
        -     ( _´U`_ )    ╰──────────────────────────╯
        -     /___A___\   /
        -      |  ~  |
        -    __'.___.'__
        -  ´   `  |° ´ Y `
        -
        - ? Are you ready to build a Blockstack app in React? (Y/n)
        -
        -
        -
      6. -
      7. -

        Respond to the prompts to populate the initial app.

        - -

        After the process completes successfully, you see a prompt similar to the following:

        - -
         [fsevents] Success:
        - "/Users/theuser/repos/hello-blockstack/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node"
        - is installed via remote npm notice created a lockfile as package-lock.json.
        - You should commit this file. added 1060 packages in 26.901s
        -
        -
        -
      8. -
      9. -

        Run the initial application.

        - -
         npm start
        -
        - > hello-blockstack@0.0.0 start /Users/moxiegirl/repos/hello-blockstack
        - > webpack-dev-server
        -
        - Project is running at http://localhost:8080/
        - webpack output is served from /
        - 404s will fallback to /index.html
        - Hash: 4d2312ba236a4b95dc3a
        - Version: webpack 2.7.0
        - Time: 2969ms
        -                                  Asset       Size  Chunks                    Chunk Names
        - ....
        -   Child html-webpack-plugin for "index.html":
        -      chunk    {0} index.html 541 kB [entry] [rendered]
        -          [0] ./~/lodash/lodash.js 540 kB {0} [built]
        -          [1] ./~/html-webpack-plugin/lib/loader.js!./src/index.html 533 bytes {0} [built]
        -          [2] (webpack)/buildin/global.js 509 bytes {0} [built]
        -          [3] (webpack)/buildin/module.js 517 bytes {0} [built]
        -  webpack: Compiled successfully.
        -
        -
        - -

        The system opens a browser displaying your running application.

        -
      10. -
      - -

      - -

      At this point, the browser is running a Blockstack server on your local host. - This is for testing your applications only.

      - -
        -
      1. -

        Choose Sign in with Blockstack

        - -

        The system displays a prompt allowing you to create a new Blockstack ID or restore an existing one.

        - -

        -
      2. -
      3. -

        Follow the prompts appropriate to your situation.

        - -

        If you are restoring an existing ID, you may see a prompt about your user - being nameless, ignore it. At this point you have only a single application - on your test server. So, you should see this single application, with your - own blockstack.id display name, once you are signed in:

        - -

        -
      4. -
      - -

      Add a redirect end point to your application

      - -

      When a user opens the webapp from the Blockstack browser on an iOS phone, -you want the web app to redirect the user to your iOS application. The work -you do here will allow it.

      - -
        -
      1. -

        From the terminal command line, change directory to the root of your web -application directory.

        -
      2. -
      3. -

        Use the touch command to add a redirect endpoint to your application.

        - -

        This endpoint on the web version of your app will redirect iOS users back -to your mobile app.

        - -
         $ touch public/redirect.html
        -
        -
        -
      4. -
      5. -

        Open redirect.html and add code to the endpoint.

        - -
         <!DOCTYPE html>
        - <html>
        - <head>
        -   <title>Hello, Blockstack!</title>
        -   <script>
        -   function getParameterByName(name) {
        -     var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
        -     return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
        -   }
        -
        -   var authResponse = getParameterByName('authResponse')
        -   window.location="myblockstackapp:" + authResponse
        -   </script>
        - <body>
        - </body>
        - </html>
        -
        -
        - -

        Blockstack apps are identified by their domain names. The endpoint will - receive a get request with the query parameter authResponse=XXXX and - should redirect the browser to myblockstackapp:XXXX.

        - -

        myblockstackapp: is custom protocol handler. The handler should be unique - to your application. Your app’s web-based authentication uses this handler - to redirect the user back to your iOS app. Later, you’ll add a reference - to this handler in your iOS application.

        -
      6. -
      7. Close and save the redirect.html file.
      8. -
      9. Ensure your Blockstack compiles successfully.
      10. -
      - -

      Build the hello-blockstack-ios

      - -

      Now, you build an iOS application that can access and run your Blockstack web -application on a mobile device.

      - -

      Create an XCode Project

      - -

      This tutorial uses XCode 9.3, you can use another version but be aware that some -menu items and therefore these procedures may be differœent on your version.

      - -
        -
      1. Launch the XCode interface.
      2. -
      3. Choose Create new XCode project.
      4. -
      5. Select iOS.
      6. -
      7. -

        Select Single View Page.

        - -

        -
      8. -
      9. -

        Choose options for your new project for your project.

        - -

        -
      10. -
      11. -

        Press Next.

        - -

        The system prompts you for a location to store your code.

        -
      12. -
      13. Save your project.
      14. -
      15. Close XCode.
      16. -
      - -

      Add and edit a Podfile

      - -

      To use CocoaPods you need to define the XCode target to link them to. -So for example if you are writing an iOS app, it would be the name of your app. -Create a target section by writing target ‘$TARGET_NAME’ do and an end a few -lines after.

      - -
        -
      1. Open a terminal window on your workstation.
      2. -
      3. -

        Navigate to and change directory into the root of your project directory.

        - -
          $ cd hello-blockstack-ios
        -
        -
        -
      4. -
      5. -

        Create a Podfile.

        - -
          $ pod init
        -
        -
        - -

        The command creates a Podfile in the directory.

        -
      6. -
      7. Open the Podfile for editing.
      8. -
      9. -

        Add a line stating the Blockstack dependency.

        - -
        # Uncomment the next line to define a global platform for your project
        -# platform :ios, '9.0'
        -
        -target 'hello-blockstack-ios' do
        -      # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
        -      use_frameworks!
        -
        -      # Pods for hello-blockstack-ios
        -      pod 'Blockstack'
        -
        -      target 'hello-blockstack-iosTests' do
        -        inherit! :search_paths
        -        # Pods for testing
        -      end
        -end
        -
        -
        -
      10. -
      11. Save and close the Podfile.
      12. -
      - -

      Install Blockstack SDK and open the pod project

      - -
        -
      1. Close your new XCode project.
      2. -
      3. Change to the root of your hello-blockstack-is project.
      4. -
      5. -

        Initialize the project with Cocoapods.

        - -
          $ pod install
        -  Analyzing dependencies
        -  Downloading dependencies
        -  Installing Blockstack (0.2.0)
        -  Installing CryptoSwift (0.11.0)
        -  Generating Pods project
        -  Integrating client project
        -
        -  [!] Please close any current XCode sessions and use `hello-blockstack-ios.xcworkspace` for this project from now on.
        -  Sending stats
        -  Pod installation complete! There is 1 dependency from the Podfile and 2 total pods installed.
        -
        -  [!] Automatically assigning platform `ios` with version `11.4` on target `hello-blockstack-ios` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.
        -
        -
        - -

        This command creates a number of files

        -
      6. -
      7. -

        Review the files that the pod installation created:

        - -
        $ ls
        -Podfile                hello-blockstack-ios                    hello-blockstack-iosTests
        -Podfile.lock           hello-blockstack-ios.xcodeproj
        -Pods                   hello-blockstack-ios.xcworkspace
        -
        -
        -
      8. -
      9. Start XCode and choose Open another project.
      10. -
      11. -

        Choose the .xcworkspace file created in your project folder.

        - -

        - -

        When you open the workspace you’ll see a warning indicator at the top in the - project title.

        -
      12. -
      13. Click the signal to reveal the warning.
      14. -
      15. -

        Click Update to recommented settings.

        - -

        -
      16. -
      17. -

        Choose Perform Changes and Continue when prompted.

        - -

        The indicator disappears.

        -
      18. -
      - -

      Choose a custom protocol handler

      - -

      You’ll need to choose a custom protocol handler that is unique to your app. This -is so that your app’s web-based authentication redirect.html endpoint can redirect -the user back to your iOS app. In this example, you use myblockstackapp://.

      - -
        -
      1. Open the .xcworkspace file in XCode if it isn’t open already.
      2. -
      3. Select the top node of your project.
      4. -
      5. Select the Info tab in XCode.
      6. -
      7. Scroll to URL Types and press + (plus) sign.
      8. -
      9. Enter an Identifier and URL Schemes value.
      10. -
      11. -

        Set the Role to Editor.

        - -

        When you are done the type appears as follows:

        - -

        -
      12. -
      - -

      Add a splash screen

      - -

      All iOS applications require a splash page.

      - -
        -
      1. Select Assets.xcassets
      2. -
      3. Move your cursor into the area below Appicon.
      4. -
      5. -

        Right click and choose New Image Set

        - -

        -
      6. -
      7. -

        Download the Blockstack icon.

        - -

        -
      8. -
      9. -

        Drag the downloaded file into the 3X position in your new Images folder.

        - -

        -
      10. -
      11. Select the LaunchScreen.storyboard.
      12. -
      13. -

        Choose Open As > Source Code.

        - -

        -
      14. -
      15. -

        Replace the content of the <scenes> element with the following:

        - -
        <scenes>
        -    <!--View Controller-->
        -    <scene sceneID="EHf-IW-A2E">
        -        <objects>
        -            <viewController id="01J-lp-oVM" sceneMemberID="viewController">
        -                <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
        -                    <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
        -                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
        -                    <subviews>
        -                        <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Image" translatesAutoresizingMaskIntoConstraints="NO" id="SpU-hA-y2f">
        -                            <rect key="frame" x="155.5" y="273" width="64" height="64"/>
        -                        </imageView>
        -                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Hello Blockstack iOS" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wfj-A6-BZM">
        -                            <rect key="frame" x="108" y="432" width="158" height="21"/>
        -                            <fontDescription key="fontDescription" type="system" pointSize="17"/>
        -                            <nil key="textColor"/>
        -                            <nil key="highlightedColor"/>
        -                        </label>
        -                    </subviews>
        -                    <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
        -                    <constraints>
        -                        <constraint firstItem="Wfj-A6-BZM" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="AZy-qf-xHq"/>
        -                        <constraint firstItem="Wfj-A6-BZM" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="412" id="SwP-qV-1RP"/>
        -                        <constraint firstItem="SpU-hA-y2f" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="XdI-Db-fDo"/>
        -                        <constraint firstItem="SpU-hA-y2f" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="253" id="xc5-po-W1E"/>
        -                    </constraints>
        -                    <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
        -                </view>
        -            </viewController>
        -            <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
        -        </objects>
        -        <point key="canvasLocation" x="52" y="374.66266866566718"/>
        -    </scene>
        -</scenes>
        -
        -
        -
      16. -
      17. -

        Immediately after scenes but before the close of the </document> tag add the following <resources>.

        - -
                <resources>
        -      <image name="Image" width="64" height="64"/>
        -        </resources>
        -     </document>
        -
        -
        -
      18. -
      19. -

        Choose Run > Run app in the emulator.

        - -

        The emulator now contains a new splash screen.

        - -

        -
      20. -
      - -

      Add a Main.storyboard

      - -

      Rather than have you build up your own UI, this section has you copy and paste a layout into the XML file source code for the Main.storyboard file.

      - -
        -
      1. Select the Main.storyboard file.
      2. -
      3. -

        Chooose Open As > Source Code

        - -

        The blockstack-example/blockstack-example/Base.lproj/Main.storyboard file -defines the graphical elements. Some elements are required before you can -functionality to your code.

        -
      4. -
      5. -

        Replace the element with the following:

        - -
        <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
        -     <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
        -     <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
        -     <subviews>
        -         <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="hello-blockstack-ios" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9eE-ZS-LU9">
        -             <rect key="frame" x="0.0" y="101" width="375" height="50"/>
        -             <color key="backgroundColor" red="0.44735813140000003" green="0.1280144453" blue="0.57268613580000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
        -             <constraints>
        -                 <constraint firstAttribute="height" constant="50" id="U5v-13-4Ux"/>
        -             </constraints>
        -             <fontDescription key="fontDescription" type="system" pointSize="17"/>
        -             <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        -             <nil key="highlightedColor"/>
        -         </label>
        -         <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Lfp-KX-BDb">
        -             <rect key="frame" x="100" y="382" width="175" height="40"/>
        -             <color key="backgroundColor" red="0.1215686275" green="0.12941176469999999" blue="0.14117647059999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
        -             <constraints>
        -                 <constraint firstAttribute="height" constant="40" id="8fN-Ro-Krn"/>
        -             </constraints>
        -             <color key="tintColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
        -             <state key="normal" title="Sign into Blocksack">
        -                 <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        -             </state>
        -             <connections>
        -                 <action selector="signIn:" destination="BYZ-38-t0r" eventType="touchUpInside" id="nV7-rt-euZ"/>
        -             </connections>
        -         </button>
        -     </subviews>
        -     <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
        -     <constraints>
        -         <constraint firstItem="9eE-ZS-LU9" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="2ZP-tU-h9Y"/>
        -         <constraint firstItem="9eE-ZS-LU9" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="81" id="DBh-q0-pAV"/>
        -         <constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="Lfp-KX-BDb" secondAttribute="trailing" constant="100" id="MHO-ew-4Bd"/>
        -         <constraint firstItem="Lfp-KX-BDb" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="100" id="Rsm-LP-ya7"/>
        -         <constraint firstItem="Lfp-KX-BDb" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="362" id="chE-B7-ya6"/>
        -         <constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="9eE-ZS-LU9" secondAttribute="trailing" id="j0x-8j-04u"/>
        -     </constraints>
        -     <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
        - </view>
        -
        -
        -
      6. -
      - -

      Add the UI variables to the ViewController file.

      - -

      In this section, you edit the ViewController.swift file using the storyboard as a starting point.

      - -
        -
      1. -

        Select the Main.storyboard and choose Open As > Interface Builder - storyboard.

        - -

        -
      2. -
      3. -

        With the interface builder open, display the ViewController.swift file in the rigth panel.

        - -

        -
      4. -
      5. -

        In the storyboard, select the Sign into Blockstack button.

        -
      6. -
      7. -

        Control-drag from the button to the code display in the editor on the right, stopping the drag at the line below controller’s opening statement.

        - -

        -
      8. -
      9. -

        Repeat this process with the storyboard’s purple hello-blockstack-ios label.

        - -

        When you are done, your ViewController file contains the following variables:

        - -
         class ViewController: UIViewController {
        -
        - @IBOutlet var nameLabel: UILabel!
        - @IBOutlet var signInButton: UIButton!
        -
        -
        - -

        And XCode has added two outlines to the Main.storyboard source.

        - -
         <connections>
        - <outlet property="nameLabel" destination="9eE-ZS-LU9" id="Ahv-Te-ZZo"/>
        - <outlet property="signInButton" destination="Lfp-KX-BDb" id="yef-Jj-uPt"/>
        - </connections>
        -
        -
        - -

        Your connectors will have their own destination and id values.

        -
      10. -
      - -

      Edit the ViewController.swift file

      - -

      Now, you are ready to connect your applicaiton with your Blockstack Web -Application. Normally, after building your Web application you would have -registred it with Blockstack and the app would be available on the Web. This -example skips this registroation step and uses an example application we’ve -already created for you:

      - -

      https://heuristic-brown-7a88f8.netlify.com/redirect.html

      - -

      This web application already has a redirect in place for you. You’ll reference -this application in your mobile add for now. In XCode, do the following;

      - -
        -
      1. Open the ViewController.swift file.
      2. -
      3. -

        Add an import both for Blockstack and for SafariServices.

        - -
          import UIKit
        -  import Blockstack
        -  import SafariServices
        -
        -
        -
      4. -
      5. -

        Just before the didReceiveMemoryWarning function a private updateUI function.

        - -

        This function takes care of loading the user data from Blockstack.

        - -
        private func updateUI() {
        -  DispatchQueue.main.async {
        - 		if Blockstack.shared.isSignedIn() {
        - 			// Read user profile data
        - 			let retrievedUserData = Blockstack.shared.loadUserData()
        - 			print(retrievedUserData?.profile?.name as Any)
        - 			let name = retrievedUserData?.profile?.name ?? "Nameless Person"
        - 			self.nameLabel?.text = "Hello, \(name)"
        - 			self.nameLabel?.isHidden = false
        - 			self.signInButton?.setTitle("Sign Out", for: .normal)
        - 		} else {
        - 			self.signInButton?.setTitle("Sign into Blockstack", for: .normal)
        - 		}
        - 	}
        - }
        -
        -
        -
      6. -
      7. -

        Replace the content of the viewDidLoad() function so that it calls this private function.

        - -
          override func viewDidLoad() {
        -     self.updateUI()
        -}
        -
        -
        -
      8. -
      9. -

        Add a handleSignInSuccess() function to handle sign in.

        - -
          func handleSignInSuccess(userData: UserData) {
        -      print(userData.profile?.name as Any)
        -
        -      self.updateUI()
        -
        -      // Check if signed in
        -      // checkIfSignedIn()
        -  }
        -
        -
        -
      10. -
      11. -

        Add a signOut() function to handle signOut

        - -
        @IBAction func signOut(_ sender: Any) {
        -  // Sign user out
        -  Blockstack.shared.signOut(redirectURI: "myBlockstackApp") { error in
        -    if let error = error {
        -      print("sign out failed, error: \(error)")
        -    } else {
        -      self.updateUI()
        -      print("sign out success")
        -    }
        -   }
        -     }
        -
        -
        -
      12. -
      13. -

        Add a function to check if a user is signed in

        - -
          func checkIfSignedIn() {
        -      Blockstack.shared.isSignedIn() ? print("currently signed in") : print("not signed in")
        -  }
        -
        -
        -
      14. -
      15. -

        Put it all together with a signin buttonType

        - -
          @IBAction func signIn(_ sender: UIButton) {
        -  // Address of deployed example web app
        -  Blockstack.shared.signIn(redirectURI: "https://heuristic-brown-7a88f8.netlify.com/redirect.html",
        -     appDomain: URL(string: "https://heuristic-brown-7a88f8.netlify.com")!) { authResult in
        -     switch authResult {
        -     case .success(let userData):
        -       print("sign in success")
        -       self.handleSignInSuccess(userData: userData)
        -     case .cancelled:
        -       print("sign in cancelled")
        -     case .failed(let error):
        -       print("sign in failed, error: ", error ?? "n/a")
        -     }
        -      }
        -    }
        -
        -
        -
      16. -
      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Related Articles

      - - - - - -
        - -
      -
      - - - -
      - - - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/news/index.html b/_site/news/index.html deleted file mode 100644 index 2ef7d9b5dc..0000000000 --- a/_site/news/index.html +++ /dev/null @@ -1,293 +0,0 @@ - - - - - - - - -News | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      - -

      News

      - - -
      - -
      -
      - - -
      - - Blockstack - -
      -
      -

      Site tags

      -

      -
      -
      -
      -
      -

      Site tags

      - - -
      - - -
      - - - -
        - -
      - - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/_site/package.json b/_site/package.json deleted file mode 100644 index 65298ac475..0000000000 --- a/_site/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "docs", - "version": "1.0.0", - "description": "Docs Jekyll theme.", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "copy": "ncp node_modules/uikit/src/scss/ _sass/uikit/ && ncp node_modules/system-font-css/ _sass/system-font-css/", - "lint": "jshint assets/js/custom.js", - "uglify": "uglifyjs node_modules/uikit/dist/js/uikit.js node_modules/uikit/dist/js/uikit-icons.js node_modules/simple-jekyll-search/dest/simple-jekyll-search.js assets/js/custom.js -m -c -o assets/js/main.js", - "concat": "uglifyjs node_modules/uikit/dist/js/uikit.js node_modules/uikit/dist/js/uikit-icons.js node_modules/simple-jekyll-search/dest/simple-jekyll-search.js assets/js/custom.js -b -o assets/js/main.js", - "dev": "npm run lint && npm run concat", - "build": "npm run lint && npm run uglify", - "watch": "watch 'npm run dev' assets/js/", - "postinstall": "npm run copy && npm run concat" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/" - }, - "author": "moxiegirl", - "license": "MIT", - "bugs": { - "url": "https://github.com/" - }, - "homepage": "https://github.com/", - "dependencies": { - "simple-jekyll-search": "^1.5.0", - "system-font-css": "^2.0.1", - "uikit": "^3.0.0-beta.42" - }, - "devDependencies": { - "jshint": "^2.9.5", - "ncp": "latest", - "uglify-js": "^3.2.1", - "watch": "^1.0.2" - }, - "directories": { - "doc": "docs" - } -} diff --git a/_site/robots.txt b/_site/robots.txt deleted file mode 100644 index 1f53798bb4..0000000000 --- a/_site/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: / diff --git a/_site/search.json b/_site/search.json deleted file mode 100644 index 3c5c1916ab..0000000000 --- a/_site/search.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - -] diff --git a/_site/staticman.yml b/_site/staticman.yml deleted file mode 100644 index 5dc758657d..0000000000 --- a/_site/staticman.yml +++ /dev/null @@ -1,77 +0,0 @@ -# Name of the property. You can have multiple properties with completely -# different config blocks for different sections of your site. -# For example, you can have one property to handle comment submission and -# another one to handle posts. -comments: - # (*) REQUIRED - # - # Names of the fields the form is allowed to submit. If a field that is - # not here is part of the request, an error will be thrown. - allowedFields: ["name", "email", "url", "message"] - - # (*) REQUIRED - # - # Name of the branch being used. Must match the one sent in the URL of the - # request. - branch: "master" - - # Text to use as the commit message or pull request title. Accepts placeholders. - commitMessage: "Add Staticman data" - - # (*) REQUIRED - # - # Destination path (filename) for the data files. Accepts placeholders. - filename: "entry{@timestamp}" - - # The format of the generated data files. Accepted values are "json", "yaml" - # or "frontmatter" - format: "yaml" - - # List of fields to be populated automatically by Staticman and included in - # the data file. Keys are the name of the field. The value can be an object - # with a `type` property, which configures the generated field, or any value - # to be used directly (e.g. a string, number or array) - generatedFields: - date: - type: date - options: - format: "timestamp-seconds" - - # Whether entries need to be appproved before they are published to the main - # branch. If set to `true`, a pull request will be created for your approval. - # Otherwise, entries will be published to the main branch automatically. - moderation: true - - # Name of the site. Used in notification emails. - name: "zbabystack.netlify.com" - - # Notification settings. When enabled, users can choose to receive notifications - # via email when someone adds a reply or a new comment. This requires an account - # with Mailgun, which you can get for free at http://mailgun.com. - #notifications: - # Enable notifications - #enabled: true - - # (!) ENCRYPTED - # - # Mailgun API key - #apiKey: "1q2w3e4r" - - # (!) ENCRYPTED - # - # Mailgun domain (encrypted) - #domain: "4r3e2w1q" - - # (*) REQUIRED - # - # Destination path (directory) for the data files. Accepts placeholders. - path: "_data/comments/{options.slug}" - - # Names of required fields. If any of these isn't in the request or is empty, - # an error will be thrown. - requiredFields: ["name", "email", "message"] - - # List of transformations to apply to any of the fields supplied. Keys are - # the name of the field and values are possible transformation types. - transforms: - email: md5 diff --git a/_site/thanks/index.html b/_site/thanks/index.html deleted file mode 100644 index 485ae8f07e..0000000000 --- a/_site/thanks/index.html +++ /dev/null @@ -1,269 +0,0 @@ - - - - - - - - -Thanks | Blockstack - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      - -
      -
      - - - - -
      -
      - -
      - -

      Thanks

      - -
      -

      Varius tempor. Nulla non sollicitudin tortor. Morbi sit amet laoreet ipsum, vel pretium mi. Morbi varius, tellus in accumsan blandit, elit ligula eleifend velit, luctus mattis ante nulla condimentum nulla.

      - -

      Ut malesuada varius tempor. Nulla non sollicitudin tortor. Morbi sit amet laoreet ipsum, vel pretium mi. Morbi varius, tellus in accumsan blandit, elit ligula eleifend velit, luctus mattis ante nulla condimentum nulla.

      - -

      Mesuada varius tempor. Nulla non sollicitudin tortor. Morbi sit amet laoreet ipsum, vel pretium mi. Morbi varius, tellus in accumsan blandit, elit ligula eleifend velit, luctus mattis ante nulla condimentum nulla.

      - -

      Varius tempor. Nulla non sollicitudin tortor. Morbi sit amet laoreet ipsum, vel pretium mi. Morbi varius, tellus in accumsan blandit, elit ligula eleifend velit, luctus mattis ante nulla condimentum nulla.

      - -
      - -
      - -
      -
      - - -
      -
      - - - - - - - -
      -
      - - - -
      -
      - -
      - -
      -
      -
      - -
      - -
      - - -
      - -
      - - - - -
      - -
      - - - - - -
      - -
      - - - - - - -
      -
      - - -
      -
      - - - - - - - - - - - diff --git a/assets/css/main.scss b/assets/css/main.scss deleted file mode 100644 index 109f4248b3..0000000000 --- a/assets/css/main.scss +++ /dev/null @@ -1,22 +0,0 @@ ---- -# Only the main Sass file needs front matter (the dashes are enough) ---- - -// System fonts -@import "system-font-css/_system-font"; - -// Custom variables and variable overwrites. -@import "theme/variables"; - -// Import default variables and available mixins. -@import "uikit/variables-theme"; -@import "uikit/mixins-theme"; - -// Custom mixin overwrites. -@import "theme/mixins"; - -// Import UIkit. -@import "theme/uikit"; - -// Other vendor styles -@import "syntax-highlighting/github"; diff --git a/assets/img/favicon.png b/assets/img/favicon.png deleted file mode 100644 index 09d6ca94a9..0000000000 Binary files a/assets/img/favicon.png and /dev/null differ diff --git a/assets/img/location.svg b/assets/img/location.svg deleted file mode 100644 index e71e58b04c..0000000000 --- a/assets/img/location.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - location-dark - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/assets/img/touch-icon.png b/assets/img/touch-icon.png deleted file mode 100644 index 243615bb9f..0000000000 Binary files a/assets/img/touch-icon.png and /dev/null differ diff --git a/assets/js/custom.js b/assets/js/custom.js deleted file mode 100644 index d123eb3515..0000000000 --- a/assets/js/custom.js +++ /dev/null @@ -1 +0,0 @@ -// Custom scripts diff --git a/assets/js/main.js b/assets/js/main.js deleted file mode 100644 index 031cf722e2..0000000000 --- a/assets/js/main.js +++ /dev/null @@ -1,8772 +0,0 @@ -(function(global, factory) { - typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define("uikit", factory) : global.UIkit = factory(); -})(this, function() { - "use strict"; - function bind(fn, context) { - return function(a) { - var l = arguments.length; - return l ? l > 1 ? fn.apply(context, arguments) : fn.call(context, a) : fn.call(context); - }; - } - var ref = Object.prototype; - var hasOwnProperty = ref.hasOwnProperty; - function hasOwn(obj, key) { - return hasOwnProperty.call(obj, key); - } - var hyphenateRe = /([a-z\d])([A-Z])/g; - function hyphenate(str) { - return str.replace(hyphenateRe, "$1-$2").toLowerCase(); - } - var camelizeRE = /-(\w)/g; - function camelize(str) { - return str.replace(camelizeRE, toUpper); - } - function toUpper(_, c) { - return c ? c.toUpperCase() : ""; - } - function ucfirst(str) { - return str.length ? toUpper(null, str.charAt(0)) + str.slice(1) : ""; - } - var strPrototype = String.prototype; - var startsWithFn = strPrototype.startsWith || function(search) { - return this.lastIndexOf(search, 0) === 0; - }; - function startsWith(str, search) { - return startsWithFn.call(str, search); - } - var endsWithFn = strPrototype.endsWith || function(search) { - return this.substr(-search.length) === search; - }; - function endsWith(str, search) { - return endsWithFn.call(str, search); - } - var includesFn = function(search) { - return ~this.indexOf(search); - }; - var includesStr = strPrototype.includes || includesFn; - var includesArray = Array.prototype.includes || includesFn; - function includes(obj, search) { - return obj && (isString(obj) ? includesStr : includesArray).call(obj, search); - } - var isArray = Array.isArray; - function isFunction(obj) { - return typeof obj === "function"; - } - function isObject(obj) { - return obj !== null && typeof obj === "object"; - } - function isPlainObject(obj) { - return isObject(obj) && Object.getPrototypeOf(obj) === Object.prototype; - } - function isWindow(obj) { - return isObject(obj) && obj === obj.window; - } - function isDocument(obj) { - return isObject(obj) && obj.nodeType === 9; - } - function isJQuery(obj) { - return isObject(obj) && !!obj.jquery; - } - function isNode(element) { - return element instanceof Node || isObject(element) && element.nodeType === 1; - } - function isNodeCollection(element) { - return element instanceof NodeList || element instanceof HTMLCollection; - } - function isBoolean(value) { - return typeof value === "boolean"; - } - function isString(value) { - return typeof value === "string"; - } - function isNumber(value) { - return typeof value === "number"; - } - function isNumeric(value) { - return isNumber(value) || isString(value) && !isNaN(value - parseFloat(value)); - } - function isUndefined(value) { - return value === void 0; - } - function toBoolean(value) { - return isBoolean(value) ? value : value === "true" || value === "1" || value === "" ? true : value === "false" || value === "0" ? false : value; - } - function toNumber(value) { - var number = Number(value); - return !isNaN(number) ? number : false; - } - function toFloat(value) { - return parseFloat(value) || 0; - } - function toNode(element) { - return isNode(element) || isWindow(element) || isDocument(element) ? element : isNodeCollection(element) || isJQuery(element) ? element[0] : isArray(element) ? toNode(element[0]) : null; - } - var arrayProto = Array.prototype; - function toNodes(element) { - return isNode(element) ? [ element ] : isNodeCollection(element) ? arrayProto.slice.call(element) : isArray(element) ? element.map(toNode).filter(Boolean) : isJQuery(element) ? element.toArray() : []; - } - function toList(value) { - return isArray(value) ? value : isString(value) ? value.split(/,(?![^(]*\))/).map(function(value) { - return isNumeric(value) ? toNumber(value) : toBoolean(value.trim()); - }) : [ value ]; - } - function toMs(time) { - return !time ? 0 : endsWith(time, "ms") ? toFloat(time) : toFloat(time) * 1e3; - } - function swap(value, a, b) { - return value.replace(new RegExp(a + "|" + b, "mg"), function(match) { - return match === a ? b : a; - }); - } - var assign = Object.assign || function(target) { - var args = [], len = arguments.length - 1; - while (len-- > 0) args[len] = arguments[len + 1]; - target = Object(target); - for (var i = 0; i < args.length; i++) { - var source = args[i]; - if (source !== null) { - for (var key in source) { - if (hasOwn(source, key)) { - target[key] = source[key]; - } - } - } - } - return target; - }; - function each(obj, cb) { - for (var key in obj) { - if (cb.call(obj[key], obj[key], key) === false) { - break; - } - } - } - function sortBy(collection, prop) { - return collection.sort(function(a, b) { - return a[prop] - b[prop]; - }); - } - function clamp(number, min, max) { - if (min === void 0) min = 0; - if (max === void 0) max = 1; - return Math.min(Math.max(number, min), max); - } - function noop() {} - function intersectRect(r1, r2) { - return r1.left <= r2.right && r2.left <= r1.right && r1.top <= r2.bottom && r2.top <= r1.bottom; - } - function pointInRect(point, rect) { - return intersectRect({ - top: point.y, - bottom: point.y, - left: point.x, - right: point.x - }, rect); - } - var Dimensions = { - ratio: function ratio(dimensions, prop, value) { - var obj; - var aProp = prop === "width" ? "height" : "width"; - return obj = {}, obj[aProp] = Math.round(value * dimensions[aProp] / dimensions[prop]), - obj[prop] = value, obj; - }, - contain: function contain(dimensions, maxDimensions) { - var this$1 = this; - dimensions = assign({}, dimensions); - each(dimensions, function(_, prop) { - return dimensions = dimensions[prop] > maxDimensions[prop] ? this$1.ratio(dimensions, prop, maxDimensions[prop]) : dimensions; - }); - return dimensions; - }, - cover: function cover(dimensions, maxDimensions) { - var this$1 = this; - dimensions = this.contain(dimensions, maxDimensions); - each(dimensions, function(_, prop) { - return dimensions = dimensions[prop] < maxDimensions[prop] ? this$1.ratio(dimensions, prop, maxDimensions[prop]) : dimensions; - }); - return dimensions; - } - }; - function attr(element, name, value) { - if (isObject(name)) { - for (var key in name) { - attr(element, key, name[key]); - } - return; - } - if (isUndefined(value)) { - element = toNode(element); - return element && element.getAttribute(name); - } else { - toNodes(element).forEach(function(element) { - if (isFunction(value)) { - value = value.call(element, attr(element, name)); - } - if (value === null) { - removeAttr(element, name); - } else { - element.setAttribute(name, value); - } - }); - } - } - function hasAttr(element, name) { - return toNodes(element).some(function(element) { - return element.hasAttribute(name); - }); - } - function removeAttr(element, name) { - element = toNodes(element); - name.split(" ").forEach(function(name) { - return element.forEach(function(element) { - return element.removeAttribute(name); - }); - }); - } - function filterAttr(element, attribute, pattern, replacement) { - attr(element, attribute, function(value) { - return value ? value.replace(pattern, replacement) : value; - }); - } - function data(element, attribute) { - for (var i = 0, attrs = [ attribute, "data-" + attribute ]; i < attrs.length; i++) { - if (hasAttr(element, attrs[i])) { - return attr(element, attrs[i]); - } - } - } - function query(selector, context) { - return toNode(selector) || find(selector, isContextSelector(selector) ? context : document); - } - function queryAll(selector, context) { - var nodes = toNodes(selector); - return nodes.length && nodes || findAll(selector, isContextSelector(selector) ? context : document); - } - function find(selector, context) { - return toNode(_query(selector, context, "querySelector")); - } - function findAll(selector, context) { - return toNodes(_query(selector, context, "querySelectorAll")); - } - function _query(selector, context, queryFn) { - if (context === void 0) context = document; - if (!selector || !isString(selector)) { - return null; - } - selector = selector.replace(contextSanitizeRe, "$1 *"); - var removes; - if (isContextSelector(selector)) { - removes = []; - selector = selector.split(",").map(function(selector, i) { - var ctx = context; - selector = selector.trim(); - if (selector[0] === "!") { - var selectors = selector.substr(1).trim().split(" "); - ctx = closest(context.parentNode, selectors[0]); - selector = selectors.slice(1).join(" "); - } - if (!ctx) { - return null; - } - if (!ctx.id) { - ctx.id = "uk-" + Date.now() + i; - removes.push(function() { - return removeAttr(ctx, "id"); - }); - } - return "#" + escape(ctx.id) + " " + selector; - }).filter(Boolean).join(","); - context = document; - } - try { - return context[queryFn](selector); - } catch (e) { - return null; - } finally { - removes && removes.forEach(function(remove) { - return remove(); - }); - } - } - var contextSelectorRe = /(^|,)\s*[!>+~]/; - var contextSanitizeRe = /([!>+~])(?=\s+[!>+~]|\s*$)/g; - function isContextSelector(selector) { - return isString(selector) && selector.match(contextSelectorRe); - } - var elProto = Element.prototype; - var matchesFn = elProto.matches || elProto.webkitMatchesSelector || elProto.msMatchesSelector; - function matches(element, selector) { - return toNodes(element).some(function(element) { - return matchesFn.call(element, selector); - }); - } - var closestFn = elProto.closest || function(selector) { - var ancestor = this; - do { - if (matches(ancestor, selector)) { - return ancestor; - } - ancestor = ancestor.parentNode; - } while (ancestor && ancestor.nodeType === 1); - }; - function closest(element, selector) { - if (startsWith(selector, ">")) { - selector = selector.slice(1); - } - return isNode(element) ? element.parentNode && closestFn.call(element, selector) : toNodes(element).map(function(element) { - return element.parentNode && closestFn.call(element, selector); - }).filter(Boolean); - } - function parents(element, selector) { - var elements = []; - var parent = toNode(element).parentNode; - while (parent && parent.nodeType === 1) { - if (matches(parent, selector)) { - elements.push(parent); - } - parent = parent.parentNode; - } - return elements; - } - var escapeFn = window.CSS && CSS.escape || function(css) { - return css.replace(/([^\x7f-\uFFFF\w-])/g, function(match) { - return "\\" + match; - }); - }; - function escape(css) { - return isString(css) ? escapeFn.call(null, css) : ""; - } - var voidElements = { - area: true, - base: true, - br: true, - col: true, - embed: true, - hr: true, - img: true, - input: true, - keygen: true, - link: true, - menuitem: true, - meta: true, - param: true, - source: true, - track: true, - wbr: true - }; - function isVoidElement(element) { - return toNodes(element).some(function(element) { - return voidElements[element.tagName.toLowerCase()]; - }); - } - function isVisible(element) { - return toNodes(element).some(function(element) { - return element.offsetHeight || element.getBoundingClientRect().height; - }); - } - var selInput = "input,select,textarea,button"; - function isInput(element) { - return toNodes(element).some(function(element) { - return matches(element, selInput); - }); - } - function filter(element, selector) { - return toNodes(element).filter(function(element) { - return matches(element, selector); - }); - } - function within(element, selector) { - return !isString(selector) ? element === selector || (isDocument(selector) ? selector.documentElement : toNode(selector)).contains(toNode(element)) : matches(element, selector) || closest(element, selector); - } - function on() { - var args = [], len = arguments.length; - while (len--) args[len] = arguments[len]; - var ref = getArgs(args); - var target = ref[0]; - var type = ref[1]; - var selector = ref[2]; - var listener = ref[3]; - var useCapture = ref[4]; - target = toEventTarget(target); - if (selector) { - listener = delegate(target, selector, listener); - } - if (listener.length > 1) { - listener = detail(listener); - } - type.split(" ").forEach(function(type) { - return target && target.addEventListener(type, listener, useCapture); - }); - return function() { - return off(target, type, listener, useCapture); - }; - } - function off(target, type, listener, useCapture) { - if (useCapture === void 0) useCapture = false; - target = toEventTarget(target); - target && type.split(" ").forEach(function(type) { - return target.removeEventListener(type, listener, useCapture); - }); - } - function once() { - var args = [], len = arguments.length; - while (len--) args[len] = arguments[len]; - var ref = getArgs(args); - var element = ref[0]; - var type = ref[1]; - var selector = ref[2]; - var listener = ref[3]; - var useCapture = ref[4]; - var condition = ref[5]; - var off = on(element, type, selector, function(e) { - var result = !condition || condition(e); - if (result) { - off(); - listener(e, result); - } - }, useCapture); - return off; - } - function trigger(target, event, detail) { - return toEventTargets(target).reduce(function(notCanceled, target) { - return notCanceled && target.dispatchEvent(createEvent(event, true, true, detail)); - }, true); - } - function createEvent(e, bubbles, cancelable, detail) { - if (bubbles === void 0) bubbles = true; - if (cancelable === void 0) cancelable = false; - if (isString(e)) { - var event = document.createEvent("CustomEvent"); - event.initCustomEvent(e, bubbles, cancelable, detail); - e = event; - } - return e; - } - function getArgs(args) { - if (isString(args[0])) { - args[0] = find(args[0]); - } - if (isFunction(args[2])) { - args.splice(2, 0, false); - } - return args; - } - function delegate(element, selector, listener) { - var this$1 = this; - return function(e) { - var target = e.target; - var current = selector[0] === ">" ? findAll(selector, element).reverse().filter(function(element) { - return within(target, element); - })[0] : closest(target, selector); - if (current) { - e.delegate = element; - e.current = current; - listener.call(this$1, e); - } - }; - } - function detail(listener) { - return function(e) { - return isArray(e.detail) ? listener.apply(listener, [ e ].concat(e.detail)) : listener(e); - }; - } - function isEventTarget(target) { - return "EventTarget" in window ? target instanceof EventTarget : target && "addEventListener" in target; - } - function toEventTarget(target) { - return isEventTarget(target) ? target : toNode(target); - } - function toEventTargets(target) { - return isEventTarget(target) ? [ target ] : isArray(target) ? target.map(toEventTarget).filter(Boolean) : toNodes(target); - } - function preventClick() { - var timer = setTimeout(once(document, "click", function(e) { - e.preventDefault(); - e.stopImmediatePropagation(); - clearTimeout(timer); - }, true)); - trigger(document, "touchcancel"); - } - var Promise = "Promise" in window ? window.Promise : PromiseFn; - var Deferred = function Deferred() { - var this$1 = this; - this.promise = new Promise(function(resolve, reject) { - this$1.reject = reject; - this$1.resolve = resolve; - }); - }; - var RESOLVED = 0; - var REJECTED = 1; - var PENDING = 2; - var async = "setImmediate" in window ? setImmediate : setTimeout; - function PromiseFn(executor) { - this.state = PENDING; - this.value = undefined; - this.deferred = []; - var promise = this; - try { - executor(function(x) { - promise.resolve(x); - }, function(r) { - promise.reject(r); - }); - } catch (e) { - promise.reject(e); - } - } - PromiseFn.reject = function(r) { - return new PromiseFn(function(resolve, reject) { - reject(r); - }); - }; - PromiseFn.resolve = function(x) { - return new PromiseFn(function(resolve, reject) { - resolve(x); - }); - }; - PromiseFn.all = function all(iterable) { - return new PromiseFn(function(resolve, reject) { - var result = []; - var count = 0; - if (iterable.length === 0) { - resolve(result); - } - function resolver(i) { - return function(x) { - result[i] = x; - count += 1; - if (count === iterable.length) { - resolve(result); - } - }; - } - for (var i = 0; i < iterable.length; i += 1) { - PromiseFn.resolve(iterable[i]).then(resolver(i), reject); - } - }); - }; - PromiseFn.race = function race(iterable) { - return new PromiseFn(function(resolve, reject) { - for (var i = 0; i < iterable.length; i += 1) { - PromiseFn.resolve(iterable[i]).then(resolve, reject); - } - }); - }; - var p = PromiseFn.prototype; - p.resolve = function resolve(x) { - var promise = this; - if (promise.state === PENDING) { - if (x === promise) { - throw new TypeError("Promise settled with itself."); - } - var called = false; - try { - var then = x && x.then; - if (x !== null && isObject(x) && isFunction(then)) { - then.call(x, function(x) { - if (!called) { - promise.resolve(x); - } - called = true; - }, function(r) { - if (!called) { - promise.reject(r); - } - called = true; - }); - return; - } - } catch (e) { - if (!called) { - promise.reject(e); - } - return; - } - promise.state = RESOLVED; - promise.value = x; - promise.notify(); - } - }; - p.reject = function reject(reason) { - var promise = this; - if (promise.state === PENDING) { - if (reason === promise) { - throw new TypeError("Promise settled with itself."); - } - promise.state = REJECTED; - promise.value = reason; - promise.notify(); - } - }; - p.notify = function notify() { - var this$1 = this; - async(function() { - if (this$1.state !== PENDING) { - while (this$1.deferred.length) { - var ref = this$1.deferred.shift(); - var onResolved = ref[0]; - var onRejected = ref[1]; - var resolve = ref[2]; - var reject = ref[3]; - try { - if (this$1.state === RESOLVED) { - if (isFunction(onResolved)) { - resolve(onResolved.call(undefined, this$1.value)); - } else { - resolve(this$1.value); - } - } else if (this$1.state === REJECTED) { - if (isFunction(onRejected)) { - resolve(onRejected.call(undefined, this$1.value)); - } else { - reject(this$1.value); - } - } - } catch (e) { - reject(e); - } - } - } - }); - }; - p.then = function then(onResolved, onRejected) { - var this$1 = this; - return new PromiseFn(function(resolve, reject) { - this$1.deferred.push([ onResolved, onRejected, resolve, reject ]); - this$1.notify(); - }); - }; - p.catch = function(onRejected) { - return this.then(undefined, onRejected); - }; - function ajax(url, options) { - return new Promise(function(resolve, reject) { - var env = assign({ - data: null, - method: "GET", - headers: {}, - xhr: new XMLHttpRequest(), - beforeSend: noop, - responseType: "" - }, options); - env.beforeSend(env); - var xhr = env.xhr; - for (var prop in env) { - if (prop in xhr) { - try { - xhr[prop] = env[prop]; - } catch (e) {} - } - } - xhr.open(env.method.toUpperCase(), url); - for (var header in env.headers) { - xhr.setRequestHeader(header, env.headers[header]); - } - on(xhr, "load", function() { - if (xhr.status === 0 || xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) { - resolve(xhr); - } else { - reject(assign(Error(xhr.statusText), { - xhr: xhr, - status: xhr.status - })); - } - }); - on(xhr, "error", function() { - return reject(assign(Error("Network Error"), { - xhr: xhr - })); - }); - on(xhr, "timeout", function() { - return reject(assign(Error("Network Timeout"), { - xhr: xhr - })); - }); - xhr.send(env.data); - }); - } - function getImage(src) { - return new Promise(function(resolve, reject) { - var img = new Image(); - img.onerror = reject; - img.onload = function() { - return resolve(img); - }; - img.src = src; - }); - } - function isReady() { - return document.readyState === "complete" || document.readyState !== "loading" && !document.documentElement.doScroll; - } - function ready(fn) { - if (isReady()) { - fn(); - return; - } - var handle = function() { - unbind1(); - unbind2(); - fn(); - }; - var unbind1 = on(document, "DOMContentLoaded", handle); - var unbind2 = on(window, "load", handle); - } - function index(element, ref) { - return ref ? toNodes(element).indexOf(toNode(ref)) : toNodes((element = toNode(element)) && element.parentNode.children).indexOf(element); - } - function getIndex(i, elements, current, finite) { - if (current === void 0) current = 0; - if (finite === void 0) finite = false; - elements = toNodes(elements); - var length = elements.length; - i = isNumeric(i) ? toNumber(i) : i === "next" ? current + 1 : i === "previous" ? current - 1 : index(elements, i); - if (finite) { - return clamp(i, 0, length - 1); - } - i %= length; - return i < 0 ? i + length : i; - } - function empty(element) { - element = toNode(element); - element.innerHTML = ""; - return element; - } - function html(parent, html) { - parent = toNode(parent); - return isUndefined(html) ? parent.innerHTML : append(parent.hasChildNodes() ? empty(parent) : parent, html); - } - function prepend(parent, element) { - parent = toNode(parent); - if (!parent.hasChildNodes()) { - return append(parent, element); - } else { - return insertNodes(element, function(element) { - return parent.insertBefore(element, parent.firstChild); - }); - } - } - function append(parent, element) { - parent = toNode(parent); - return insertNodes(element, function(element) { - return parent.appendChild(element); - }); - } - function before(ref, element) { - ref = toNode(ref); - return insertNodes(element, function(element) { - return ref.parentNode.insertBefore(element, ref); - }); - } - function after(ref, element) { - ref = toNode(ref); - return insertNodes(element, function(element) { - return ref.nextSibling ? before(ref.nextSibling, element) : append(ref.parentNode, element); - }); - } - function insertNodes(element, fn) { - element = isString(element) ? fragment(element) : element; - return element ? "length" in element ? toNodes(element).map(fn) : fn(element) : null; - } - function remove(element) { - toNodes(element).map(function(element) { - return element.parentNode && element.parentNode.removeChild(element); - }); - } - function wrapAll(element, structure) { - structure = toNode(before(element, structure)); - while (structure.firstChild) { - structure = structure.firstChild; - } - append(structure, element); - return structure; - } - function wrapInner(element, structure) { - return toNodes(toNodes(element).map(function(element) { - return element.hasChildNodes ? wrapAll(toNodes(element.childNodes), structure) : append(element, structure); - })); - } - function unwrap(element) { - toNodes(element).map(function(element) { - return element.parentNode; - }).filter(function(value, index, self) { - return self.indexOf(value) === index; - }).forEach(function(parent) { - before(parent, parent.childNodes); - remove(parent); - }); - } - var fragmentRE = /^\s*<(\w+|!)[^>]*>/; - var singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>)?$/; - function fragment(html) { - var matches = singleTagRE.exec(html); - if (matches) { - return document.createElement(matches[1]); - } - var container = document.createElement("div"); - if (fragmentRE.test(html)) { - container.insertAdjacentHTML("beforeend", html.trim()); - } else { - container.textContent = html; - } - return container.childNodes.length > 1 ? toNodes(container.childNodes) : container.firstChild; - } - function apply(node, fn) { - if (!node || node.nodeType !== 1) { - return; - } - fn(node); - node = node.firstElementChild; - while (node) { - apply(node, fn); - node = node.nextElementSibling; - } - } - function addClass(element) { - var args = [], len = arguments.length - 1; - while (len-- > 0) args[len] = arguments[len + 1]; - apply$1(element, args, "add"); - } - function removeClass(element) { - var args = [], len = arguments.length - 1; - while (len-- > 0) args[len] = arguments[len + 1]; - apply$1(element, args, "remove"); - } - function removeClasses(element, cls) { - filterAttr(element, "class", new RegExp("(^|\\s)" + cls + "(?!\\S)", "g"), ""); - } - function replaceClass(element) { - var args = [], len = arguments.length - 1; - while (len-- > 0) args[len] = arguments[len + 1]; - args[0] && removeClass(element, args[0]); - args[1] && addClass(element, args[1]); - } - function hasClass(element, cls) { - return toNodes(element).some(function(element) { - return element.classList.contains(cls); - }); - } - function toggleClass(element) { - var args = [], len = arguments.length - 1; - while (len-- > 0) args[len] = arguments[len + 1]; - if (!args.length) { - return; - } - args = getArgs$1(args); - var force = !isString(args[args.length - 1]) ? args.pop() : []; - args = args.filter(Boolean); - toNodes(element).forEach(function(ref) { - var classList = ref.classList; - for (var i = 0; i < args.length; i++) { - supports.Force ? classList.toggle.apply(classList, [ args[i] ].concat(force)) : classList[(!isUndefined(force) ? force : !classList.contains(args[i])) ? "add" : "remove"](args[i]); - } - }); - } - function apply$1(element, args, fn) { - args = getArgs$1(args).filter(Boolean); - args.length && toNodes(element).forEach(function(ref) { - var classList = ref.classList; - supports.Multiple ? classList[fn].apply(classList, args) : args.forEach(function(cls) { - return classList[fn](cls); - }); - }); - } - function getArgs$1(args) { - return args.reduce(function(args, arg) { - return args.concat.call(args, isString(arg) && includes(arg, " ") ? arg.trim().split(" ") : arg); - }, []); - } - var supports = {}; - (function() { - var list = document.createElement("_").classList; - if (list) { - list.add("a", "b"); - list.toggle("c", false); - supports.Multiple = list.contains("b"); - supports.Force = !list.contains("c"); - } - list = null; - })(); - var cssNumber = { - "animation-iteration-count": true, - "column-count": true, - "fill-opacity": true, - "flex-grow": true, - "flex-shrink": true, - "font-weight": true, - "line-height": true, - opacity: true, - order: true, - orphans: true, - widows: true, - "z-index": true, - zoom: true - }; - function css(element, property, value) { - return toNodes(element).map(function(element) { - if (isString(property)) { - property = propName(property); - if (isUndefined(value)) { - return getStyle(element, property); - } else if (!value && value !== 0) { - element.style.removeProperty(property); - } else { - element.style[property] = isNumeric(value) && !cssNumber[property] ? value + "px" : value; - } - } else if (isArray(property)) { - var styles = getStyles(element); - return property.reduce(function(props, property) { - props[property] = styles[propName(property)]; - return props; - }, {}); - } else if (isObject(property)) { - each(property, function(value, property) { - return css(element, property, value); - }); - } - return element; - })[0]; - } - function getStyles(element, pseudoElt) { - element = toNode(element); - return element.ownerDocument.defaultView.getComputedStyle(element, pseudoElt); - } - function getStyle(element, property, pseudoElt) { - return getStyles(element, pseudoElt)[property]; - } - var vars = {}; - function getCssVar(name) { - if (!(name in vars)) { - var element = append(document.documentElement, document.createElement("div")); - addClass(element, "var-" + name); - try { - vars[name] = getStyle(element, "content", ":before").replace(/^["'](.*)["']$/, "$1"); - vars[name] = JSON.parse(vars[name]); - } catch (e) {} - document.documentElement.removeChild(element); - } - return vars[name]; - } - var cssProps = {}; - function propName(name) { - var ret = cssProps[name]; - if (!ret) { - ret = cssProps[name] = vendorPropName(name) || name; - } - return ret; - } - var cssPrefixes = [ "webkit", "moz", "ms" ]; - var ref$1 = document.createElement("_"); - var style = ref$1.style; - function vendorPropName(name) { - name = hyphenate(name); - if (name in style) { - return name; - } - var i = cssPrefixes.length, prefixedName; - while (i--) { - prefixedName = "-" + cssPrefixes[i] + "-" + name; - if (prefixedName in style) { - return prefixedName; - } - } - } - function transition(element, props, duration, timing) { - if (duration === void 0) duration = 400; - if (timing === void 0) timing = "linear"; - return Promise.all(toNodes(element).map(function(element) { - return new Promise(function(resolve, reject) { - for (var name in props) { - var value = css(element, name); - if (value === "") { - css(element, name, value); - } - } - var timer = setTimeout(function() { - return trigger(element, "transitionend"); - }, duration); - once(element, "transitionend transitioncanceled", function(ref) { - var type = ref.type; - clearTimeout(timer); - removeClass(element, "uk-transition"); - css(element, { - "transition-property": "", - "transition-duration": "", - "transition-timing-function": "" - }); - type === "transitioncanceled" ? reject() : resolve(); - }, false, function(ref) { - var target = ref.target; - return element === target; - }); - addClass(element, "uk-transition"); - css(element, assign({ - "transition-property": Object.keys(props).map(propName).join(","), - "transition-duration": duration + "ms", - "transition-timing-function": timing - }, props)); - }); - })); - } - var Transition = { - start: transition, - stop: function stop(element) { - trigger(element, "transitionend"); - return Promise.resolve(); - }, - cancel: function cancel(element) { - trigger(element, "transitioncanceled"); - }, - inProgress: function inProgress(element) { - return hasClass(element, "uk-transition"); - } - }; - var animationPrefix = "uk-animation-"; - var clsCancelAnimation = "uk-cancel-animation"; - function animate(element, animation, duration, origin, out) { - var arguments$1 = arguments; - if (duration === void 0) duration = 200; - return Promise.all(toNodes(element).map(function(element) { - return new Promise(function(resolve, reject) { - if (hasClass(element, clsCancelAnimation)) { - requestAnimationFrame(function() { - return Promise.resolve().then(function() { - return animate.apply(void 0, arguments$1).then(resolve, reject); - }); - }); - return; - } - var cls = animation + " " + animationPrefix + (out ? "leave" : "enter"); - if (startsWith(animation, animationPrefix)) { - if (origin) { - cls += " uk-transform-origin-" + origin; - } - if (out) { - cls += " " + animationPrefix + "reverse"; - } - } - reset(); - once(element, "animationend animationcancel", function(ref) { - var type = ref.type; - var hasReset = false; - if (type === "animationcancel") { - reject(); - reset(); - } else { - resolve(); - Promise.resolve().then(function() { - hasReset = true; - reset(); - }); - } - requestAnimationFrame(function() { - if (!hasReset) { - addClass(element, clsCancelAnimation); - requestAnimationFrame(function() { - return removeClass(element, clsCancelAnimation); - }); - } - }); - }, false, function(ref) { - var target = ref.target; - return element === target; - }); - css(element, "animationDuration", duration + "ms"); - addClass(element, cls); - function reset() { - css(element, "animationDuration", ""); - removeClasses(element, animationPrefix + "\\S*"); - } - }); - })); - } - var inProgress = new RegExp(animationPrefix + "(enter|leave)"); - var Animation = { - in: function in$1(element, animation, duration, origin) { - return animate(element, animation, duration, origin, false); - }, - out: function out(element, animation, duration, origin) { - return animate(element, animation, duration, origin, true); - }, - inProgress: function inProgress$1(element) { - return inProgress.test(attr(element, "class")); - }, - cancel: function cancel(element) { - trigger(element, "animationcancel"); - } - }; - function $(selector, context) { - return !isString(selector) ? toNode(selector) : isHtml(selector) ? toNode(fragment(selector)) : find(selector, context); - } - function $$(selector, context) { - return !isString(selector) ? toNodes(selector) : isHtml(selector) ? toNodes(fragment(selector)) : findAll(selector, context); - } - function isHtml(str) { - return str[0] === "<" || str.match(/^\s* boundary[alignFlip]) { - var centerOffset = dim[prop] / 2; - var centerTargetOffset = targetAttach[dir] === "center" ? -targetDim[prop] / 2 : 0; - elAttach[dir] === "center" && (apply(centerOffset, centerTargetOffset) || apply(-centerOffset, -centerTargetOffset)) || apply(elemOffset, targetOffset); - } - function apply(elemOffset, targetOffset) { - var newVal = position[align] + elemOffset + targetOffset - elOffset[dir] * 2; - if (newVal >= boundary[align] && newVal + dim[prop] <= boundary[alignFlip]) { - position[align] = newVal; - [ "element", "target" ].forEach(function(el) { - flipped[el][dir] = !elemOffset ? flipped[el][dir] : flipped[el][dir] === dirs[prop][1] ? dirs[prop][2] : dirs[prop][1]; - }); - return true; - } - } - }); - } - offset(element, position); - return flipped; - } - function offset(element, coordinates) { - element = toNode(element); - if (coordinates) { - var currentOffset = offset(element); - var pos = css(element, "position"); - [ "left", "top" ].forEach(function(prop) { - if (prop in coordinates) { - var value = css(element, prop); - element.style[prop] = coordinates[prop] - currentOffset[prop] + toFloat(pos === "absolute" && value === "auto" ? position(element)[prop] : value) + "px"; - } - }); - return; - } - return getDimensions(element); - } - function getDimensions(element) { - element = toNode(element); - var ref = window$1(element); - var top = ref.pageYOffset; - var left = ref.pageXOffset; - if (isWindow(element)) { - var height = element.innerHeight; - var width = element.innerWidth; - return { - top: top, - left: left, - height: height, - width: width, - bottom: top + height, - right: left + width - }; - } - var display = false; - if (!isVisible(element)) { - display = element.style.display; - element.style.display = "block"; - } - var rect = element.getBoundingClientRect(); - if (display !== false) { - element.style.display = display; - } - return { - height: rect.height, - width: rect.width, - top: rect.top + top, - left: rect.left + left, - bottom: rect.bottom + top, - right: rect.right + left - }; - } - function position(element) { - element = toNode(element); - var parent = offsetParent(element); - var parentOffset = parent === docEl(element) ? { - top: 0, - left: 0 - } : offset(parent); - var ref = [ "top", "left" ].reduce(function(props, prop) { - var propName$$1 = ucfirst(prop); - props[prop] -= parentOffset[prop] + (toFloat(css(element, "margin" + propName$$1)) || 0) + (toFloat(css(parent, "border" + propName$$1 + "Width")) || 0); - return props; - }, offset(element)); - var top = ref.top; - var left = ref.left; - return { - top: top, - left: left - }; - } - function offsetParent(element) { - var parent = toNode(element).offsetParent; - while (parent && css(parent, "position") === "static") { - parent = parent.offsetParent; - } - return parent || docEl(element); - } - var height = dimension("height"); - var width = dimension("width"); - function dimension(prop) { - var propName$$1 = ucfirst(prop); - return function(element, value) { - element = toNode(element); - if (isUndefined(value)) { - if (isWindow(element)) { - return element["inner" + propName$$1]; - } - if (isDocument(element)) { - var doc = element.documentElement; - return Math.max(doc["offset" + propName$$1], doc["scroll" + propName$$1]); - } - value = css(element, prop); - value = value === "auto" ? element["offset" + propName$$1] : toFloat(value) || 0; - return value - boxModelAdjust(prop, element); - } else { - css(element, prop, !value && value !== 0 ? "" : +value + boxModelAdjust(prop, element) + "px"); - } - }; - } - function boxModelAdjust(prop, element) { - return css(element, "boxSizing") === "border-box" ? dirs[prop].slice(1).map(ucfirst).reduce(function(value, prop) { - return value + toFloat(css(element, "padding" + prop)) + toFloat(css(element, "border" + prop + "Width")); - }, 0) : 0; - } - function moveTo(position, attach, dim, factor) { - each(dirs, function(ref, prop) { - var dir = ref[0]; - var align = ref[1]; - var alignFlip = ref[2]; - if (attach[dir] === alignFlip) { - position[align] += dim[prop] * factor; - } else if (attach[dir] === "center") { - position[align] += dim[prop] * factor / 2; - } - }); - } - function getPos(pos) { - var x = /left|center|right/; - var y = /top|center|bottom/; - pos = (pos || "").split(" "); - if (pos.length === 1) { - pos = x.test(pos[0]) ? pos.concat([ "center" ]) : y.test(pos[0]) ? [ "center" ].concat(pos) : [ "center", "center" ]; - } - return { - x: x.test(pos[0]) ? pos[0] : "center", - y: y.test(pos[1]) ? pos[1] : "center" - }; - } - function getOffsets(offsets, width, height) { - var ref = (offsets || "").split(" "); - var x = ref[0]; - var y = ref[1]; - return { - x: x ? toFloat(x) * (endsWith(x, "%") ? width / 100 : 1) : 0, - y: y ? toFloat(y) * (endsWith(y, "%") ? height / 100 : 1) : 0 - }; - } - function flipPosition(pos) { - switch (pos) { - case "left": - return "right"; - - case "right": - return "left"; - - case "top": - return "bottom"; - - case "bottom": - return "top"; - - default: - return pos; - } - } - function isInView(element, top, left) { - if (top === void 0) top = 0; - if (left === void 0) left = 0; - element = toNode(element); - var win = window$1(element); - return isVisible(element) && intersectRect(element.getBoundingClientRect(), { - top: top, - left: left, - bottom: top + height(win), - right: left + width(win) - }); - } - function scrolledOver(element) { - if (!isVisible(element)) { - return 0; - } - element = toNode(element); - var win = window$1(element); - var doc = document$1(element); - var elHeight = element.offsetHeight; - var top = positionTop(element); - var vp = height(win); - var vh = vp + Math.min(0, top - vp); - var diff = Math.max(0, vp - (height(doc) - (top + elHeight))); - return clamp((vh + win.pageYOffset - top) / ((vh + (elHeight - (diff < vp ? diff : 0))) / 100) / 100); - } - function positionTop(element) { - var top = 0; - do { - top += element.offsetTop; - } while (element = element.offsetParent); - return top; - } - function window$1(element) { - return isWindow(element) ? element : document$1(element).defaultView; - } - function document$1(element) { - return toNode(element).ownerDocument; - } - function docEl(element) { - return document$1(element).documentElement; - } - var isRtl = attr(document.documentElement, "dir") === "rtl"; - var hasTouchEvents = "ontouchstart" in window; - var hasPointerEvents = window.PointerEvent; - var hasTouch = hasTouchEvents || window.DocumentTouch && document instanceof DocumentTouch || navigator.maxTouchPoints; - var pointerDown = !hasTouch ? "mousedown" : "mousedown " + (hasTouchEvents ? "touchstart" : "pointerdown"); - var pointerMove = !hasTouch ? "mousemove" : "mousemove " + (hasTouchEvents ? "touchmove" : "pointermove"); - var pointerUp = !hasTouch ? "mouseup" : "mouseup " + (hasTouchEvents ? "touchend" : "pointerup"); - var pointerEnter = hasTouch && hasPointerEvents ? "pointerenter" : "mouseenter"; - var pointerLeave = hasTouch && hasPointerEvents ? "pointerleave" : "mouseleave"; - var fastdom = { - reads: [], - writes: [], - read: function read(task) { - this.reads.push(task); - scheduleFlush(); - return task; - }, - write: function write(task) { - this.writes.push(task); - scheduleFlush(); - return task; - }, - clear: function clear(task) { - return remove$1(this.reads, task) || remove$1(this.writes, task); - }, - flush: function flush() { - runTasks(this.reads); - runTasks(this.writes.splice(0, this.writes.length)); - this.scheduled = false; - if (this.reads.length || this.writes.length) { - scheduleFlush(); - } - } - }; - function scheduleFlush() { - if (!fastdom.scheduled) { - fastdom.scheduled = true; - requestAnimationFrame(fastdom.flush.bind(fastdom)); - } - } - function runTasks(tasks) { - var task; - while (task = tasks.shift()) { - task(); - } - } - function remove$1(array, item) { - var index = array.indexOf(item); - return !!~index && !!array.splice(index, 1); - } - function MouseTracker() {} - MouseTracker.prototype = { - positions: [], - position: null, - init: function init() { - var this$1 = this; - this.positions = []; - this.position = null; - var ticking = false; - this.unbind = on(document, "mousemove", function(e) { - if (ticking) { - return; - } - setTimeout(function() { - var time = Date.now(); - var ref = this$1.positions; - var length = ref.length; - if (length && time - this$1.positions[length - 1].time > 100) { - this$1.positions.splice(0, length); - } - this$1.positions.push({ - time: time, - x: e.pageX, - y: e.pageY - }); - if (this$1.positions.length > 5) { - this$1.positions.shift(); - } - ticking = false; - }, 5); - ticking = true; - }); - }, - cancel: function cancel() { - if (this.unbind) { - this.unbind(); - } - }, - movesTo: function movesTo(target) { - if (this.positions.length < 2) { - return false; - } - var p = offset(target); - var position$$1 = this.positions[this.positions.length - 1]; - var ref = this.positions; - var prevPos = ref[0]; - if (p.left <= position$$1.x && position$$1.x <= p.right && p.top <= position$$1.y && position$$1.y <= p.bottom) { - return false; - } - var points = [ [ { - x: p.left, - y: p.top - }, { - x: p.right, - y: p.bottom - } ], [ { - x: p.right, - y: p.top - }, { - x: p.left, - y: p.bottom - } ] ]; - if (p.right <= position$$1.x) {} else if (p.left >= position$$1.x) { - points[0].reverse(); - points[1].reverse(); - } else if (p.bottom <= position$$1.y) { - points[0].reverse(); - } else if (p.top >= position$$1.y) { - points[1].reverse(); - } - return !!points.reduce(function(result, point) { - return result + (slope(prevPos, point[0]) < slope(position$$1, point[0]) && slope(prevPos, point[1]) > slope(position$$1, point[1])); - }, 0); - } - }; - function slope(a, b) { - return (b.y - a.y) / (b.x - a.x); - } - var strats = {}; - strats.args = strats.events = strats.init = strats.created = strats.beforeConnect = strats.connected = strats.ready = strats.beforeDisconnect = strats.disconnected = strats.destroy = function(parentVal, childVal) { - parentVal = parentVal && !isArray(parentVal) ? [ parentVal ] : parentVal; - return childVal ? parentVal ? parentVal.concat(childVal) : isArray(childVal) ? childVal : [ childVal ] : parentVal; - }; - strats.update = function(parentVal, childVal) { - return strats.args(parentVal, isFunction(childVal) ? { - read: childVal - } : childVal); - }; - strats.props = function(parentVal, childVal) { - if (isArray(childVal)) { - childVal = childVal.reduce(function(value, key) { - value[key] = String; - return value; - }, {}); - } - return strats.methods(parentVal, childVal); - }; - strats.computed = strats.defaults = strats.methods = function(parentVal, childVal) { - return childVal ? parentVal ? assign({}, parentVal, childVal) : childVal : parentVal; - }; - var defaultStrat = function(parentVal, childVal) { - return isUndefined(childVal) ? parentVal : childVal; - }; - function mergeOptions(parent, child) { - var options = {}; - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i]); - } - } - for (var key in parent) { - mergeKey(key); - } - for (var key$1 in child) { - if (!hasOwn(parent, key$1)) { - mergeKey(key$1); - } - } - function mergeKey(key) { - options[key] = (strats[key] || defaultStrat)(parent[key], child[key]); - } - return options; - } - var id = 0; - var Player = function Player(el) { - this.id = ++id; - this.el = toNode(el); - }; - Player.prototype.isVideo = function isVideo() { - return this.isYoutube() || this.isVimeo() || this.isHTML5(); - }; - Player.prototype.isHTML5 = function isHTML5() { - return this.el.tagName === "VIDEO"; - }; - Player.prototype.isIFrame = function isIFrame() { - return this.el.tagName === "IFRAME"; - }; - Player.prototype.isYoutube = function isYoutube() { - return this.isIFrame() && !!this.el.src.match(/\/\/.*?youtube(-nocookie)?\.[a-z]+\/(watch\?v=[^&\s]+|embed)|youtu\.be\/.*/); - }; - Player.prototype.isVimeo = function isVimeo() { - return this.isIFrame() && !!this.el.src.match(/vimeo\.com\/video\/.*/); - }; - Player.prototype.enableApi = function enableApi() { - var this$1 = this; - if (this.ready) { - return this.ready; - } - var youtube = this.isYoutube(); - var vimeo = this.isVimeo(); - var poller; - if (youtube || vimeo) { - return this.ready = new Promise(function(resolve) { - once(this$1.el, "load", function() { - if (youtube) { - var listener = function() { - return post(this$1.el, { - event: "listening", - id: this$1.id - }); - }; - poller = setInterval(listener, 100); - listener(); - } - }); - listen(function(data$$1) { - return youtube && data$$1.id === this$1.id && data$$1.event === "onReady" || vimeo && Number(data$$1.player_id) === this$1.id; - }).then(function() { - resolve(); - poller && clearInterval(poller); - }); - attr(this$1.el, "src", "" + this$1.el.src + (includes(this$1.el.src, "?") ? "&" : "?") + (youtube ? "enablejsapi=1" : "api=1&player_id=" + this$1.id)); - }); - } - return Promise.resolve(); - }; - Player.prototype.play = function play() { - var this$1 = this; - if (!this.isVideo()) { - return; - } - if (this.isIFrame()) { - this.enableApi().then(function() { - return post(this$1.el, { - func: "playVideo", - method: "play" - }); - }); - } else if (this.isHTML5()) { - try { - var promise = this.el.play(); - if (promise) { - promise.catch(noop); - } - } catch (e) {} - } - }; - Player.prototype.pause = function pause() { - var this$1 = this; - if (!this.isVideo()) { - return; - } - if (this.isIFrame()) { - this.enableApi().then(function() { - return post(this$1.el, { - func: "pauseVideo", - method: "pause" - }); - }); - } else if (this.isHTML5()) { - this.el.pause(); - } - }; - Player.prototype.mute = function mute() { - var this$1 = this; - if (!this.isVideo()) { - return; - } - if (this.isIFrame()) { - this.enableApi().then(function() { - return post(this$1.el, { - func: "mute", - method: "setVolume", - value: 0 - }); - }); - } else if (this.isHTML5()) { - this.el.muted = true; - attr(this.el, "muted", ""); - } - }; - function post(el, cmd) { - try { - el.contentWindow.postMessage(JSON.stringify(assign({ - event: "command" - }, cmd)), "*"); - } catch (e) {} - } - function listen(cb) { - return new Promise(function(resolve) { - once(window, "message", function(_, data$$1) { - return resolve(data$$1); - }, false, function(ref) { - var data$$1 = ref.data; - if (!data$$1 || !isString(data$$1)) { - return; - } - try { - data$$1 = JSON.parse(data$$1); - } catch (e) { - return; - } - return data$$1 && cb(data$$1); - }); - }); - } - var touch = {}; - var clickTimeout; - var swipeTimeout; - var tapTimeout; - var clicked; - function swipeDirection(ref) { - var x1 = ref.x1; - var x2 = ref.x2; - var y1 = ref.y1; - var y2 = ref.y2; - return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? x1 - x2 > 0 ? "Left" : "Right" : y1 - y2 > 0 ? "Up" : "Down"; - } - function cancelAll() { - clickTimeout && clearTimeout(clickTimeout); - swipeTimeout && clearTimeout(swipeTimeout); - tapTimeout && clearTimeout(tapTimeout); - clickTimeout = swipeTimeout = tapTimeout = null; - touch = {}; - } - ready(function() { - on(document, "click", function() { - return clicked = true; - }, true); - on(document, pointerDown, function(e) { - var target = e.target; - var ref = getPos$1(e); - var x = ref.x; - var y = ref.y; - var now = Date.now(); - var type = getType(e.type); - if (touch.type && touch.type !== type) { - return; - } - touch.el = "tagName" in target ? target : target.parentNode; - clickTimeout && clearTimeout(clickTimeout); - touch.x1 = x; - touch.y1 = y; - if (touch.last && now - touch.last <= 250) { - touch = {}; - } - touch.type = type; - touch.last = now; - clicked = e.button > 0; - }); - on(document, pointerMove, function(e) { - if (e.defaultPrevented) { - return; - } - var ref = getPos$1(e); - var x = ref.x; - var y = ref.y; - touch.x2 = x; - touch.y2 = y; - }); - on(document, pointerUp, function(ref) { - var type = ref.type; - var target = ref.target; - if (touch.type !== getType(type)) { - return; - } - if (touch.x2 && Math.abs(touch.x1 - touch.x2) > 30 || touch.y2 && Math.abs(touch.y1 - touch.y2) > 30) { - swipeTimeout = setTimeout(function() { - if (touch.el) { - trigger(touch.el, "swipe"); - trigger(touch.el, "swipe" + swipeDirection(touch)); - } - touch = {}; - }); - } else if ("last" in touch) { - tapTimeout = setTimeout(function() { - return trigger(touch.el, "tap"); - }); - if (touch.el && type !== "mouseup" && within(target, touch.el)) { - clickTimeout = setTimeout(function() { - clickTimeout = null; - if (touch.el && !clicked) { - trigger(touch.el, "click"); - } - touch = {}; - }, 350); - } - } else { - touch = {}; - } - }); - on(document, "touchcancel", cancelAll); - on(window, "scroll", cancelAll); - }); - var touching = false; - on(document, "touchstart", function() { - return touching = true; - }, true); - on(document, "click", function() { - touching = false; - }); - on(document, "touchcancel", function() { - return touching = false; - }, true); - function isTouch(e) { - return touching || e.pointerType === "touch"; - } - function getPos$1(e) { - var touches = e.touches; - var changedTouches = e.changedTouches; - var ref = touches && touches[0] || changedTouches && changedTouches[0] || e; - var x = ref.pageX; - var y = ref.pageY; - return { - x: x, - y: y - }; - } - function getType(type) { - return type.slice(0, 5); - } - var util = Object.freeze({ - ajax: ajax, - getImage: getImage, - transition: transition, - Transition: Transition, - animate: animate, - Animation: Animation, - attr: attr, - hasAttr: hasAttr, - removeAttr: removeAttr, - filterAttr: filterAttr, - data: data, - addClass: addClass, - removeClass: removeClass, - removeClasses: removeClasses, - replaceClass: replaceClass, - hasClass: hasClass, - toggleClass: toggleClass, - $: $, - $$: $$, - positionAt: positionAt, - offset: offset, - position: position, - height: height, - width: width, - flipPosition: flipPosition, - isInView: isInView, - scrolledOver: scrolledOver, - isReady: isReady, - ready: ready, - index: index, - getIndex: getIndex, - empty: empty, - html: html, - prepend: prepend, - append: append, - before: before, - after: after, - remove: remove, - wrapAll: wrapAll, - wrapInner: wrapInner, - unwrap: unwrap, - fragment: fragment, - apply: apply, - isRtl: isRtl, - hasTouch: hasTouch, - pointerDown: pointerDown, - pointerMove: pointerMove, - pointerUp: pointerUp, - pointerEnter: pointerEnter, - pointerLeave: pointerLeave, - on: on, - off: off, - once: once, - trigger: trigger, - createEvent: createEvent, - toEventTargets: toEventTargets, - preventClick: preventClick, - fastdom: fastdom, - isVoidElement: isVoidElement, - isVisible: isVisible, - selInput: selInput, - isInput: isInput, - filter: filter, - within: within, - bind: bind, - hasOwn: hasOwn, - hyphenate: hyphenate, - camelize: camelize, - ucfirst: ucfirst, - startsWith: startsWith, - endsWith: endsWith, - includes: includes, - isArray: isArray, - isFunction: isFunction, - isObject: isObject, - isPlainObject: isPlainObject, - isWindow: isWindow, - isDocument: isDocument, - isJQuery: isJQuery, - isNode: isNode, - isNodeCollection: isNodeCollection, - isBoolean: isBoolean, - isString: isString, - isNumber: isNumber, - isNumeric: isNumeric, - isUndefined: isUndefined, - toBoolean: toBoolean, - toNumber: toNumber, - toFloat: toFloat, - toNode: toNode, - toNodes: toNodes, - toList: toList, - toMs: toMs, - swap: swap, - assign: assign, - each: each, - sortBy: sortBy, - clamp: clamp, - noop: noop, - intersectRect: intersectRect, - pointInRect: pointInRect, - Dimensions: Dimensions, - MouseTracker: MouseTracker, - mergeOptions: mergeOptions, - Player: Player, - Promise: Promise, - Deferred: Deferred, - query: query, - queryAll: queryAll, - find: find, - findAll: findAll, - matches: matches, - closest: closest, - parents: parents, - escape: escape, - css: css, - getStyles: getStyles, - getStyle: getStyle, - getCssVar: getCssVar, - propName: propName, - isTouch: isTouch, - getPos: getPos$1 - }); - function componentAPI(UIkit) { - var DATA = UIkit.data; - UIkit.components = {}; - UIkit.component = function(id, options) { - var name = camelize(id); - if (isPlainObject(options)) { - options.name = name; - options = UIkit.extend(options); - } else if (isUndefined(options)) { - return UIkit.components[name]; - } else { - options.options.name = name; - } - UIkit.components[name] = options; - UIkit[name] = function(element, data) { - var i = arguments.length, argsArray = Array(i); - while (i--) argsArray[i] = arguments[i]; - if (isPlainObject(element)) { - return new UIkit.components[name]({ - data: element - }); - } - if (UIkit.components[name].options.functional) { - return new UIkit.components[name]({ - data: [].concat(argsArray) - }); - } - return element && element.nodeType ? init(element) : $$(element).map(init)[0]; - function init(element) { - var cmp = UIkit.getComponent(element, name); - if (cmp && data) { - cmp.$reset(data); - } - return cmp || new UIkit.components[name]({ - el: element, - data: data || {} - }); - } - }; - if (UIkit._initialized && !options.options.functional) { - fastdom.read(function() { - return UIkit[name]("[uk-" + id + "],[data-uk-" + id + "]"); - }); - } - return UIkit.components[name]; - }; - UIkit.getComponents = function(element) { - return element && element[DATA] || {}; - }; - UIkit.getComponent = function(element, name) { - return UIkit.getComponents(element)[name]; - }; - UIkit.connect = function(node) { - if (node[DATA]) { - for (var name in node[DATA]) { - node[DATA][name]._callConnected(); - } - } - for (var i = 0; i < node.attributes.length; i++) { - var name$1 = getComponentName(node.attributes[i].name); - if (name$1 && name$1 in UIkit.components) { - UIkit[name$1](node); - } - } - }; - UIkit.disconnect = function(node) { - for (var name in node[DATA]) { - node[DATA][name]._callDisconnected(); - } - }; - } - function getComponentName(attribute) { - return startsWith(attribute, "uk-") || startsWith(attribute, "data-uk-") ? camelize(attribute.replace("data-uk-", "").replace("uk-", "")) : false; - } - function boot(UIkit) { - var connect = UIkit.connect; - var disconnect = UIkit.disconnect; - if (!("MutationObserver" in window)) { - return; - } - if (document.body) { - init(); - } else { - new MutationObserver(function() { - if (document.body) { - this.disconnect(); - init(); - } - }).observe(document, { - childList: true, - subtree: true - }); - } - function init() { - apply(document.body, connect); - fastdom.flush(); - new MutationObserver(function(mutations) { - return mutations.forEach(applyMutation); - }).observe(document, { - childList: true, - subtree: true, - characterData: true, - attributes: true - }); - UIkit._initialized = true; - } - function applyMutation(mutation) { - var target = mutation.target; - var type = mutation.type; - var update = type !== "attributes" ? applyChildList(mutation) : applyAttribute(mutation); - update && UIkit.update(target); - } - function applyAttribute(ref) { - var target = ref.target; - var attributeName = ref.attributeName; - if (attributeName === "href") { - return true; - } - var name = getComponentName(attributeName); - if (!name || !(name in UIkit.components)) { - return; - } - if (hasAttr(target, attributeName)) { - UIkit[name](target); - return true; - } - var component = UIkit.getComponent(target, name); - if (component) { - component.$destroy(); - return true; - } - } - function applyChildList(ref) { - var addedNodes = ref.addedNodes; - var removedNodes = ref.removedNodes; - for (var i = 0; i < addedNodes.length; i++) { - apply(addedNodes[i], connect); - } - for (var i$1 = 0; i$1 < removedNodes.length; i$1++) { - apply(removedNodes[i$1], disconnect); - } - return true; - } - function apply(node, fn) { - if (node.nodeType !== 1 || hasAttr(node, "uk-no-boot")) { - return; - } - fn(node); - node = node.firstElementChild; - while (node) { - var next = node.nextElementSibling; - apply(node, fn); - node = next; - } - } - } - function globalAPI(UIkit) { - var DATA = UIkit.data; - UIkit.use = function(plugin) { - if (plugin.installed) { - return; - } - plugin.call(null, this); - plugin.installed = true; - return this; - }; - UIkit.mixin = function(mixin, component) { - component = (isString(component) ? UIkit.components[component] : component) || this; - mixin = mergeOptions({}, mixin); - mixin.mixins = component.options.mixins; - delete component.options.mixins; - component.options = mergeOptions(mixin, component.options); - }; - UIkit.extend = function(options) { - options = options || {}; - var Super = this; - var Sub = function UIkitComponent(options) { - this._init(options); - }; - Sub.prototype = Object.create(Super.prototype); - Sub.prototype.constructor = Sub; - Sub.options = mergeOptions(Super.options, options); - Sub["super"] = Super; - Sub.extend = Super.extend; - return Sub; - }; - UIkit.update = function(element, e) { - e = createEvent(e || "update"); - element = element ? toNode(element) : document.body; - apply(element, function(element) { - return update(element[DATA], e); - }); - while (element && element.parentNode) { - update(element.parentNode[DATA], e); - element = element.parentNode; - } - }; - var container; - Object.defineProperty(UIkit, "container", { - get: function get() { - return container || document.body; - }, - set: function set(element) { - container = $(element); - } - }); - function update(data, e) { - if (!data) { - return; - } - for (var name in data) { - if (data[name]._isReady) { - data[name]._callUpdate(e); - } - } - } - } - function hooksAPI(UIkit) { - UIkit.prototype._callHook = function(hook) { - var this$1 = this; - var handlers = this.$options[hook]; - if (handlers) { - handlers.forEach(function(handler) { - return handler.call(this$1); - }); - } - }; - UIkit.prototype._callConnected = function() { - var this$1 = this; - if (this._connected) { - return; - } - this._data = {}; - this._callHook("beforeConnect"); - this._connected = true; - this._initEvents(); - this._initObserver(); - this._callHook("connected"); - if (!this._isReady) { - ready(function() { - return this$1._callReady(); - }); - } - this._callUpdate(); - }; - UIkit.prototype._callDisconnected = function() { - if (!this._connected) { - return; - } - this._callHook("beforeDisconnect"); - if (this._observer) { - this._observer.disconnect(); - this._observer = null; - } - this._unbindEvents(); - this._callHook("disconnected"); - this._connected = false; - }; - UIkit.prototype._callReady = function() { - if (this._isReady) { - return; - } - this._isReady = true; - this._callHook("ready"); - this._resetComputeds(); - this._callUpdate(); - }; - UIkit.prototype._callUpdate = function(e) { - var this$1 = this; - e = createEvent(e || "update"); - var type = e.type; - if (includes([ "update", "load", "resize" ], type)) { - this._resetComputeds(); - } - var updates = this.$options.update; - var ref = this._frames; - var reads = ref.reads; - var writes = ref.writes; - if (!updates) { - return; - } - updates.forEach(function(ref, i) { - var read = ref.read; - var write = ref.write; - var events = ref.events; - if (type !== "update" && !includes(events, type)) { - return; - } - if (read && !includes(fastdom.reads, reads[i])) { - reads[i] = fastdom.read(function() { - var result = read.call(this$1, this$1._data, e); - if (result === false && write) { - fastdom.clear(writes[i]); - delete writes[i]; - } else if (isPlainObject(result)) { - assign(this$1._data, result); - } - delete reads[i]; - }); - } - if (write && !includes(fastdom.writes, writes[i])) { - writes[i] = fastdom.write(function() { - write.call(this$1, this$1._data, e); - delete writes[i]; - }); - } - }); - }; - } - function stateAPI(UIkit) { - var uid = 0; - UIkit.prototype.props = {}; - UIkit.prototype._init = function(options) { - options = options || {}; - options = this.$options = mergeOptions(this.constructor.options, options, this); - this.$el = null; - this.$name = UIkit.prefix + hyphenate(this.$options.name); - this.$props = {}; - this._frames = { - reads: {}, - writes: {} - }; - this._events = []; - this._uid = uid++; - this._initData(); - this._initMethods(); - this._initComputeds(); - this._callHook("created"); - if (options.el) { - this.$mount(options.el); - } - }; - UIkit.prototype._initData = function() { - var this$1 = this; - var ref = this.$options; - var defaults = ref.defaults; - var data$$1 = ref.data; - if (data$$1 === void 0) data$$1 = {}; - var args = ref.args; - if (args === void 0) args = []; - var props = ref.props; - if (props === void 0) props = {}; - var el = ref.el; - if (args.length && isArray(data$$1)) { - data$$1 = data$$1.slice(0, args.length).reduce(function(data$$1, value, index) { - if (isPlainObject(value)) { - assign(data$$1, value); - } else { - data$$1[args[index]] = value; - } - return data$$1; - }, {}); - } - for (var key in assign({}, defaults, props)) { - this$1.$props[key] = this$1[key] = hasOwn(data$$1, key) && !isUndefined(data$$1[key]) ? coerce(props[key], data$$1[key], el) : defaults ? defaults[key] && isArray(defaults[key]) ? defaults[key].concat() : defaults[key] : null; - } - }; - UIkit.prototype._initMethods = function() { - var this$1 = this; - var ref = this.$options; - var methods = ref.methods; - if (methods) { - for (var key in methods) { - this$1[key] = bind(methods[key], this$1); - } - } - }; - UIkit.prototype._initComputeds = function() { - var this$1 = this; - var ref = this.$options; - var computed = ref.computed; - this._resetComputeds(); - if (computed) { - for (var key in computed) { - registerComputed(this$1, key, computed[key]); - } - } - }; - UIkit.prototype._resetComputeds = function() { - this._computeds = {}; - }; - UIkit.prototype._initProps = function(props) { - var this$1 = this; - var key; - this._resetComputeds(); - props = props || getProps(this.$options, this.$name); - for (key in props) { - if (!isUndefined(props[key])) { - this$1.$props[key] = props[key]; - } - } - var exclude = [ this.$options.computed, this.$options.methods ]; - for (key in this$1.$props) { - if (key in props && notIn(exclude, key)) { - this$1[key] = this$1.$props[key]; - } - } - }; - UIkit.prototype._initEvents = function() { - var this$1 = this; - var ref = this.$options; - var events = ref.events; - if (events) { - events.forEach(function(event) { - if (!hasOwn(event, "handler")) { - for (var key in event) { - registerEvent(this$1, event[key], key); - } - } else { - registerEvent(this$1, event); - } - }); - } - }; - UIkit.prototype._unbindEvents = function() { - this._events.forEach(function(unbind) { - return unbind(); - }); - this._events = []; - }; - UIkit.prototype._initObserver = function() { - var this$1 = this; - var ref = this.$options; - var attrs = ref.attrs; - var props = ref.props; - var el = ref.el; - if (this._observer || !props || !attrs) { - return; - } - attrs = isArray(attrs) ? attrs : Object.keys(props).map(function(key) { - return hyphenate(key); - }); - this._observer = new MutationObserver(function() { - var data$$1 = getProps(this$1.$options, this$1.$name); - if (attrs.some(function(key) { - return !isUndefined(data$$1[key]) && data$$1[key] !== this$1.$props[key]; - })) { - this$1.$reset(data$$1); - } - }); - this._observer.observe(el, { - attributes: true, - attributeFilter: attrs.concat([ this.$name, "data-" + this.$name ]) - }); - }; - function getProps(opts, name) { - var data$$1 = {}; - var args = opts.args; - if (args === void 0) args = []; - var props = opts.props; - if (props === void 0) props = {}; - var el = opts.el; - if (!props) { - return data$$1; - } - for (var key in props) { - var prop = hyphenate(key); - if (hasAttr(el, prop)) { - var value = coerce(props[key], attr(el, prop), el); - if (prop === "target" && (!value || startsWith(value, "_"))) { - continue; - } - data$$1[key] = value; - } - } - var options = parseOptions(data(el, name), args); - for (var key$1 in options) { - var prop$1 = camelize(key$1); - if (props[prop$1] !== undefined) { - data$$1[prop$1] = coerce(props[prop$1], options[key$1], el); - } - } - return data$$1; - } - function parseOptions(options, args) { - var obj; - if (args === void 0) args = []; - try { - return !options ? {} : startsWith(options, "{") ? JSON.parse(options) : args.length && !includes(options, ":") ? (obj = {}, - obj[args[0]] = options, obj) : options.split(";").reduce(function(options, option) { - var ref = option.split(/:(.+)/); - var key = ref[0]; - var value = ref[1]; - if (key && value) { - options[key.trim()] = value.trim(); - } - return options; - }, {}); - } catch (e) { - return {}; - } - } - function registerComputed(component, key, cb) { - Object.defineProperty(component, key, { - enumerable: true, - get: function get() { - var _computeds = component._computeds; - var $props = component.$props; - var $el = component.$el; - if (!hasOwn(_computeds, key)) { - _computeds[key] = cb.call(component, $props, $el); - } - return _computeds[key]; - }, - set: function set(value) { - component._computeds[key] = value; - } - }); - } - function registerEvent(component, event, key) { - if (!isPlainObject(event)) { - event = { - name: key, - handler: event - }; - } - var name = event.name; - var el = event.el; - var handler = event.handler; - var capture = event.capture; - var delegate = event.delegate; - var filter = event.filter; - var self = event.self; - el = isFunction(el) ? el.call(component) : el || component.$el; - if (isArray(el)) { - el.forEach(function(el) { - return registerEvent(component, assign({}, event, { - el: el - }), key); - }); - return; - } - if (!el || filter && !filter.call(component)) { - return; - } - handler = detail(isString(handler) ? component[handler] : bind(handler, component)); - if (self) { - handler = selfFilter(handler); - } - component._events.push(on(el, name, !delegate ? null : isString(delegate) ? delegate : delegate.call(component), handler, capture)); - } - function selfFilter(handler) { - return function selfHandler(e) { - if (e.target === e.currentTarget || e.target === e.current) { - return handler.call(null, e); - } - }; - } - function notIn(options, key) { - return options.every(function(arr) { - return !arr || !hasOwn(arr, key); - }); - } - function detail(listener) { - return function(e) { - return isArray(e.detail) ? listener.apply(void 0, [ e ].concat(e.detail)) : listener(e); - }; - } - function coerce(type, value, context) { - if (type === Boolean) { - return toBoolean(value); - } else if (type === Number) { - return toNumber(value); - } else if (type === "query") { - return query(value, context); - } else if (type === "list") { - return toList(value); - } else if (type === "media") { - return toMedia(value); - } - return type ? type(value) : value; - } - function toMedia(value) { - if (isString(value)) { - if (value[0] === "@") { - var name = "media-" + value.substr(1); - value = toFloat(getCssVar(name)); - } else if (isNaN(value)) { - return value; - } - } - return value && !isNaN(value) ? "(min-width: " + value + "px)" : false; - } - } - function instanceAPI(UIkit) { - var DATA = UIkit.data; - UIkit.prototype.$mount = function(el) { - var ref = this.$options; - var name = ref.name; - if (!el[DATA]) { - el[DATA] = {}; - } - if (el[DATA][name]) { - return; - } - el[DATA][name] = this; - this.$el = this.$options.el = this.$options.el || el; - this._initProps(); - this._callHook("init"); - if (within(el, document)) { - this._callConnected(); - } - }; - UIkit.prototype.$emit = function(e) { - this._callUpdate(e); - }; - UIkit.prototype.$reset = function(data) { - this._callDisconnected(); - this._initProps(data); - this._callConnected(); - }; - UIkit.prototype.$destroy = function(removeEl) { - if (removeEl === void 0) removeEl = false; - var ref = this.$options; - var el = ref.el; - var name = ref.name; - if (el) { - this._callDisconnected(); - } - this._callHook("destroy"); - if (!el || !el[DATA]) { - return; - } - delete el[DATA][name]; - if (!Object.keys(el[DATA]).length) { - delete el[DATA]; - } - if (removeEl) { - remove(this.$el); - } - }; - } - var UIkit$2 = function(options) { - this._init(options); - }; - UIkit$2.util = util; - UIkit$2.data = "__uikit__"; - UIkit$2.prefix = "uk-"; - UIkit$2.options = {}; - globalAPI(UIkit$2); - hooksAPI(UIkit$2); - stateAPI(UIkit$2); - instanceAPI(UIkit$2); - componentAPI(UIkit$2); - var Class = { - init: function init() { - addClass(this.$el, this.$name); - } - }; - var Container = { - props: { - container: Boolean - }, - defaults: { - container: true - }, - computed: { - container: function container(ref) { - var container = ref.container; - return container === true && UIkit$2.container || container && $(container); - } - } - }; - var Togglable = { - props: { - cls: Boolean, - animation: "list", - duration: Number, - origin: String, - transition: String, - queued: Boolean - }, - defaults: { - cls: false, - animation: [ false ], - duration: 200, - origin: false, - transition: "linear", - queued: false, - initProps: { - overflow: "", - height: "", - paddingTop: "", - paddingBottom: "", - marginTop: "", - marginBottom: "" - }, - hideProps: { - overflow: "hidden", - height: 0, - paddingTop: 0, - paddingBottom: 0, - marginTop: 0, - marginBottom: 0 - } - }, - computed: { - hasAnimation: function hasAnimation(ref) { - var animation = ref.animation; - return !!animation[0]; - }, - hasTransition: function hasTransition(ref) { - var animation = ref.animation; - return this.hasAnimation && animation[0] === true; - } - }, - methods: { - toggleElement: function toggleElement(targets, show, animate) { - var this$1 = this; - return new Promise(function(resolve) { - targets = toNodes(targets); - var all = function(targets) { - return Promise.all(targets.map(function(el) { - return this$1._toggleElement(el, show, animate); - })); - }; - var toggled = targets.filter(function(el) { - return this$1.isToggled(el); - }); - var untoggled = targets.filter(function(el) { - return !includes(toggled, el); - }); - var p; - if (!this$1.queued || !isUndefined(animate) || !isUndefined(show) || !this$1.hasAnimation || targets.length < 2) { - p = all(untoggled.concat(toggled)); - } else { - var body = document.body; - var scroll = body.scrollTop; - var el = toggled[0]; - var inProgress = Animation.inProgress(el) && hasClass(el, "uk-animation-leave") || Transition.inProgress(el) && el.style.height === "0px"; - p = all(toggled); - if (!inProgress) { - p = p.then(function() { - var p = all(untoggled); - body.scrollTop = scroll; - return p; - }); - } - } - p.then(resolve, noop); - }); - }, - toggleNow: function toggleNow(targets, show) { - var this$1 = this; - return new Promise(function(resolve) { - return Promise.all(toNodes(targets).map(function(el) { - return this$1._toggleElement(el, show, false); - })).then(resolve, noop); - }); - }, - isToggled: function isToggled(el) { - var nodes = toNodes(el || this.$el); - return this.cls ? hasClass(nodes, this.cls.split(" ")[0]) : !hasAttr(nodes, "hidden"); - }, - updateAria: function updateAria(el) { - if (this.cls === false) { - attr(el, "aria-hidden", !this.isToggled(el)); - } - }, - _toggleElement: function _toggleElement(el, show, animate) { - var this$1 = this; - show = isBoolean(show) ? show : Animation.inProgress(el) ? hasClass(el, "uk-animation-leave") : Transition.inProgress(el) ? el.style.height === "0px" : !this.isToggled(el); - if (!trigger(el, "before" + (show ? "show" : "hide"), [ this ])) { - return Promise.reject(); - } - var promise = (animate === false || !this.hasAnimation ? this._toggleImmediate : this.hasTransition ? this._toggleHeight : this._toggleAnimation)(el, show); - trigger(el, show ? "show" : "hide", [ this ]); - return promise.then(function() { - trigger(el, show ? "shown" : "hidden", [ this$1 ]); - trigger(el, "resize"); - }); - }, - _toggle: function _toggle(el, toggled) { - if (!el) { - return; - } - var changed; - if (this.cls) { - changed = includes(this.cls, " ") || Boolean(toggled) !== hasClass(el, this.cls); - changed && toggleClass(el, this.cls, includes(this.cls, " ") ? undefined : toggled); - } else { - changed = Boolean(toggled) === hasAttr(el, "hidden"); - changed && attr(el, "hidden", !toggled ? "" : null); - } - $$("[autofocus]", el).some(function(el) { - return isVisible(el) && (el.focus() || true); - }); - this.updateAria(el); - changed && trigger(el, "resize"); - }, - _toggleImmediate: function _toggleImmediate(el, show) { - this._toggle(el, show); - return Promise.resolve(); - }, - _toggleHeight: function _toggleHeight(el, show) { - var this$1 = this; - var inProgress = Transition.inProgress(el); - var inner = el.hasChildNodes ? toFloat(css(el.firstElementChild, "marginTop")) + toFloat(css(el.lastElementChild, "marginBottom")) : 0; - var currentHeight = isVisible(el) ? height(el) + (inProgress ? 0 : inner) : 0; - Transition.cancel(el); - if (!this.isToggled(el)) { - this._toggle(el, true); - } - height(el, ""); - fastdom.flush(); - var endHeight = height(el) + (inProgress ? 0 : inner); - height(el, currentHeight); - return (show ? Transition.start(el, assign({}, this.initProps, { - overflow: "hidden", - height: endHeight - }), Math.round(this.duration * (1 - currentHeight / endHeight)), this.transition) : Transition.start(el, this.hideProps, Math.round(this.duration * (currentHeight / endHeight)), this.transition).then(function() { - return this$1._toggle(el, false); - })).then(function() { - return css(el, this$1.initProps); - }); - }, - _toggleAnimation: function _toggleAnimation(el, show) { - var this$1 = this; - Animation.cancel(el); - if (show) { - this._toggle(el, true); - return Animation.in(el, this.animation[0], this.duration, this.origin); - } - return Animation.out(el, this.animation[1] || this.animation[0], this.duration, this.origin).then(function() { - return this$1._toggle(el, false); - }); - } - } - }; - var active; - var Modal = { - mixins: [ Class, Container, Togglable ], - props: { - selPanel: String, - selClose: String, - escClose: Boolean, - bgClose: Boolean, - stack: Boolean - }, - defaults: { - cls: "uk-open", - escClose: true, - bgClose: true, - overlay: true, - stack: false - }, - computed: { - panel: function panel(ref, $el) { - var selPanel = ref.selPanel; - return $(selPanel, $el); - }, - transitionElement: function transitionElement() { - return this.panel; - }, - transitionDuration: function transitionDuration() { - return toMs(css(this.transitionElement, "transitionDuration")); - } - }, - events: [ { - name: "click", - delegate: function delegate() { - return this.selClose; - }, - handler: function handler(e) { - e.preventDefault(); - this.hide(); - } - }, { - name: "toggle", - self: true, - handler: function handler(e) { - if (e.defaultPrevented) { - return; - } - e.preventDefault(); - this.toggle(); - } - }, { - name: "beforeshow", - self: true, - handler: function handler(e) { - var prev = active && active !== this && active; - active = this; - if (prev) { - if (this.stack) { - this.prev = prev; - } else { - prev.hide().then(this.show); - e.preventDefault(); - return; - } - } - registerEvents(); - } - }, { - name: "beforehide", - self: true, - handler: function handler() { - active = active && active !== this && active || this.prev; - if (!active) { - deregisterEvents(); - } - } - }, { - name: "show", - self: true, - handler: function handler() { - if (!hasClass(document.documentElement, this.clsPage)) { - this.scrollbarWidth = width(window) - width(document); - css(document.body, "overflowY", this.scrollbarWidth && this.overlay ? "scroll" : ""); - } - addClass(document.documentElement, this.clsPage); - } - }, { - name: "hidden", - self: true, - handler: function handler() { - var this$1 = this; - var found; - var ref = this; - var prev = ref.prev; - while (prev) { - if (prev.clsPage === this$1.clsPage) { - found = true; - break; - } - prev = prev.prev; - } - if (!found) { - removeClass(document.documentElement, this.clsPage); - } - !this.prev && css(document.body, "overflowY", ""); - } - } ], - methods: { - toggle: function toggle() { - return this.isToggled() ? this.hide() : this.show(); - }, - show: function show() { - if (this.isToggled()) { - return; - } - if (this.container && this.$el.parentNode !== this.container) { - append(this.container, this.$el); - this._callConnected(); - } - return this.toggleNow(this.$el, true); - }, - hide: function hide() { - if (this.isToggled()) { - return this.toggleNow(this.$el, false); - } - }, - getActive: function getActive() { - return active; - }, - _toggleImmediate: function _toggleImmediate(el, show) { - var this$1 = this; - return new Promise(function(resolve) { - return requestAnimationFrame(function() { - this$1._toggle(el, show); - if (this$1.transitionDuration) { - once(this$1.transitionElement, "transitionend", resolve, false, function(e) { - return e.target === this$1.transitionElement; - }); - } else { - resolve(); - } - }); - }); - } - } - }; - var events; - function registerEvents() { - if (events) { - return; - } - events = [ on(document, "click", function(ref) { - var target = ref.target; - var defaultPrevented = ref.defaultPrevented; - if (active && active.bgClose && !defaultPrevented && (!active.overlay || within(target, active.$el)) && (!active.panel || !within(target, active.panel))) { - active.hide(); - } - }), on(document, "keydown", function(e) { - if (e.keyCode === 27 && active && active.escClose) { - e.preventDefault(); - active.hide(); - } - }) ]; - } - function deregisterEvents() { - events && events.forEach(function(unbind) { - return unbind(); - }); - events = null; - } - var Position = { - props: { - pos: String, - offset: null, - flip: Boolean, - clsPos: String - }, - defaults: { - pos: "bottom-" + (!isRtl ? "left" : "right"), - flip: true, - offset: false, - clsPos: "" - }, - computed: { - pos: function pos(ref) { - var pos = ref.pos; - return (pos + (!includes(pos, "-") ? "-center" : "")).split("-"); - }, - dir: function dir() { - return this.pos[0]; - }, - align: function align() { - return this.pos[1]; - } - }, - methods: { - positionAt: function positionAt$1(element, target, boundary) { - removeClasses(element, this.clsPos + "-(top|bottom|left|right)(-[a-z]+)?"); - css(element, { - top: "", - left: "" - }); - var node; - var ref = this; - var offset$$1 = ref.offset; - offset$$1 = isNumeric(offset$$1) ? offset$$1 : (node = $(offset$$1)) ? offset(node)[axis === "x" ? "left" : "top"] - offset(target)[axis === "x" ? "right" : "bottom"] : 0; - var axis = this.getAxis(); - var ref$1 = positionAt(element, target, axis === "x" ? flipPosition(this.dir) + " " + this.align : this.align + " " + flipPosition(this.dir), axis === "x" ? this.dir + " " + this.align : this.align + " " + this.dir, axis === "x" ? "" + (this.dir === "left" ? -offset$$1 : offset$$1) : " " + (this.dir === "top" ? -offset$$1 : offset$$1), null, this.flip, boundary).target; - var x = ref$1.x; - var y = ref$1.y; - this.dir = axis === "x" ? x : y; - this.align = axis === "x" ? y : x; - toggleClass(element, this.clsPos + "-" + this.dir + "-" + this.align, this.offset === false); - }, - getAxis: function getAxis() { - return this.dir === "top" || this.dir === "bottom" ? "y" : "x"; - } - } - }; - function mixin(UIkit) { - UIkit.mixin.class = Class; - UIkit.mixin.container = Container; - UIkit.mixin.modal = Modal; - UIkit.mixin.position = Position; - UIkit.mixin.togglable = Togglable; - } - function Accordion(UIkit) { - UIkit.component("accordion", { - mixins: [ Class, Togglable ], - props: { - targets: String, - active: null, - collapsible: Boolean, - multiple: Boolean, - toggle: String, - content: String, - transition: String - }, - defaults: { - targets: "> *", - active: false, - animation: [ true ], - collapsible: true, - multiple: false, - clsOpen: "uk-open", - toggle: "> .uk-accordion-title", - content: "> .uk-accordion-content", - transition: "ease" - }, - computed: { - items: function items(ref, $el) { - var targets = ref.targets; - return $$(targets, $el); - } - }, - events: [ { - name: "click", - delegate: function delegate() { - return this.targets + " " + this.$props.toggle; - }, - handler: function handler(e) { - e.preventDefault(); - this.toggle(index($$(this.targets + " " + this.$props.toggle, this.$el), e.current)); - } - } ], - connected: function connected() { - if (this.active === false) { - return; - } - var active = this.items[Number(this.active)]; - if (active && !hasClass(active, this.clsOpen)) { - this.toggle(active, false); - } - }, - update: function update() { - var this$1 = this; - this.items.forEach(function(el) { - return this$1._toggleImmediate($(this$1.content, el), hasClass(el, this$1.clsOpen)); - }); - var active = !this.collapsible && !hasClass(this.items, this.clsOpen) && this.items[0]; - if (active) { - this.toggle(active, false); - } - }, - methods: { - toggle: function toggle(item, animate) { - var this$1 = this; - var index = getIndex(item, this.items); - var active = filter(this.items, "." + this.clsOpen); - item = this.items[index]; - item && [ item ].concat(!this.multiple && !includes(active, item) && active || []).forEach(function(el) { - var isItem = el === item; - var state = isItem && !hasClass(el, this$1.clsOpen); - if (!state && isItem && !this$1.collapsible && active.length < 2) { - return; - } - toggleClass(el, this$1.clsOpen, state); - var content = el._wrapper ? el._wrapper.firstElementChild : $(this$1.content, el); - if (!el._wrapper) { - el._wrapper = wrapAll(content, "
      "); - attr(el._wrapper, "hidden", state ? "" : null); - } - this$1._toggleImmediate(content, true); - this$1.toggleElement(el._wrapper, state, animate).then(function() { - if (hasClass(el, this$1.clsOpen) === state) { - if (!state) { - this$1._toggleImmediate(content, false); - } - el._wrapper = null; - unwrap(content); - } - }); - }); - } - } - }); - } - function Alert(UIkit) { - UIkit.component("alert", { - attrs: true, - mixins: [ Class, Togglable ], - args: "animation", - props: { - close: String - }, - defaults: { - animation: [ true ], - selClose: ".uk-alert-close", - duration: 150, - hideProps: assign({ - opacity: 0 - }, Togglable.defaults.hideProps) - }, - events: [ { - name: "click", - delegate: function delegate() { - return this.selClose; - }, - handler: function handler(e) { - e.preventDefault(); - this.close(); - } - } ], - methods: { - close: function close() { - var this$1 = this; - this.toggleElement(this.$el).then(function() { - return this$1.$destroy(true); - }); - } - } - }); - } - function Core(UIkit) { - ready(function() { - var scroll = 0; - var started = 0; - on(window, "load resize", function(e) { - return UIkit.update(null, e); - }); - on(window, "scroll", function(e) { - e.dir = scroll <= window.pageYOffset ? "down" : "up"; - e.scrollY = scroll = window.pageYOffset; - UIkit.update(null, e); - }); - on(document, "animationstart", function(ref) { - var target = ref.target; - if ((css(target, "animationName") || "").match(/^uk-.*(left|right)/)) { - started++; - css(document.body, "overflowX", "hidden"); - setTimeout(function() { - if (!--started) { - css(document.body, "overflowX", ""); - } - }, toMs(css(target, "animationDuration")) + 100); - } - }, true); - if (!hasTouch) { - return; - } - var cls = "uk-hover"; - on(document, "tap", function(ref) { - var target = ref.target; - return $$("." + cls).forEach(function(el) { - return !within(target, el) && removeClass(el, cls); - }); - }); - Object.defineProperty(UIkit, "hoverSelector", { - set: function set(selector) { - on(document, "tap", selector, function(ref) { - var current = ref.current; - return addClass(current, cls); - }); - } - }); - UIkit.hoverSelector = ".uk-animation-toggle, .uk-transition-toggle, [uk-hover]"; - }); - } - function Cover(UIkit) { - UIkit.component("cover", { - mixins: [ Class, UIkit.components.video.options ], - props: { - width: Number, - height: Number - }, - defaults: { - automute: true - }, - update: { - write: function write() { - var el = this.$el; - if (!isVisible(el)) { - return; - } - var ref = el.parentNode; - var height = ref.offsetHeight; - var width = ref.offsetWidth; - css(css(el, { - width: "", - height: "" - }), Dimensions.cover({ - width: this.width || el.clientWidth, - height: this.height || el.clientHeight - }, { - width: width + (width % 2 ? 1 : 0), - height: height + (height % 2 ? 1 : 0) - })); - }, - events: [ "load", "resize" ] - }, - events: { - loadedmetadata: function loadedmetadata() { - this.$emit(); - } - } - }); - } - function Drop(UIkit) { - var active; - UIkit.component("drop", { - mixins: [ Position, Togglable ], - args: "pos", - props: { - mode: "list", - toggle: Boolean, - boundary: "query", - boundaryAlign: Boolean, - delayShow: Number, - delayHide: Number, - clsDrop: String - }, - defaults: { - mode: [ "click", "hover" ], - toggle: true, - boundary: window, - boundaryAlign: false, - delayShow: 0, - delayHide: 800, - clsDrop: false, - hoverIdle: 200, - animation: [ "uk-animation-fade" ], - cls: "uk-open" - }, - computed: { - clsDrop: function clsDrop(ref) { - var clsDrop = ref.clsDrop; - return clsDrop || "uk-" + this.$options.name; - }, - clsPos: function clsPos() { - return this.clsDrop; - } - }, - init: function init() { - this.tracker = new MouseTracker(); - addClass(this.$el, this.clsDrop); - }, - connected: function connected() { - var ref = this.$props; - var toggle = ref.toggle; - this.toggle = toggle && UIkit.toggle(isString(toggle) ? query(toggle, this.$el) : this.$el.previousElementSibling, { - target: this.$el, - mode: this.mode - }); - this.updateAria(this.$el); - }, - events: [ { - name: "click", - delegate: function delegate() { - return "." + this.clsDrop + "-close"; - }, - handler: function handler(e) { - e.preventDefault(); - this.hide(false); - } - }, { - name: "click", - delegate: function delegate() { - return 'a[href^="#"]'; - }, - handler: function handler(e) { - if (e.defaultPrevented) { - return; - } - var id = e.target.hash; - if (!id) { - e.preventDefault(); - } - if (!id || !within(id, this.$el)) { - this.hide(false); - } - } - }, { - name: "beforescroll", - handler: function handler() { - this.hide(false); - } - }, { - name: "toggle", - self: true, - handler: function handler(e, toggle) { - e.preventDefault(); - if (this.isToggled()) { - this.hide(false); - } else { - this.show(toggle, false); - } - } - }, { - name: pointerEnter, - filter: function filter() { - return includes(this.mode, "hover"); - }, - handler: function handler(e) { - if (isTouch(e)) { - return; - } - if (active && active !== this && active.toggle && includes(active.toggle.mode, "hover") && !within(e.target, active.toggle.$el) && !pointInRect({ - x: e.pageX, - y: e.pageY - }, offset(active.$el))) { - active.hide(false); - } - e.preventDefault(); - this.show(this.toggle); - } - }, { - name: "toggleshow", - handler: function handler(e, toggle) { - if (toggle && !includes(toggle.target, this.$el)) { - return; - } - e.preventDefault(); - this.show(toggle || this.toggle); - } - }, { - name: "togglehide " + pointerLeave, - handler: function handler(e, toggle) { - if (isTouch(e) || toggle && !includes(toggle.target, this.$el)) { - return; - } - e.preventDefault(); - if (this.toggle && includes(this.toggle.mode, "hover")) { - this.hide(); - } - } - }, { - name: "beforeshow", - self: true, - handler: function handler() { - this.clearTimers(); - Animation.cancel(this.$el); - this.position(); - } - }, { - name: "show", - self: true, - handler: function handler() { - this.tracker.init(); - if (this.toggle) { - addClass(this.toggle.$el, this.cls); - attr(this.toggle.$el, "aria-expanded", "true"); - } - registerEvent(); - } - }, { - name: "beforehide", - self: true, - handler: function handler() { - this.clearTimers(); - } - }, { - name: "hide", - handler: function handler(ref) { - var target = ref.target; - if (this.$el !== target) { - active = active === null && within(target, this.$el) && this.isToggled() ? this : active; - return; - } - active = this.isActive() ? null : active; - if (this.toggle) { - removeClass(this.toggle.$el, this.cls); - attr(this.toggle.$el, "aria-expanded", "false"); - this.toggle.$el.blur(); - $$("a, button", this.toggle.$el).forEach(function(el) { - return el.blur(); - }); - } - this.tracker.cancel(); - } - } ], - update: { - write: function write() { - if (this.isToggled() && !Animation.inProgress(this.$el)) { - this.position(); - } - }, - events: [ "resize" ] - }, - methods: { - show: function show(toggle, delay) { - var this$1 = this; - if (delay === void 0) delay = true; - var show = function() { - return !this$1.isToggled() && this$1.toggleElement(this$1.$el, true); - }; - var tryShow = function() { - this$1.toggle = toggle || this$1.toggle; - this$1.clearTimers(); - if (this$1.isActive()) { - return; - } else if (delay && active && active !== this$1 && active.isDelaying) { - this$1.showTimer = setTimeout(this$1.show, 10); - return; - } else if (this$1.isParentOf(active)) { - if (active.hideTimer) { - active.hide(false); - } else { - return; - } - } else if (active && !this$1.isChildOf(active) && !this$1.isParentOf(active)) { - var prev; - while (active && active !== prev && !this$1.isChildOf(active)) { - prev = active; - active.hide(false); - } - } - if (delay && this$1.delayShow) { - this$1.showTimer = setTimeout(show, this$1.delayShow); - } else { - show(); - } - active = this$1; - }; - if (toggle && this.toggle && toggle.$el !== this.toggle.$el) { - once(this.$el, "hide", tryShow); - this.hide(false); - } else { - tryShow(); - } - }, - hide: function hide(delay) { - var this$1 = this; - if (delay === void 0) delay = true; - var hide = function() { - return this$1.toggleNow(this$1.$el, false); - }; - this.clearTimers(); - this.isDelaying = this.tracker.movesTo(this.$el); - if (delay && this.isDelaying) { - this.hideTimer = setTimeout(this.hide, this.hoverIdle); - } else if (delay && this.delayHide) { - this.hideTimer = setTimeout(hide, this.delayHide); - } else { - hide(); - } - }, - clearTimers: function clearTimers() { - clearTimeout(this.showTimer); - clearTimeout(this.hideTimer); - this.showTimer = null; - this.hideTimer = null; - this.isDelaying = false; - }, - isActive: function isActive() { - return active === this; - }, - isChildOf: function isChildOf(drop) { - return drop && drop !== this && within(this.$el, drop.$el); - }, - isParentOf: function isParentOf(drop) { - return drop && drop !== this && within(drop.$el, this.$el); - }, - position: function position() { - removeClasses(this.$el, this.clsDrop + "-(stack|boundary)"); - css(this.$el, { - top: "", - left: "", - display: "block" - }); - toggleClass(this.$el, this.clsDrop + "-boundary", this.boundaryAlign); - var boundary = offset(this.boundary); - var alignTo = this.boundaryAlign ? boundary : offset(this.toggle.$el); - if (this.align === "justify") { - var prop = this.getAxis() === "y" ? "width" : "height"; - css(this.$el, prop, alignTo[prop]); - } else if (this.$el.offsetWidth > Math.max(boundary.right - alignTo.left, alignTo.right - boundary.left)) { - addClass(this.$el, this.clsDrop + "-stack"); - } - this.positionAt(this.$el, this.boundaryAlign ? this.boundary : this.toggle.$el, this.boundary); - css(this.$el, "display", ""); - } - } - }); - UIkit.drop.getActive = function() { - return active; - }; - var registered; - function registerEvent() { - if (registered) { - return; - } - registered = true; - on(document, "click", function(ref) { - var target = ref.target; - var defaultPrevented = ref.defaultPrevented; - var prev; - if (defaultPrevented) { - return; - } - while (active && active !== prev && !within(target, active.$el) && !(active.toggle && within(target, active.toggle.$el))) { - prev = active; - active.hide(false); - } - }); - } - } - function Dropdown(UIkit) { - UIkit.component("dropdown", UIkit.components.drop.extend({ - name: "dropdown" - })); - } - function FormCustom(UIkit) { - UIkit.component("form-custom", { - mixins: [ Class ], - args: "target", - props: { - target: Boolean - }, - defaults: { - target: false - }, - computed: { - input: function input(_, $el) { - return $(selInput, $el); - }, - state: function state() { - return this.input.nextElementSibling; - }, - target: function target(ref, $el) { - var target = ref.target; - return target && (target === true && this.input.parentNode === $el && this.input.nextElementSibling || query(target, $el)); - } - }, - update: function update() { - var ref = this; - var target = ref.target; - var input = ref.input; - if (!target) { - return; - } - var option; - target[isInput(target) ? "value" : "textContent"] = input.files && input.files[0] ? input.files[0].name : matches(input, "select") && (option = $$("option", input).filter(function(el) { - return el.selected; - })[0]) ? option.textContent : input.value; - }, - events: [ { - name: "focusin focusout mouseenter mouseleave", - delegate: selInput, - handler: function handler(ref) { - var type = ref.type; - var current = ref.current; - if (current === this.input) { - toggleClass(this.state, "uk-" + (includes(type, "focus") ? "focus" : "hover"), includes([ "focusin", "mouseenter" ], type)); - } - } - }, { - name: "change", - handler: function handler() { - this.$emit(); - } - } ] - }); - } - function Gif(UIkit) { - UIkit.component("gif", { - update: { - read: function read(data) { - var inview = isInView(this.$el); - if (!inview || data.isInView === inview) { - return false; - } - data.isInView = inview; - }, - write: function write() { - this.$el.src = this.$el.src; - }, - events: [ "scroll", "load", "resize" ] - } - }); - } - function Grid(UIkit) { - UIkit.component("grid", UIkit.components.margin.extend({ - mixins: [ Class ], - name: "grid", - defaults: { - margin: "uk-grid-margin", - clsStack: "uk-grid-stack" - }, - update: { - write: function write(ref) { - var stacks = ref.stacks; - toggleClass(this.$el, this.clsStack, stacks); - }, - events: [ "load", "resize" ] - } - })); - } - function HeightMatch(UIkit) { - UIkit.component("height-match", { - args: "target", - props: { - target: String, - row: Boolean - }, - defaults: { - target: "> *", - row: true - }, - computed: { - elements: function elements(ref, $el) { - var target = ref.target; - return $$(target, $el); - } - }, - update: { - read: function read() { - var this$1 = this; - var lastOffset = false; - css(this.elements, "minHeight", ""); - return { - rows: !this.row ? [ this.match(this.elements) ] : this.elements.reduce(function(rows, el) { - if (lastOffset !== el.offsetTop) { - rows.push([ el ]); - } else { - rows[rows.length - 1].push(el); - } - lastOffset = el.offsetTop; - return rows; - }, []).map(function(elements) { - return this$1.match(elements); - }) - }; - }, - write: function write(ref) { - var rows = ref.rows; - rows.forEach(function(ref) { - var height = ref.height; - var elements = ref.elements; - return css(elements, "minHeight", height); - }); - }, - events: [ "load", "resize" ] - }, - methods: { - match: function match(elements) { - if (elements.length < 2) { - return {}; - } - var heights = []; - var max = 0; - elements.forEach(function(el) { - var style, hidden; - if (!isVisible(el)) { - style = attr(el, "style"); - hidden = attr(el, "hidden"); - attr(el, { - style: (style || "") + ";display:block !important;", - hidden: null - }); - } - max = Math.max(max, el.offsetHeight); - heights.push(el.offsetHeight); - if (!isUndefined(style)) { - attr(el, { - style: style, - hidden: hidden - }); - } - }); - elements = elements.filter(function(el, i) { - return heights[i] < max; - }); - return { - height: max, - elements: elements - }; - } - } - }); - } - function HeightViewport(UIkit) { - UIkit.component("height-viewport", { - props: { - expand: Boolean, - offsetTop: Boolean, - offsetBottom: Boolean, - minHeight: Number - }, - defaults: { - expand: false, - offsetTop: false, - offsetBottom: false, - minHeight: 0 - }, - update: { - write: function write() { - css(this.$el, "boxSizing", "border-box"); - var viewport = height(window); - var minHeight, offsetTop = 0; - if (this.expand) { - css(this.$el, { - height: "", - minHeight: "" - }); - var diff = viewport - offsetHeight(document.documentElement); - if (diff > 0) { - minHeight = offsetHeight(this.$el) + diff; - } - } else { - var ref = offset(this.$el); - var top = ref.top; - if (top < viewport / 2 && this.offsetTop) { - offsetTop += top; - } - if (this.offsetBottom === true) { - offsetTop += offsetHeight(this.$el.nextElementSibling); - } else if (isNumeric(this.offsetBottom)) { - offsetTop += viewport / 100 * this.offsetBottom; - } else if (this.offsetBottom && endsWith(this.offsetBottom, "px")) { - offsetTop += toFloat(this.offsetBottom); - } else if (isString(this.offsetBottom)) { - offsetTop += offsetHeight(query(this.offsetBottom, this.$el)); - } - minHeight = offsetTop ? "calc(100vh - " + offsetTop + "px)" : "100vh"; - } - if (!minHeight) { - return; - } - css(this.$el, { - height: "", - minHeight: minHeight - }); - var elHeight = this.$el.offsetHeight; - if (this.minHeight && this.minHeight > elHeight) { - css(this.$el, "minHeight", this.minHeight); - } - if (viewport - offsetTop >= elHeight) { - css(this.$el, "height", minHeight); - } - }, - events: [ "load", "resize" ] - } - }); - function offsetHeight(el) { - return el && el.offsetHeight || 0; - } - } - var closeIcon = ''; - var closeLarge = ''; - var marker = ''; - var navbarToggleIcon = ''; - var overlayIcon = ''; - var paginationNext = ''; - var paginationPrevious = ''; - var searchIcon = ''; - var searchLarge = ''; - var searchNavbar = ''; - var slidenavNext = ''; - var slidenavNextLarge = ''; - var slidenavPrevious = ''; - var slidenavPreviousLarge = ''; - var spinner = ''; - var totop = ''; - function Icon(UIkit) { - var parsed = {}; - var icons = { - spinner: spinner, - totop: totop, - marker: marker, - "close-icon": closeIcon, - "close-large": closeLarge, - "navbar-toggle-icon": navbarToggleIcon, - "overlay-icon": overlayIcon, - "pagination-next": paginationNext, - "pagination-previous": paginationPrevious, - "search-icon": searchIcon, - "search-large": searchLarge, - "search-navbar": searchNavbar, - "slidenav-next": slidenavNext, - "slidenav-next-large": slidenavNextLarge, - "slidenav-previous": slidenavPrevious, - "slidenav-previous-large": slidenavPreviousLarge - }; - UIkit.component("icon", UIkit.components.svg.extend({ - attrs: [ "icon", "ratio" ], - mixins: [ Class ], - name: "icon", - args: "icon", - props: [ "icon" ], - defaults: { - exclude: [ "id", "style", "class", "src", "icon" ] - }, - init: function init() { - addClass(this.$el, "uk-icon"); - if (isRtl) { - this.icon = swap(swap(this.icon, "left", "right"), "previous", "next"); - } - }, - methods: { - getSvg: function getSvg() { - var icon = getIcon(this.icon); - if (!icon) { - return Promise.reject("Icon not found."); - } - return Promise.resolve(icon); - } - } - })); - [ "marker", "navbar-toggle-icon", "overlay-icon", "pagination-previous", "pagination-next", "totop" ].forEach(function(name) { - return registerComponent(name); - }); - [ "slidenav-previous", "slidenav-next" ].forEach(function(name) { - return registerComponent(name, { - init: function init() { - addClass(this.$el, "uk-slidenav"); - if (hasClass(this.$el, "uk-slidenav-large")) { - this.icon += "-large"; - } - } - }); - }); - registerComponent("search-icon", { - init: function init() { - if (hasClass(this.$el, "uk-search-icon") && parents(this.$el, ".uk-search-large").length) { - this.icon = "search-large"; - } else if (parents(this.$el, ".uk-search-navbar").length) { - this.icon = "search-navbar"; - } - } - }); - registerComponent("close", { - init: function init() { - this.icon = "close-" + (hasClass(this.$el, "uk-close-large") ? "large" : "icon"); - } - }); - registerComponent("spinner", { - connected: function connected() { - var this$1 = this; - this.svg.then(function(svg) { - return this$1.ratio !== 1 && css($("circle", svg), "stroke-width", 1 / this$1.ratio); - }, noop); - } - }); - UIkit.icon.add = function(added) { - Object.keys(added).forEach(function(name) { - icons[name] = added[name]; - delete parsed[name]; - }); - if (UIkit._initialized) { - apply(document.body, function(el) { - var icon = UIkit.getComponent(el, "icon"); - if (icon) { - icon.$reset(); - } - }); - } - }; - function registerComponent(name, mixin$$1) { - UIkit.component(name, UIkit.components.icon.extend({ - name: name, - mixins: mixin$$1 ? [ mixin$$1 ] : [], - defaults: { - icon: name - } - })); - } - function getIcon(icon) { - if (!icons[icon]) { - return null; - } - if (!parsed[icon]) { - parsed[icon] = $(icons[icon].trim()); - } - return parsed[icon]; - } - } - function Leader(UIkit) { - UIkit.component("leader", { - mixins: [ Class ], - props: { - fill: String, - media: "media" - }, - defaults: { - fill: "", - media: false, - clsWrapper: "uk-leader-fill", - clsHide: "uk-leader-hide", - attrFill: "data-fill" - }, - computed: { - fill: function fill(ref) { - var fill = ref.fill; - return fill || getCssVar("leader-fill"); - } - }, - connected: function connected() { - var assign; - assign = wrapInner(this.$el, ''), this.wrapper = assign[0]; - }, - disconnected: function disconnected() { - unwrap(this.wrapper.childNodes); - }, - update: [ { - read: function read(ref) { - var changed = ref.changed; - var width = ref.width; - var prev = width; - width = Math.floor(this.$el.offsetWidth / 2); - return { - width: width, - changed: changed || prev !== width, - hide: this.media && !window.matchMedia(this.media).matches - }; - }, - write: function write(data) { - toggleClass(this.wrapper, this.clsHide, data.hide); - if (data.changed) { - data.changed = false; - attr(this.wrapper, this.attrFill, new Array(data.width).join(this.fill)); - } - }, - events: [ "load", "resize" ] - } ] - }); - } - function Margin(UIkit) { - UIkit.component("margin", { - props: { - margin: String, - firstColumn: Boolean - }, - defaults: { - margin: "uk-margin-small-top", - firstColumn: "uk-first-column" - }, - update: { - read: function read(data) { - var items = this.$el.children; - if (!items.length || !isVisible(this.$el)) { - return data.rows = false; - } - data.stacks = true; - var rows = [ [] ]; - for (var i = 0; i < items.length; i++) { - var el = items[i]; - var dim = el.getBoundingClientRect(); - if (!dim.height) { - continue; - } - for (var j = rows.length - 1; j >= 0; j--) { - var row = rows[j]; - if (!row[0]) { - row.push(el); - break; - } - var leftDim = row[0].getBoundingClientRect(); - if (dim.top >= Math.floor(leftDim.bottom)) { - rows.push([ el ]); - break; - } - if (Math.floor(dim.bottom) > leftDim.top) { - data.stacks = false; - if (dim.left < leftDim.left && !isRtl) { - row.unshift(el); - break; - } - row.push(el); - break; - } - if (j === 0) { - rows.unshift([ el ]); - break; - } - } - } - data.rows = rows; - }, - write: function write(ref) { - var this$1 = this; - var rows = ref.rows; - rows.forEach(function(row, i) { - return row.forEach(function(el, j) { - toggleClass(el, this$1.margin, i !== 0); - toggleClass(el, this$1.firstColumn, j === 0); - }); - }); - }, - events: [ "load", "resize" ] - } - }); - } - function Modal$1(UIkit) { - UIkit.component("modal", { - mixins: [ Modal ], - defaults: { - clsPage: "uk-modal-page", - selPanel: ".uk-modal-dialog", - selClose: ".uk-modal-close, .uk-modal-close-default, .uk-modal-close-outside, .uk-modal-close-full" - }, - events: [ { - name: "show", - self: true, - handler: function handler() { - if (hasClass(this.panel, "uk-margin-auto-vertical")) { - addClass(this.$el, "uk-flex"); - } else { - css(this.$el, "display", "block"); - } - height(this.$el); - } - }, { - name: "hidden", - self: true, - handler: function handler() { - css(this.$el, "display", ""); - removeClass(this.$el, "uk-flex"); - } - } ] - }); - UIkit.component("overflow-auto", { - mixins: [ Class ], - computed: { - modal: function modal(_, $el) { - return closest($el, ".uk-modal"); - }, - panel: function panel(_, $el) { - return closest($el, ".uk-modal-dialog"); - } - }, - connected: function connected() { - css(this.$el, "minHeight", 150); - }, - update: { - write: function write() { - if (!this.panel || !this.modal) { - return; - } - var current = css(this.$el, "maxHeight"); - css(css(this.$el, "maxHeight", 150), "maxHeight", Math.max(150, 150 + height(this.modal) - this.panel.offsetHeight)); - if (current !== css(this.$el, "maxHeight")) { - trigger(this.$el, "resize"); - } - }, - events: [ "load", "resize" ] - } - }); - UIkit.modal.dialog = function(content, options) { - var dialog = UIkit.modal('
      ' + content + "
      ", options); - dialog.show(); - on(dialog.$el, "hidden", function(ref) { - var target = ref.target; - var currentTarget = ref.currentTarget; - if (target === currentTarget) { - dialog.$destroy(true); - } - }); - return dialog; - }; - UIkit.modal.alert = function(message, options) { - options = assign({ - bgClose: false, - escClose: false, - labels: UIkit.modal.labels - }, options); - return new Promise(function(resolve) { - return on(UIkit.modal.dialog('
      ' + (isString(message) ? message : html(message)) + '
      ", options).$el, "hide", resolve); - }); - }; - UIkit.modal.confirm = function(message, options) { - options = assign({ - bgClose: false, - escClose: true, - labels: UIkit.modal.labels - }, options); - return new Promise(function(resolve, reject) { - var confirm = UIkit.modal.dialog('
      ' + (isString(message) ? message : html(message)) + '
      ", options); - var resolved = false; - on(confirm.$el, "submit", "form", function(e) { - e.preventDefault(); - resolve(); - resolved = true; - confirm.hide(); - }); - on(confirm.$el, "hide", function() { - if (!resolved) { - reject(); - } - }); - }); - }; - UIkit.modal.prompt = function(message, value, options) { - options = assign({ - bgClose: false, - escClose: true, - labels: UIkit.modal.labels - }, options); - return new Promise(function(resolve) { - var prompt = UIkit.modal.dialog('
      ", options), input = $("input", prompt.$el); - input.value = value; - var resolved = false; - on(prompt.$el, "submit", "form", function(e) { - e.preventDefault(); - resolve(input.value); - resolved = true; - prompt.hide(); - }); - on(prompt.$el, "hide", function() { - if (!resolved) { - resolve(null); - } - }); - }); - }; - UIkit.modal.labels = { - ok: "Ok", - cancel: "Cancel" - }; - } - function Nav(UIkit) { - UIkit.component("nav", UIkit.components.accordion.extend({ - name: "nav", - defaults: { - targets: "> .uk-parent", - toggle: "> a", - content: "> ul" - } - })); - } - function Navbar(UIkit) { - UIkit.component("navbar", { - mixins: [ Class ], - props: { - dropdown: String, - mode: "list", - align: String, - offset: Number, - boundary: Boolean, - boundaryAlign: Boolean, - clsDrop: String, - delayShow: Number, - delayHide: Number, - dropbar: Boolean, - dropbarMode: String, - dropbarAnchor: "query", - duration: Number - }, - defaults: { - dropdown: ".uk-navbar-nav > li", - align: !isRtl ? "left" : "right", - clsDrop: "uk-navbar-dropdown", - mode: undefined, - offset: undefined, - delayShow: undefined, - delayHide: undefined, - boundaryAlign: undefined, - flip: "x", - boundary: true, - dropbar: false, - dropbarMode: "slide", - dropbarAnchor: false, - duration: 200 - }, - computed: { - boundary: function boundary(ref, $el) { - var boundary = ref.boundary; - var boundaryAlign = ref.boundaryAlign; - return boundary === true || boundaryAlign ? $el : boundary; - }, - pos: function pos(ref) { - var align = ref.align; - return "bottom-" + align; - } - }, - beforeConnect: function beforeConnect() { - var ref = this.$props; - var dropbar = ref.dropbar; - this.dropbar = dropbar && (isString(dropbar) && query(dropbar, this.$el) || $("
      ")); - if (this.dropbar) { - addClass(this.dropbar, "uk-navbar-dropbar"); - if (this.dropbarMode === "slide") { - addClass(this.dropbar, "uk-navbar-dropbar-slide"); - } - } - }, - disconnected: function disconnected() { - this.dropbar && remove(this.dropbar); - }, - update: function update() { - UIkit.drop($$(this.dropdown + " ." + this.clsDrop, this.$el).filter(function(el) { - return !UIkit.getComponent(el, "drop") && !UIkit.getComponent(el, "dropdown"); - }), assign({}, this.$props, { - boundary: this.boundary, - pos: this.pos, - offset: this.dropbar || this.offset - })); - }, - events: [ { - name: "mouseover", - delegate: function delegate() { - return this.dropdown; - }, - handler: function handler(ref) { - var current = ref.current; - var active = this.getActive(); - if (active && active.toggle && !within(active.toggle.$el, current) && !active.tracker.movesTo(active.$el)) { - active.hide(false); - } - } - }, { - name: "mouseleave", - el: function el() { - return this.dropbar; - }, - handler: function handler() { - var active = this.getActive(); - if (active && !matches(this.dropbar, ":hover")) { - active.hide(); - } - } - }, { - name: "beforeshow", - capture: true, - filter: function filter() { - return this.dropbar; - }, - handler: function handler() { - if (!this.dropbar.parentNode) { - after(this.dropbarAnchor || this.$el, this.dropbar); - } - } - }, { - name: "show", - capture: true, - filter: function filter() { - return this.dropbar; - }, - handler: function handler(_, drop) { - var $el = drop.$el; - var dir = drop.dir; - this.clsDrop && addClass($el, this.clsDrop + "-dropbar"); - if (dir === "bottom") { - this.transitionTo($el.offsetHeight + toFloat(css($el, "marginTop")) + toFloat(css($el, "marginBottom")), $el); - } - } - }, { - name: "beforehide", - filter: function filter() { - return this.dropbar; - }, - handler: function handler(e, ref) { - var $el = ref.$el; - var active = this.getActive(); - if (matches(this.dropbar, ":hover") && active && active.$el === $el) { - e.preventDefault(); - } - } - }, { - name: "hide", - filter: function filter() { - return this.dropbar; - }, - handler: function handler(_, ref) { - var $el = ref.$el; - var active = this.getActive(); - if (!active || active && active.$el === $el) { - this.transitionTo(0); - } - } - } ], - methods: { - getActive: function getActive() { - var active = UIkit.drop.getActive(); - return active && includes(active.mode, "hover") && within(active.toggle.$el, this.$el) && active; - }, - transitionTo: function transitionTo(newHeight, el) { - var ref = this; - var dropbar = ref.dropbar; - var oldHeight = isVisible(dropbar) ? height(dropbar) : 0; - el = oldHeight < newHeight && el; - css(el, { - height: oldHeight, - overflow: "hidden" - }); - height(dropbar, oldHeight); - Transition.cancel([ el, dropbar ]); - return Transition.start([ el, dropbar ], { - height: newHeight - }, this.duration).catch(noop).then(function() { - return css(el, { - height: "", - overflow: "" - }); - }); - } - } - }); - } - var scroll; - function Offcanvas(UIkit) { - UIkit.component("offcanvas", { - mixins: [ Modal ], - args: "mode", - props: { - content: String, - mode: String, - flip: Boolean, - overlay: Boolean - }, - defaults: { - content: ".uk-offcanvas-content", - mode: "slide", - flip: false, - overlay: false, - clsPage: "uk-offcanvas-page", - clsContainer: "uk-offcanvas-container", - selPanel: ".uk-offcanvas-bar", - clsFlip: "uk-offcanvas-flip", - clsContent: "uk-offcanvas-content", - clsContentAnimation: "uk-offcanvas-content-animation", - clsSidebarAnimation: "uk-offcanvas-bar-animation", - clsMode: "uk-offcanvas", - clsOverlay: "uk-offcanvas-overlay", - selClose: ".uk-offcanvas-close" - }, - computed: { - content: function content(ref) { - var content = ref.content; - return $(content) || document.body; - }, - clsFlip: function clsFlip(ref) { - var flip = ref.flip; - var clsFlip = ref.clsFlip; - return flip ? clsFlip : ""; - }, - clsOverlay: function clsOverlay(ref) { - var overlay = ref.overlay; - var clsOverlay = ref.clsOverlay; - return overlay ? clsOverlay : ""; - }, - clsMode: function clsMode(ref) { - var mode = ref.mode; - var clsMode = ref.clsMode; - return clsMode + "-" + mode; - }, - clsSidebarAnimation: function clsSidebarAnimation(ref) { - var mode = ref.mode; - var clsSidebarAnimation = ref.clsSidebarAnimation; - return mode === "none" || mode === "reveal" ? "" : clsSidebarAnimation; - }, - clsContentAnimation: function clsContentAnimation(ref) { - var mode = ref.mode; - var clsContentAnimation = ref.clsContentAnimation; - return mode !== "push" && mode !== "reveal" ? "" : clsContentAnimation; - }, - transitionElement: function transitionElement(ref) { - var mode = ref.mode; - return mode === "reveal" ? this.panel.parentNode : this.panel; - } - }, - update: { - write: function write() { - if (this.getActive() === this) { - if (this.overlay || this.clsContentAnimation) { - width(this.content, width(window) - this.scrollbarWidth); - } - if (this.overlay) { - height(this.content, height(window)); - if (scroll) { - this.content.scrollTop = scroll.y; - } - } - } - }, - events: [ "resize" ] - }, - events: [ { - name: "click", - delegate: function delegate() { - return 'a[href^="#"]'; - }, - handler: function handler(ref) { - var current = ref.current; - if (current.hash && $(current.hash, this.content)) { - scroll = null; - this.hide(); - } - } - }, { - name: "beforescroll", - filter: function filter() { - return this.overlay; - }, - handler: function handler(e, scroll, target) { - if (scroll && target && this.isToggled() && $(target, this.content)) { - once(this.$el, "hidden", function() { - return scroll.scrollTo(target); - }); - e.preventDefault(); - } - } - }, { - name: "show", - self: true, - handler: function handler() { - scroll = scroll || { - x: window.pageXOffset, - y: window.pageYOffset - }; - if (this.mode === "reveal" && !hasClass(this.panel, this.clsMode)) { - wrapAll(this.panel, "
      "); - addClass(this.panel.parentNode, this.clsMode); - } - css(document.documentElement, "overflowY", (!this.clsContentAnimation || this.flip) && this.scrollbarWidth && this.overlay ? "scroll" : ""); - addClass(document.body, this.clsContainer, this.clsFlip, this.clsOverlay); - height(document.body); - addClass(this.content, this.clsContentAnimation); - addClass(this.panel, this.clsSidebarAnimation, this.mode !== "reveal" ? this.clsMode : ""); - addClass(this.$el, this.clsOverlay); - css(this.$el, "display", "block"); - height(this.$el); - } - }, { - name: "hide", - self: true, - handler: function handler() { - removeClass(this.content, this.clsContentAnimation); - var active = this.getActive(); - if (this.mode === "none" || active && active !== this && active !== this.prev) { - trigger(this.panel, "transitionend"); - } - } - }, { - name: "hidden", - self: true, - handler: function handler() { - if (this.mode === "reveal") { - unwrap(this.panel); - } - if (!this.overlay) { - scroll = { - x: window.pageXOffset, - y: window.pageYOffset - }; - } else if (!scroll) { - var ref = this.content; - var x = ref.scrollLeft; - var y = ref.scrollTop; - scroll = { - x: x, - y: y - }; - } - removeClass(this.panel, this.clsSidebarAnimation, this.clsMode); - removeClass(this.$el, this.clsOverlay); - css(this.$el, "display", ""); - removeClass(document.body, this.clsContainer, this.clsFlip, this.clsOverlay); - document.body.scrollTop = scroll.y; - css(document.documentElement, "overflowY", ""); - width(this.content, ""); - height(this.content, ""); - window.scrollTo(scroll.x, scroll.y); - scroll = null; - } - }, { - name: "swipeLeft swipeRight", - handler: function handler(e) { - if (this.isToggled() && isTouch(e) && (e.type === "swipeLeft" && !this.flip || e.type === "swipeRight" && this.flip)) { - this.hide(); - } - } - } ] - }); - } - function Responsive(UIkit) { - UIkit.component("responsive", { - props: [ "width", "height" ], - init: function init() { - addClass(this.$el, "uk-responsive-width"); - }, - update: { - read: function read() { - return isVisible(this.$el) && this.width && this.height ? { - width: width(this.$el.parentNode), - height: this.height - } : false; - }, - write: function write(dim) { - height(this.$el, Dimensions.contain({ - height: this.height, - width: this.width - }, dim).height); - }, - events: [ "load", "resize" ] - } - }); - } - function Scroll(UIkit) { - UIkit.component("scroll", { - props: { - duration: Number, - offset: Number - }, - defaults: { - duration: 1e3, - offset: 0 - }, - methods: { - scrollTo: function scrollTo(el) { - var this$1 = this; - el = el && $(el) || document.body; - var docHeight = height(document); - var winHeight = height(window); - var target = offset(el).top - this.offset; - if (target + winHeight > docHeight) { - target = docHeight - winHeight; - } - if (!trigger(this.$el, "beforescroll", [ this, el ])) { - return; - } - var start = Date.now(); - var startY = window.pageYOffset; - var step = function() { - var currentY = startY + (target - startY) * ease(clamp((Date.now() - start) / this$1.duration)); - window.scrollTo(window.pageXOffset, currentY); - if (currentY !== target) { - requestAnimationFrame(step); - } else { - trigger(this$1.$el, "scrolled", [ this$1, el ]); - } - }; - step(); - } - }, - events: { - click: function click(e) { - if (e.defaultPrevented) { - return; - } - e.preventDefault(); - this.scrollTo(escape(this.$el.hash).substr(1)); - } - } - }); - function ease(k) { - return .5 * (1 - Math.cos(Math.PI * k)); - } - } - function Scrollspy(UIkit) { - UIkit.component("scrollspy", { - args: "cls", - props: { - cls: "list", - target: String, - hidden: Boolean, - offsetTop: Number, - offsetLeft: Number, - repeat: Boolean, - delay: Number - }, - defaults: { - cls: [], - target: false, - hidden: true, - offsetTop: 0, - offsetLeft: 0, - repeat: false, - delay: 0, - inViewClass: "uk-scrollspy-inview" - }, - computed: { - elements: function elements(ref, $el) { - var target = ref.target; - return target ? $$(target, $el) : [ $el ]; - } - }, - update: [ { - write: function write() { - if (this.hidden) { - css(filter(this.elements, ":not(." + this.inViewClass + ")"), "visibility", "hidden"); - } - } - }, { - read: function read(els) { - var this$1 = this; - if (!UIkit._initialized) { - if (document.readyState === "complete") { - requestAnimationFrame(function() { - return this$1.$emit(); - }); - } - return false; - } - this.elements.forEach(function(el, i) { - var elData = els[i]; - if (!elData || elData.el !== el) { - var cls = data(el, "uk-scrollspy-class"); - elData = { - el: el, - toggles: cls && cls.split(",") || this$1.cls - }; - } - elData.show = isInView(el, this$1.offsetTop, this$1.offsetLeft); - els[i] = elData; - }); - }, - write: function write(els) { - var this$1 = this; - var index = this.elements.length === 1 ? 1 : 0; - this.elements.forEach(function(el, i) { - var elData = els[i]; - var cls = elData.toggles[i] || elData.toggles[0]; - if (elData.show && !elData.inview && !elData.timer) { - var show = function() { - css(el, "visibility", ""); - addClass(el, this$1.inViewClass); - toggleClass(el, cls); - trigger(el, "inview"); - UIkit.update(el); - elData.inview = true; - delete elData.timer; - }; - if (this$1.delay && index) { - elData.timer = setTimeout(show, this$1.delay * index); - } else { - show(); - } - index++; - } else if (!elData.show && elData.inview && this$1.repeat) { - if (elData.timer) { - clearTimeout(elData.timer); - delete elData.timer; - } - css(el, "visibility", this$1.hidden ? "hidden" : ""); - removeClass(el, this$1.inViewClass); - toggleClass(el, cls); - trigger(el, "outview"); - UIkit.update(el); - elData.inview = false; - } - }); - }, - events: [ "scroll", "load", "resize" ] - } ] - }); - } - function ScrollspyNav(UIkit) { - UIkit.component("scrollspy-nav", { - props: { - cls: String, - closest: String, - scroll: Boolean, - overflow: Boolean, - offset: Number - }, - defaults: { - cls: "uk-active", - closest: false, - scroll: false, - overflow: true, - offset: 0 - }, - computed: { - links: function links(_, $el) { - return $$('a[href^="#"]', $el).filter(function(el) { - return el.hash; - }); - }, - elements: function elements() { - return this.closest ? closest(this.links, this.closest) : this.links; - }, - targets: function targets() { - return $$(this.links.map(function(el) { - return el.hash; - }).join(",")); - } - }, - update: [ { - read: function read() { - if (this.scroll) { - UIkit.scroll(this.links, { - offset: this.offset || 0 - }); - } - } - }, { - read: function read(data) { - var this$1 = this; - var scroll = window.pageYOffset + this.offset + 1; - var max = height(document) - height(window) + this.offset; - data.active = false; - this.targets.every(function(el, i) { - var ref = offset(el); - var top = ref.top; - var last = i + 1 === this$1.targets.length; - if (!this$1.overflow && (i === 0 && top > scroll || last && top + el.offsetTop < scroll)) { - return false; - } - if (!last && offset(this$1.targets[i + 1]).top <= scroll) { - return true; - } - if (scroll >= max) { - for (var j = this$1.targets.length - 1; j > i; j--) { - if (isInView(this$1.targets[j])) { - el = this$1.targets[j]; - break; - } - } - } - return !(data.active = $(filter(this$1.links, '[href="#' + el.id + '"]'))); - }); - }, - write: function write(ref) { - var active = ref.active; - this.links.forEach(function(el) { - return el.blur(); - }); - removeClass(this.elements, this.cls); - if (active) { - trigger(this.$el, "active", [ active, addClass(this.closest ? closest(active, this.closest) : active, this.cls) ]); - } - }, - events: [ "scroll", "load", "resize" ] - } ] - }); - } - function Sticky(UIkit) { - UIkit.component("sticky", { - mixins: [ Class ], - attrs: true, - props: { - top: null, - bottom: Boolean, - offset: Number, - animation: String, - clsActive: String, - clsInactive: String, - clsFixed: String, - clsBelow: String, - selTarget: String, - widthElement: "query", - showOnUp: Boolean, - media: "media", - target: Number - }, - defaults: { - top: 0, - bottom: false, - offset: 0, - animation: "", - clsActive: "uk-active", - clsInactive: "", - clsFixed: "uk-sticky-fixed", - clsBelow: "uk-sticky-below", - selTarget: "", - widthElement: false, - showOnUp: false, - media: false, - target: false - }, - computed: { - selTarget: function selTarget(ref, $el) { - var selTarget = ref.selTarget; - return selTarget && $(selTarget, $el) || $el; - } - }, - connected: function connected() { - this.placeholder = $('
      '); - this.widthElement = this.$props.widthElement || this.placeholder; - if (!this.isActive) { - this.hide(); - } - }, - disconnected: function disconnected() { - if (this.isActive) { - this.isActive = false; - this.hide(); - removeClass(this.selTarget, this.clsInactive); - } - remove(this.placeholder); - this.placeholder = null; - this.widthElement = null; - }, - ready: function ready() { - var this$1 = this; - if (!(this.target && location.hash && window.pageYOffset > 0)) { - return; - } - var target = $(location.hash); - if (target) { - fastdom.read(function() { - var ref = offset(target); - var top = ref.top; - var elTop = offset(this$1.$el).top; - var elHeight = this$1.$el.offsetHeight; - if (elTop + elHeight >= top && elTop <= top + target.offsetHeight) { - window.scrollTo(0, top - elHeight - this$1.target - this$1.offset); - } - }); - } - }, - events: [ { - name: "active", - self: true, - handler: function handler() { - replaceClass(this.selTarget, this.clsInactive, this.clsActive); - } - }, { - name: "inactive", - self: true, - handler: function handler() { - replaceClass(this.selTarget, this.clsActive, this.clsInactive); - } - } ], - update: [ { - write: function write() { - var ref = this; - var placeholder = ref.placeholder; - var outerHeight = (this.isActive ? placeholder : this.$el).offsetHeight; - css(placeholder, assign({ - height: css(this.$el, "position") !== "absolute" ? outerHeight : "" - }, css(this.$el, [ "marginTop", "marginBottom", "marginLeft", "marginRight" ]))); - if (!within(placeholder, document)) { - after(this.$el, placeholder); - attr(placeholder, "hidden", ""); - } - attr(this.widthElement, "hidden", null); - this.width = this.widthElement.offsetWidth; - attr(this.widthElement, "hidden", this.isActive ? null : ""); - this.topOffset = offset(this.isActive ? placeholder : this.$el).top; - this.bottomOffset = this.topOffset + outerHeight; - var bottom = parseProp("bottom", this); - this.top = Math.max(toFloat(parseProp("top", this)), this.topOffset) - this.offset; - this.bottom = bottom && bottom - outerHeight; - this.inactive = this.media && !window.matchMedia(this.media).matches; - if (this.isActive) { - this.update(); - } - }, - events: [ "load", "resize" ] - }, { - read: function read(_, ref) { - var scrollY = ref.scrollY; - if (scrollY === void 0) scrollY = window.pageYOffset; - return { - scroll: this.scroll = scrollY, - visible: isVisible(this.$el) - }; - }, - write: function write(ref, ref$1) { - var this$1 = this; - var visible = ref.visible; - var scroll = ref.scroll; - if (ref$1 === void 0) ref$1 = {}; - var dir = ref$1.dir; - if (scroll < 0 || !visible || this.disabled || this.showOnUp && !dir) { - return; - } - if (this.inactive || scroll < this.top || this.showOnUp && (scroll <= this.top || dir === "down" || dir === "up" && !this.isActive && scroll <= this.bottomOffset)) { - if (!this.isActive) { - return; - } - this.isActive = false; - if (this.animation && scroll > this.topOffset) { - Animation.cancel(this.$el); - Animation.out(this.$el, this.animation).then(function() { - return this$1.hide(); - }, noop); - } else { - this.hide(); - } - } else if (this.isActive) { - this.update(); - } else if (this.animation) { - Animation.cancel(this.$el); - this.show(); - Animation.in(this.$el, this.animation).catch(noop); - } else { - this.show(); - } - }, - events: [ "scroll" ] - } ], - methods: { - show: function show() { - this.isActive = true; - this.update(); - attr(this.placeholder, "hidden", null); - }, - hide: function hide() { - if (!this.isActive || hasClass(this.selTarget, this.clsActive)) { - trigger(this.$el, "inactive"); - } - removeClass(this.$el, this.clsFixed, this.clsBelow); - css(this.$el, { - position: "", - top: "", - width: "" - }); - attr(this.placeholder, "hidden", ""); - }, - update: function update() { - var active = this.top !== 0 || this.scroll > this.top; - var top = Math.max(0, this.offset); - if (this.bottom && this.scroll > this.bottom - this.offset) { - top = this.bottom - this.scroll; - } - css(this.$el, { - position: "fixed", - top: top + "px", - width: this.width - }); - if (hasClass(this.selTarget, this.clsActive)) { - if (!active) { - trigger(this.$el, "inactive"); - } - } else if (active) { - trigger(this.$el, "active"); - } - toggleClass(this.$el, this.clsBelow, this.scroll > this.bottomOffset); - addClass(this.$el, this.clsFixed); - } - } - }); - function parseProp(prop, ref) { - var $props = ref.$props; - var $el = ref.$el; - var propOffset = ref[prop + "Offset"]; - var value = $props[prop]; - if (!value) { - return; - } - if (isNumeric(value)) { - return propOffset + toFloat(value); - } else if (isString(value) && value.match(/^-?\d+vh$/)) { - return height(window) * toFloat(value) / 100; - } else { - var el = value === true ? $el.parentNode : query(value, $el); - if (el) { - return offset(el).top + el.offsetHeight; - } - } - } - } - var svgs = {}; - function Svg(UIkit) { - UIkit.component("svg", { - attrs: true, - props: { - id: String, - icon: String, - src: String, - style: String, - width: Number, - height: Number, - ratio: Number, - class: String - }, - defaults: { - ratio: 1, - id: false, - exclude: [ "src" ], - class: "" - }, - init: function init() { - this.class += " uk-svg"; - }, - connected: function connected() { - var this$1 = this; - if (!this.icon && includes(this.src, "#")) { - var parts = this.src.split("#"); - if (parts.length > 1) { - var assign; - assign = parts, this.src = assign[0], this.icon = assign[1]; - } - } - this.svg = this.getSvg().then(function(svg) { - var el; - if (isString(svg)) { - if (this$1.icon && includes(svg, "/g; - var symbols = {}; - function parseSymbols(svg, icon) { - if (!symbols[svg]) { - symbols[svg] = {}; - var match; - while (match = symbolRe.exec(svg)) { - symbols[svg][match[3]] = '"; - } - } - return symbols[svg][icon]; - } - } - function Switcher(UIkit) { - UIkit.component("switcher", { - mixins: [ Togglable ], - args: "connect", - props: { - connect: String, - toggle: String, - active: Number, - swiping: Boolean - }, - defaults: { - connect: "~.uk-switcher", - toggle: "> *", - active: 0, - swiping: true, - cls: "uk-active", - clsContainer: "uk-switcher", - attrItem: "uk-switcher-item", - queued: true - }, - computed: { - connects: function connects(ref, $el) { - var connect = ref.connect; - return queryAll(connect, $el); - }, - toggles: function toggles(ref, $el) { - var toggle = ref.toggle; - return $$(toggle, $el); - } - }, - events: [ { - name: "click", - delegate: function delegate() { - return this.toggle + ":not(.uk-disabled)"; - }, - handler: function handler(e) { - e.preventDefault(); - this.show(e.current); - } - }, { - name: "click", - el: function el() { - return this.connects; - }, - delegate: function delegate() { - return "[" + this.attrItem + "],[data-" + this.attrItem + "]"; - }, - handler: function handler(e) { - e.preventDefault(); - this.show(data(e.current, this.attrItem)); - } - }, { - name: "swipeRight swipeLeft", - filter: function filter() { - return this.swiping; - }, - el: function el() { - return this.connects; - }, - handler: function handler(e) { - if (!isTouch(e)) { - return; - } - e.preventDefault(); - if (!window.getSelection().toString()) { - this.show(e.type === "swipeLeft" ? "next" : "previous"); - } - } - } ], - update: function update() { - var this$1 = this; - this.connects.forEach(function(list) { - return this$1.updateAria(list.children); - }); - this.show(filter(this.toggles, "." + this.cls)[0] || this.toggles[this.active] || this.toggles[0]); - }, - methods: { - show: function show(item) { - var this$1 = this; - var ref = this.toggles; - var length = ref.length; - var prev = !!this.connects.length && index(filter(this.connects[0].children, "." + this.cls)[0]); - var hasPrev = prev >= 0; - var dir = item === "previous" ? -1 : 1; - var toggle, next = getIndex(item, this.toggles, prev); - for (var i = 0; i < length; i++, next = (next + dir + length) % length) { - if (!matches(this$1.toggles[next], ".uk-disabled, [disabled]")) { - toggle = this$1.toggles[next]; - break; - } - } - if (!toggle || prev >= 0 && hasClass(toggle, this.cls) || prev === next) { - return; - } - removeClass(this.toggles, this.cls); - attr(this.toggles, "aria-expanded", false); - addClass(toggle, this.cls); - attr(toggle, "aria-expanded", true); - this.connects.forEach(function(list) { - if (!hasPrev) { - this$1.toggleNow(list.children[next]); - } else { - this$1.toggleElement([ list.children[prev], list.children[next] ]); - } - }); - } - } - }); - } - function Tab(UIkit) { - UIkit.component("tab", UIkit.components.switcher.extend({ - mixins: [ Class ], - name: "tab", - props: { - media: "media" - }, - defaults: { - media: 960, - attrItem: "uk-tab-item" - }, - init: function init() { - var cls = hasClass(this.$el, "uk-tab-left") ? "uk-tab-left" : hasClass(this.$el, "uk-tab-right") ? "uk-tab-right" : false; - if (cls) { - UIkit.toggle(this.$el, { - cls: cls, - mode: "media", - media: this.media - }); - } - } - })); - } - function Toggle(UIkit) { - UIkit.component("toggle", { - mixins: [ UIkit.mixin.togglable ], - args: "target", - props: { - href: String, - target: null, - mode: "list", - media: "media" - }, - defaults: { - href: false, - target: false, - mode: "click", - queued: true, - media: false - }, - computed: { - target: function target(ref, $el) { - var href = ref.href; - var target = ref.target; - target = queryAll(target || href, $el); - return target.length && target || [ $el ]; - } - }, - events: [ { - name: pointerEnter + " " + pointerLeave, - filter: function filter() { - return includes(this.mode, "hover"); - }, - handler: function handler(e) { - if (!isTouch(e)) { - this.toggle("toggle" + (e.type === pointerEnter ? "show" : "hide")); - } - } - }, { - name: "click", - filter: function filter() { - return includes(this.mode, "click") || hasTouch; - }, - handler: function handler(e) { - if (!isTouch(e) && !includes(this.mode, "click")) { - return; - } - var link; - if (closest(e.target, 'a[href="#"], button') || (link = closest(e.target, "a[href]")) && (this.cls || !isVisible(this.target) || link.hash && matches(this.target, link.hash))) { - once(document, "click", function(e) { - return e.preventDefault(); - }); - } - this.toggle(); - } - } ], - update: { - write: function write() { - if (!includes(this.mode, "media") || !this.media) { - return; - } - var toggled = this.isToggled(this.target); - if (window.matchMedia(this.media).matches ? !toggled : toggled) { - this.toggle(); - } - }, - events: [ "load", "resize" ] - }, - methods: { - toggle: function toggle(type) { - if (trigger(this.target, type || "toggle", [ this ])) { - this.toggleElement(this.target); - } - } - } - }); - } - function Video(UIkit) { - UIkit.component("video", { - args: "autoplay", - props: { - automute: Boolean, - autoplay: Boolean - }, - defaults: { - automute: false, - autoplay: true - }, - computed: { - inView: function inView(ref) { - var autoplay = ref.autoplay; - return autoplay === "inview"; - } - }, - connected: function connected() { - if (this.inView && !hasAttr(this.$el, "preload")) { - this.$el.preload = "none"; - } - }, - ready: function ready() { - this.player = new Player(this.$el); - if (this.automute) { - this.player.mute(); - } - }, - update: [ { - read: function read(_, ref) { - var type = ref.type; - return !this.player || (type === "scroll" || type === "resize") && !this.inView ? false : { - visible: isVisible(this.$el) && css(this.$el, "visibility") !== "hidden", - inView: this.inView && isInView(this.$el) - }; - }, - write: function write(ref) { - var visible = ref.visible; - var inView = ref.inView; - if (!visible || this.inView && !inView) { - this.player.pause(); - } else if (this.autoplay === true || this.inView && inView) { - this.player.play(); - } - }, - events: [ "load", "resize", "scroll" ] - } ] - }); - } - function core(UIkit) { - UIkit.use(Toggle); - UIkit.use(Accordion); - UIkit.use(Alert); - UIkit.use(Video); - UIkit.use(Cover); - UIkit.use(Drop); - UIkit.use(Dropdown); - UIkit.use(FormCustom); - UIkit.use(HeightMatch); - UIkit.use(HeightViewport); - UIkit.use(Margin); - UIkit.use(Gif); - UIkit.use(Grid); - UIkit.use(Leader); - UIkit.use(Modal$1); - UIkit.use(Nav); - UIkit.use(Navbar); - UIkit.use(Offcanvas); - UIkit.use(Responsive); - UIkit.use(Scroll); - UIkit.use(Scrollspy); - UIkit.use(ScrollspyNav); - UIkit.use(Sticky); - UIkit.use(Svg); - UIkit.use(Icon); - UIkit.use(Switcher); - UIkit.use(Tab); - UIkit.use(Core); - } - UIkit$2.version = "3.0.0-beta.42"; - mixin(UIkit$2); - core(UIkit$2); - function plugin(UIkit) { - if (plugin.installed) { - return; - } - var ref = UIkit.util; - var $ = ref.$; - var empty = ref.empty; - var html = ref.html; - UIkit.component("countdown", { - mixins: [ UIkit.mixin.class ], - attrs: true, - props: { - date: String, - clsWrapper: String - }, - defaults: { - date: "", - clsWrapper: ".uk-countdown-%unit%" - }, - computed: { - date: function date(ref) { - var date = ref.date; - return Date.parse(date); - }, - days: function days(ref, $el) { - var clsWrapper = ref.clsWrapper; - return $(clsWrapper.replace("%unit%", "days"), $el); - }, - hours: function hours(ref, $el) { - var clsWrapper = ref.clsWrapper; - return $(clsWrapper.replace("%unit%", "hours"), $el); - }, - minutes: function minutes(ref, $el) { - var clsWrapper = ref.clsWrapper; - return $(clsWrapper.replace("%unit%", "minutes"), $el); - }, - seconds: function seconds(ref, $el) { - var clsWrapper = ref.clsWrapper; - return $(clsWrapper.replace("%unit%", "seconds"), $el); - }, - units: function units() { - var this$1 = this; - return [ "days", "hours", "minutes", "seconds" ].filter(function(unit) { - return this$1[unit]; - }); - } - }, - connected: function connected() { - this.start(); - }, - disconnected: function disconnected() { - var this$1 = this; - this.stop(); - this.units.forEach(function(unit) { - return empty(this$1[unit]); - }); - }, - events: [ { - name: "visibilitychange", - el: document, - handler: function handler() { - if (document.hidden) { - this.stop(); - } else { - this.start(); - } - } - } ], - update: { - write: function write() { - var this$1 = this; - var timespan = getTimeSpan(this.date); - if (timespan.total <= 0) { - this.stop(); - timespan.days = timespan.hours = timespan.minutes = timespan.seconds = 0; - } - this.units.forEach(function(unit) { - var digits = String(Math.floor(timespan[unit])); - digits = digits.length < 2 ? "0" + digits : digits; - var el = this$1[unit]; - if (el.textContent !== digits) { - digits = digits.split(""); - if (digits.length !== el.children.length) { - html(el, digits.map(function() { - return ""; - }).join("")); - } - digits.forEach(function(digit, i) { - return el.children[i].textContent = digit; - }); - } - }); - } - }, - methods: { - start: function start() { - var this$1 = this; - this.stop(); - if (this.date && this.units.length) { - this.$emit(); - this.timer = setInterval(function() { - return this$1.$emit(); - }, 1e3); - } - }, - stop: function stop() { - if (this.timer) { - clearInterval(this.timer); - this.timer = null; - } - } - } - }); - function getTimeSpan(date) { - var total = date - Date.now(); - return { - total: total, - seconds: total / 1e3 % 60, - minutes: total / 1e3 / 60 % 60, - hours: total / 1e3 / 60 / 60 % 24, - days: total / 1e3 / 60 / 60 / 24 - }; - } - } - function plugin$1(UIkit) { - if (plugin$1.installed) { - return; - } - var ref = UIkit.util; - var addClass = ref.addClass; - var css = ref.css; - var scrolledOver = ref.scrolledOver; - var sortBy = ref.sortBy; - var toFloat = ref.toFloat; - UIkit.component("grid-parallax", UIkit.components.grid.extend({ - props: { - target: String, - translate: Number - }, - defaults: { - target: false, - translate: 150 - }, - computed: { - translate: function translate(ref) { - var translate = ref.translate; - return Math.abs(translate); - } - }, - init: function init() { - addClass(this.$el, "uk-grid"); - }, - disconnected: function disconnected() { - this.reset(); - css(this.$el, "marginBottom", ""); - }, - update: [ { - read: function read(ref) { - var rows = ref.rows; - return { - columns: rows && rows[0] && rows[0].length || 0, - rows: rows && rows.map(function(elements) { - return sortBy(elements, "offsetLeft"); - }) - }; - }, - write: function write(ref) { - var columns = ref.columns; - css(this.$el, "marginBottom", columns > 1 ? this.translate + toFloat(css(css(this.$el, "marginBottom", ""), "marginBottom")) : ""); - }, - events: [ "load", "resize" ] - }, { - read: function read() { - return { - scrolled: scrolledOver(this.$el) * this.translate - }; - }, - write: function write(ref) { - var rows = ref.rows; - var columns = ref.columns; - var scrolled = ref.scrolled; - if (!rows || columns === 1 || !scrolled) { - return this.reset(); - } - rows.forEach(function(row) { - return row.forEach(function(el, i) { - return css(el, "transform", "translateY(" + (i % 2 ? scrolled : scrolled / 8) + "px)"); - }); - }); - }, - events: [ "scroll", "load", "resize" ] - } ], - methods: { - reset: function reset() { - css(this.$el.children, "transform", ""); - } - } - })); - UIkit.components.gridParallax.options.update.unshift({ - read: function read() { - this.reset(); - }, - events: [ "load", "resize" ] - }); - } - function AnimationsPlugin(UIkit) { - var ref = UIkit.util; - var css = ref.css; - var Animations = { - slide: { - show: function show(dir) { - return [ { - transform: translate(dir * -100) - }, { - transform: translate() - } ]; - }, - percent: function percent(current) { - return Animations.translated(current); - }, - translate: function translate$1(percent, dir) { - return [ { - transform: translate(dir * -100 * percent) - }, { - transform: translate(dir * 100 * (1 - percent)) - } ]; - } - }, - translated: function translated(el) { - return Math.abs(css(el, "transform").split(",")[4] / el.offsetWidth) || 0; - } - }; - return Animations; - } - function translate(value, unit) { - if (value === void 0) value = 0; - if (unit === void 0) unit = "%"; - return "translateX(" + value + (value ? unit : "") + ")"; - } - function scale3d(value) { - return "scale3d(" + value + ", " + value + ", 1)"; - } - function TransitionerPlugin(UIkit) { - var ref = UIkit.util; - var createEvent = ref.createEvent; - var clamp = ref.clamp; - var css = ref.css; - var Deferred = ref.Deferred; - var noop = ref.noop; - var Promise = ref.Promise; - var Transition = ref.Transition; - var trigger = ref.trigger; - function Transitioner(prev, next, dir, ref) { - var animation = ref.animation; - var easing = ref.easing; - var percent = animation.percent; - var translate = animation.translate; - var show = animation.show; - if (show === void 0) show = noop; - var props = show(dir); - var deferred = new Deferred(); - return { - dir: dir, - show: function show(duration, percent, linear) { - var this$1 = this; - if (percent === void 0) percent = 0; - var timing = linear ? "linear" : easing; - duration -= Math.round(duration * clamp(percent, -1, 1)); - this.translate(percent); - triggerUpdate(next, "itemin", { - percent: percent, - duration: duration, - timing: timing, - dir: dir - }); - triggerUpdate(prev, "itemout", { - percent: 1 - percent, - duration: duration, - timing: timing, - dir: dir - }); - Promise.all([ Transition.start(next, props[1], duration, timing), Transition.start(prev, props[0], duration, timing) ]).then(function() { - this$1.reset(); - deferred.resolve(); - }, noop); - return deferred.promise; - }, - stop: function stop() { - return Transition.stop([ next, prev ]); - }, - cancel: function cancel() { - Transition.cancel([ next, prev ]); - }, - reset: function reset() { - for (var prop in props[0]) { - css([ next, prev ], prop, ""); - } - }, - forward: function forward(duration, percent) { - if (percent === void 0) percent = this.percent(); - Transition.cancel([ next, prev ]); - return this.show(duration, percent, true); - }, - translate: function translate$1(percent) { - this.reset(); - var props = translate(percent, dir); - css(next, props[1]); - css(prev, props[0]); - triggerUpdate(next, "itemtranslatein", { - percent: percent, - dir: dir - }); - triggerUpdate(prev, "itemtranslateout", { - percent: 1 - percent, - dir: dir - }); - }, - percent: function percent$1() { - return percent(prev || next, next, dir); - }, - getDistance: function getDistance() { - return prev.offsetWidth; - } - }; - } - function triggerUpdate(el, type, data) { - trigger(el, createEvent(type, false, false, data)); - } - return Transitioner; - } - function AutoplayMixin(UIkit) { - var ref = UIkit.util; - var pointerDown = ref.pointerDown; - return { - props: { - autoplay: Boolean, - autoplayInterval: Number, - pauseOnHover: Boolean - }, - defaults: { - autoplay: false, - autoplayInterval: 7e3, - pauseOnHover: true - }, - connected: function connected() { - this.startAutoplay(); - }, - disconnected: function disconnected() { - this.stopAutoplay(); - }, - events: [ { - name: "visibilitychange", - el: document, - handler: function handler() { - if (document.hidden) { - this.stopAutoplay(); - } else { - this.startAutoplay(); - } - } - }, { - name: pointerDown, - handler: "stopAutoplay" - }, { - name: "mouseenter", - filter: function filter() { - return this.autoplay; - }, - handler: function handler() { - this.isHovering = true; - } - }, { - name: "mouseleave", - filter: function filter() { - return this.autoplay; - }, - handler: function handler() { - this.isHovering = false; - } - } ], - methods: { - startAutoplay: function startAutoplay() { - var this$1 = this; - this.stopAutoplay(); - if (this.autoplay) { - this.interval = setInterval(function() { - return !(this$1.isHovering && this$1.pauseOnHover) && !this$1.stack.length && this$1.show("next"); - }, this.autoplayInterval); - } - }, - stopAutoplay: function stopAutoplay() { - if (this.interval) { - clearInterval(this.interval); - } - } - } - }; - } - function DragMixin(UIkit) { - var ref = UIkit.util; - var getPos = ref.getPos; - var includes = ref.includes; - var isRtl = ref.isRtl; - var isTouch = ref.isTouch; - var off = ref.off; - var on = ref.on; - var pointerDown = ref.pointerDown; - var pointerMove = ref.pointerMove; - var pointerUp = ref.pointerUp; - var preventClick = ref.preventClick; - var trigger = ref.trigger; - return { - defaults: { - threshold: 10, - preventCatch: false - }, - init: function init() { - var this$1 = this; - [ "start", "move", "end" ].forEach(function(key) { - var fn = this$1[key]; - this$1[key] = function(e) { - var pos = getPos(e).x * (isRtl ? -1 : 1); - this$1.prevPos = pos !== this$1.pos ? this$1.pos : this$1.prevPos; - this$1.pos = pos; - fn(e); - }; - }); - }, - events: [ { - name: pointerDown, - delegate: function delegate() { - return this.slidesSelector; - }, - handler: function handler(e) { - if (!isTouch(e) && hasTextNodesOnly(e.target) || e.button > 0 || this.length < 2 || this.preventCatch) { - return; - } - this.start(e); - } - }, { - name: "dragstart", - handler: function handler(e) { - e.preventDefault(); - } - } ], - methods: { - start: function start() { - this.drag = this.pos; - if (this._transitioner) { - this.percent = this._transitioner.percent(); - this.drag += this._transitioner.getDistance() * this.percent * this.dir; - this._transitioner.translate(this.percent); - this._transitioner.cancel(); - this.dragging = true; - this.stack = []; - } else { - this.prevIndex = this.index; - } - this.unbindMove = on(document, pointerMove, this.move, { - capture: true, - passive: false - }); - on(window, "scroll", this.unbindMove); - on(document, pointerUp, this.end, true); - }, - move: function move(e) { - var this$1 = this; - var distance = this.pos - this.drag; - if (distance === 0 || this.prevPos === this.pos || !this.dragging && Math.abs(distance) < this.threshold) { - return; - } - e.cancelable && e.preventDefault(); - this.dragging = true; - this.dir = distance < 0 ? 1 : -1; - var ref = this; - var slides = ref.slides; - var ref$1 = this; - var prevIndex = ref$1.prevIndex; - var dis = Math.abs(distance); - var nextIndex = this.getIndex(prevIndex + this.dir, prevIndex); - var width = this._getDistance(prevIndex, nextIndex) || slides[prevIndex].offsetWidth; - while (nextIndex !== prevIndex && dis > width) { - this$1.drag -= width * this$1.dir; - prevIndex = nextIndex; - dis -= width; - nextIndex = this$1.getIndex(prevIndex + this$1.dir, prevIndex); - width = this$1._getDistance(prevIndex, nextIndex) || slides[prevIndex].offsetWidth; - } - this.percent = dis / width; - var prev = slides[prevIndex]; - var next = slides[nextIndex]; - var changed = this.index !== nextIndex; - var edge = prevIndex === nextIndex; - var itemShown; - [ this.index, this.prevIndex ].filter(function(i) { - return !includes([ nextIndex, prevIndex ], i); - }).forEach(function(i) { - trigger(slides[i], "itemhidden", [ this$1 ]); - if (edge) { - itemShown = true; - this$1.prevIndex = prevIndex; - } - }); - if (this.index === prevIndex && this.prevIndex !== prevIndex || itemShown) { - trigger(slides[this.index], "itemshown", [ this ]); - } - if (changed) { - this.prevIndex = prevIndex; - this.index = nextIndex; - !edge && trigger(prev, "beforeitemhide", [ this ]); - trigger(next, "beforeitemshow", [ this ]); - } - this._transitioner = this._translate(Math.abs(this.percent), prev, !edge && next); - if (changed) { - !edge && trigger(prev, "itemhide", [ this ]); - trigger(next, "itemshow", [ this ]); - } - }, - end: function end() { - off(window, "scroll", this.unbindMove); - this.unbindMove(); - off(document, pointerUp, this.end, true); - if (this.dragging) { - this.dragging = null; - if (this.index === this.prevIndex) { - this.percent = 1 - this.percent; - this.dir *= -1; - this._show(false, this.index, true); - this._transitioner = null; - } else { - var dirChange = (isRtl ? this.dir * (isRtl ? 1 : -1) : this.dir) < 0 === this.prevPos > this.pos; - this.index = dirChange ? this.index : this.prevIndex; - if (dirChange) { - this.percent = 1 - this.percent; - } - this.show(this.dir > 0 && !dirChange || this.dir < 0 && dirChange ? "next" : "previous", true); - } - preventClick(); - } - this.drag = this.percent = null; - } - } - }; - function hasTextNodesOnly(el) { - return !el.children.length && el.childNodes.length; - } - } - function NavMixin(UIkit) { - var ref = UIkit.util; - var $ = ref.$; - var $$ = ref.$$; - var data = ref.data; - var html = ref.html; - var toggleClass = ref.toggleClass; - var toNumber = ref.toNumber; - return { - defaults: { - selNav: false - }, - computed: { - nav: function nav(ref, $el) { - var selNav = ref.selNav; - return $(selNav, $el); - }, - navItemSelector: function navItemSelector(ref) { - var attrItem = ref.attrItem; - return "[" + attrItem + "],[data-" + attrItem + "]"; - }, - navItems: function navItems(_, $el) { - return $$(this.navItemSelector, $el); - } - }, - update: [ { - write: function write() { - var this$1 = this; - if (this.nav && this.length !== this.nav.children.length) { - html(this.nav, this.slides.map(function(_, i) { - return "
    • '; - }).join("")); - } - toggleClass($$(this.navItemSelector, this.$el).concat(this.nav), "uk-hidden", !this.maxIndex); - this.updateNav(); - }, - events: [ "load", "resize" ] - } ], - events: [ { - name: "click", - delegate: function delegate() { - return this.navItemSelector; - }, - handler: function handler(e) { - e.preventDefault(); - e.current.blur(); - this.show(data(e.current, this.attrItem)); - } - }, { - name: "itemshow", - handler: "updateNav" - } ], - methods: { - updateNav: function updateNav() { - var this$1 = this; - var i = this.getValidIndex(); - this.navItems.forEach(function(el) { - var cmd = data(el, this$1.attrItem); - toggleClass(el, this$1.clsActive, toNumber(cmd) === i); - toggleClass(el, "uk-invisible", this$1.finite && (cmd === "previous" && i === 0 || cmd === "next" && i >= this$1.maxIndex)); - }); - } - } - }; - } - function plugin$5(UIkit) { - if (plugin$5.installed) { - return; - } - var ref = UIkit.util; - var $ = ref.$; - var assign = ref.assign; - var clamp = ref.clamp; - var fastdom = ref.fastdom; - var getIndex = ref.getIndex; - var hasClass = ref.hasClass; - var isNumber = ref.isNumber; - var isRtl = ref.isRtl; - var Promise = ref.Promise; - var toNodes = ref.toNodes; - var trigger = ref.trigger; - UIkit.mixin.slider = { - attrs: true, - mixins: [ AutoplayMixin(UIkit), DragMixin(UIkit), NavMixin(UIkit) ], - props: { - clsActivated: Boolean, - easing: String, - index: Number, - finite: Boolean, - velocity: Number - }, - defaults: { - easing: "ease", - finite: false, - velocity: 1, - index: 0, - stack: [], - percent: 0, - clsActive: "uk-active", - clsActivated: false, - Transitioner: false, - transitionOptions: {} - }, - computed: { - duration: function duration(ref, $el) { - var velocity = ref.velocity; - return speedUp($el.offsetWidth / velocity); - }, - length: function length() { - return this.slides.length; - }, - list: function list(ref, $el) { - var selList = ref.selList; - return $(selList, $el); - }, - maxIndex: function maxIndex() { - return this.length - 1; - }, - slidesSelector: function slidesSelector(ref) { - var selList = ref.selList; - return selList + " > *"; - }, - slides: function slides() { - return toNodes(this.list.children); - } - }, - methods: { - show: function show(index, force) { - var this$1 = this; - if (force === void 0) force = false; - if (this.dragging || !this.length) { - return; - } - var ref = this; - var stack = ref.stack; - var queueIndex = force ? 0 : stack.length; - var reset = function() { - stack.splice(queueIndex, 1); - if (stack.length) { - this$1.show(stack.shift(), true); - } - }; - stack[force ? "unshift" : "push"](index); - if (!force && stack.length > 1) { - if (stack.length === 2) { - this._transitioner.forward(Math.min(this.duration, 200)); - } - return; - } - var prevIndex = this.index; - var prev = hasClass(this.slides, this.clsActive) && this.slides[prevIndex]; - var nextIndex = this.getIndex(index, this.index); - var next = this.slides[nextIndex]; - if (prev === next) { - reset(); - return; - } - this.dir = getDirection(index, prevIndex); - this.prevIndex = prevIndex; - this.index = nextIndex; - prev && trigger(prev, "beforeitemhide", [ this ]); - if (!trigger(next, "beforeitemshow", [ this, prev ])) { - this.index = this.prevIndex; - reset(); - return; - } - var promise = this._show(prev, next, force).then(function() { - prev && trigger(prev, "itemhidden", [ this$1 ]); - trigger(next, "itemshown", [ this$1 ]); - return new Promise(function(resolve) { - fastdom.write(function() { - stack.shift(); - if (stack.length) { - this$1.show(stack.shift(), true); - } else { - this$1._transitioner = null; - } - resolve(); - }); - }); - }); - prev && trigger(prev, "itemhide", [ this ]); - trigger(next, "itemshow", [ this ]); - return promise; - }, - getIndex: function getIndex$1(index, prev) { - if (index === void 0) index = this.index; - if (prev === void 0) prev = this.index; - return clamp(getIndex(index, this.slides, prev, this.finite), 0, this.maxIndex); - }, - getValidIndex: function getValidIndex(index, prevIndex) { - if (index === void 0) index = this.index; - if (prevIndex === void 0) prevIndex = this.prevIndex; - return this.getIndex(index, prevIndex); - }, - _show: function _show(prev, next, force) { - this._transitioner = this._getTransitioner(prev, next, this.dir, assign({ - easing: force ? next.offsetWidth < 600 ? "cubic-bezier(0.25, 0.46, 0.45, 0.94)" : "cubic-bezier(0.165, 0.84, 0.44, 1)" : this.easing - }, this.transitionOptions)); - if (!force && !prev) { - this._transitioner.translate(1); - return Promise.resolve(); - } - var ref = this.stack; - var length = ref.length; - return this._transitioner[length > 1 ? "forward" : "show"](length > 1 ? Math.min(this.duration, 75 + 75 / (length - 1)) : this.duration, this.percent); - }, - _getDistance: function _getDistance(prev, next) { - return new this._getTransitioner(prev, prev !== next && next).getDistance(); - }, - _translate: function _translate(percent, prev, next) { - if (prev === void 0) prev = this.prevIndex; - if (next === void 0) next = this.index; - var transitioner = this._getTransitioner(prev !== next ? prev : false, next); - transitioner.translate(percent); - return transitioner; - }, - _getTransitioner: function _getTransitioner(prev, next, dir, options) { - if (prev === void 0) prev = this.prevIndex; - if (next === void 0) next = this.index; - if (dir === void 0) dir = this.dir || 1; - if (options === void 0) options = this.transitionOptions; - return new this.Transitioner(isNumber(prev) ? this.slides[prev] : prev, isNumber(next) ? this.slides[next] : next, dir * (isRtl ? -1 : 1), options); - } - } - }; - function getDirection(index, prevIndex) { - return index === "next" ? 1 : index === "previous" ? -1 : index < prevIndex ? -1 : 1; - } - } - function speedUp(x) { - return .5 * x + 300; - } - function plugin$4(UIkit) { - if (plugin$4.installed) { - return; - } - UIkit.use(plugin$5); - var mixin = UIkit.mixin; - var UIkit_util = UIkit.util; - var addClass = UIkit_util.addClass; - var assign = UIkit_util.assign; - var fastdom = UIkit_util.fastdom; - var isNumber = UIkit_util.isNumber; - var removeClass = UIkit_util.removeClass; - var Animations = AnimationsPlugin(UIkit); - var Transitioner = TransitionerPlugin(UIkit); - UIkit.mixin.slideshow = { - mixins: [ mixin.slider ], - props: { - animation: String - }, - defaults: { - animation: "slide", - clsActivated: "uk-transition-active", - Animations: Animations, - Transitioner: Transitioner - }, - computed: { - animation: function animation(ref) { - var animation = ref.animation; - var Animations = ref.Animations; - return assign(animation in Animations ? Animations[animation] : Animations.slide, { - name: animation - }); - }, - transitionOptions: function transitionOptions() { - return { - animation: this.animation - }; - } - }, - events: { - "itemshow itemhide itemshown itemhidden": function itemshowitemhideitemshownitemhidden(ref) { - var target = ref.target; - UIkit.update(target); - }, - itemshow: function itemshow() { - isNumber(this.prevIndex) && fastdom.flush(); - }, - beforeitemshow: function beforeitemshow(ref) { - var target = ref.target; - addClass(target, this.clsActive); - }, - itemshown: function itemshown(ref) { - var target = ref.target; - addClass(target, this.clsActivated); - }, - itemhidden: function itemhidden(ref) { - var target = ref.target; - removeClass(target, this.clsActive, this.clsActivated); - } - } - }; - } - function AnimationsPlugin$1(UIkit) { - var mixin = UIkit.mixin; - var UIkit_util = UIkit.util; - var assign = UIkit_util.assign; - var css = UIkit_util.css; - return assign({}, mixin.slideshow.defaults.Animations, { - fade: { - show: function show() { - return [ { - opacity: 0 - }, { - opacity: 1 - } ]; - }, - percent: function percent(current) { - return 1 - css(current, "opacity"); - }, - translate: function translate$$1(percent) { - return [ { - opacity: 1 - percent - }, { - opacity: percent - } ]; - } - }, - scale: { - show: function show() { - return [ { - opacity: 0, - transform: scale3d(1 - .2) - }, { - opacity: 1, - transform: scale3d(1) - } ]; - }, - percent: function percent(current) { - return 1 - css(current, "opacity"); - }, - translate: function translate$$1(percent) { - return [ { - opacity: 1 - percent, - transform: scale3d(1 - .2 * percent) - }, { - opacity: percent, - transform: scale3d(1 - .2 + .2 * percent) - } ]; - } - } - }); - } - function plugin$3(UIkit) { - if (plugin$3.installed) { - return; - } - UIkit.use(plugin$4); - var mixin = UIkit.mixin; - var util = UIkit.util; - var $ = util.$; - var addClass = util.addClass; - var ajax = util.ajax; - var append = util.append; - var assign = util.assign; - var attr = util.attr; - var css = util.css; - var getImage = util.getImage; - var html = util.html; - var index = util.index; - var on = util.on; - var pointerDown = util.pointerDown; - var pointerMove = util.pointerMove; - var removeClass = util.removeClass; - var Transition = util.Transition; - var trigger = util.trigger; - var Animations = AnimationsPlugin$1(UIkit); - UIkit.component("lightbox-panel", { - mixins: [ mixin.container, mixin.modal, mixin.togglable, mixin.slideshow ], - functional: true, - props: { - delayControls: Number, - preload: Number, - videoAutoplay: Boolean, - template: String - }, - defaults: { - preload: 1, - videoAutoplay: false, - delayControls: 3e3, - items: [], - cls: "uk-open", - clsPage: "uk-lightbox-page", - selList: ".uk-lightbox-items", - attrItem: "uk-lightbox-item", - selClose: ".uk-close-large", - pauseOnHover: false, - velocity: 2, - Animations: Animations, - template: '
        ' - }, - created: function created() { - var this$1 = this; - this.$mount(append(this.container, this.template)); - this.caption = $(".uk-lightbox-caption", this.$el); - this.items.forEach(function() { - return append(this$1.list, "
      • "); - }); - }, - events: [ { - name: pointerMove + " " + pointerDown + " keydown", - handler: "showControls" - }, { - name: "click", - self: true, - delegate: function delegate() { - return this.slidesSelector; - }, - handler: function handler(e) { - e.preventDefault(); - this.hide(); - } - }, { - name: "shown", - self: true, - handler: "showControls" - }, { - name: "hide", - self: true, - handler: function handler() { - this.hideControls(); - removeClass(this.slides, this.clsActive); - Transition.stop(this.slides); - } - }, { - name: "keyup", - el: document, - handler: function handler(e) { - if (!this.isToggled(this.$el)) { - return; - } - switch (e.keyCode) { - case 37: - this.show("previous"); - break; - - case 39: - this.show("next"); - break; - } - } - }, { - name: "beforeitemshow", - handler: function handler(e) { - if (this.isToggled()) { - return; - } - this.preventCatch = true; - e.preventDefault(); - this.toggleNow(this.$el, true); - this.animation = Animations["scale"]; - removeClass(e.target, this.clsActive); - this.stack.splice(1, 0, this.index); - } - }, { - name: "itemshow", - handler: function handler(ref) { - var this$1 = this; - var target = ref.target; - var i = index(target); - var ref$1 = this.getItem(i); - var caption = ref$1.caption; - css(this.caption, "display", caption ? "" : "none"); - html(this.caption, caption); - for (var j = 0; j <= this.preload; j++) { - this$1.loadItem(this$1.getIndex(i + j)); - this$1.loadItem(this$1.getIndex(i - j)); - } - } - }, { - name: "itemshown", - handler: function handler() { - this.preventCatch = false; - } - }, { - name: "itemload", - handler: function handler(_, item) { - var this$1 = this; - var source = item.source; - var type = item.type; - var alt = item.alt; - this.setItem(item, ""); - if (!source) { - return; - } - var matches; - if (type === "image" || source.match(/\.(jp(e)?g|png|gif|svg)$/i)) { - getImage(source).then(function(img) { - return this$1.setItem(item, '' + (alt ? alt : '); - }, function() { - return this$1.setError(item); - }); - } else if (type === "video" || source.match(/\.(mp4|webm|ogv)$/i)) { - var video = $("'); - attr(video, "src", source); - on(video, "error", function() { - return this$1.setError(item); - }); - on(video, "loadedmetadata", function() { - attr(video, { - width: video.videoWidth, - height: video.videoHeight - }); - this$1.setItem(item, video); - }); - } else if (type === "iframe") { - this.setItem(item, ''); - } else if (matches = source.match(/\/\/.*?youtube(-nocookie)?\.[a-z]+\/watch\?v=([^&\s]+)/) || source.match(/()youtu\.be\/(.*)/)) { - var id = matches[2]; - var setIframe = function(width, height) { - if (width === void 0) width = 640; - if (height === void 0) height = 450; - return this$1.setItem(item, getIframe("//www.youtube" + (matches[1] || "") + ".com/embed/" + id, width, height, this$1.videoAutoplay)); - }; - getImage("//img.youtube.com/vi/" + id + "/maxresdefault.jpg").then(function(ref) { - var width = ref.width; - var height = ref.height; - if (width === 120 && height === 90) { - getImage("//img.youtube.com/vi/" + id + "/0.jpg").then(function(ref) { - var width = ref.width; - var height = ref.height; - return setIframe(width, height); - }, setIframe); - } else { - setIframe(width, height); - } - }, setIframe); - } else if (matches = source.match(/(\/\/.*?)vimeo\.[a-z]+\/([0-9]+).*?/)) { - ajax("//vimeo.com/api/oembed.json?maxwidth=1920&url=" + encodeURI(source), { - responseType: "json" - }).then(function(ref) { - var ref_response = ref.response; - var height = ref_response.height; - var width = ref_response.width; - return this$1.setItem(item, getIframe("//player.vimeo.com/video/" + matches[2], width, height, this$1.videoAutoplay)); - }); - } - } - } ], - methods: { - loadItem: function loadItem(index) { - if (index === void 0) index = this.index; - var item = this.getItem(index); - if (item.content) { - return; - } - trigger(this.$el, "itemload", [ item ]); - }, - getItem: function getItem(index) { - if (index === void 0) index = this.index; - return this.items[index] || {}; - }, - setItem: function setItem(item, content) { - assign(item, { - content: content - }); - var el = html(this.slides[this.items.indexOf(item)], content); - trigger(this.$el, "itemloaded", [ this, el ]); - UIkit.update(el); - }, - setError: function setError(item) { - this.setItem(item, ''); - }, - showControls: function showControls() { - clearTimeout(this.controlsTimer); - this.controlsTimer = setTimeout(this.hideControls, this.delayControls); - addClass(this.$el, "uk-active", "uk-transition-active"); - }, - hideControls: function hideControls() { - removeClass(this.$el, "uk-active", "uk-transition-active"); - } - } - }); - function getIframe(src, width, height, autoplay) { - return ''; - } - } - function plugin$2(UIkit) { - if (plugin$2.installed) { - return; - } - UIkit.use(plugin$3); - var util = UIkit.util; - var $$ = util.$$; - var assign = util.assign; - var data = util.data; - var index = util.index; - var ref = UIkit.components.lightboxPanel; - var options = ref.options; - UIkit.component("lightbox", { - attrs: true, - props: assign({ - toggle: String - }, options.props), - defaults: assign({ - toggle: "a" - }, Object.keys(options.props).reduce(function(defaults, key) { - defaults[key] = options.defaults[key]; - return defaults; - }, {})), - computed: { - toggles: function toggles(ref, $el) { - var toggle = ref.toggle; - return $$(toggle, $el); - } - }, - disconnected: function disconnected() { - this._destroy(); - }, - events: [ { - name: "click", - delegate: function delegate() { - return this.toggle + ":not(.uk-disabled)"; - }, - handler: function handler(e) { - e.preventDefault(); - e.current.blur(); - this.show(index(this.toggles, e.current)); - } - } ], - update: function update(data) { - data.toggles = data.toggles || this.toggles; - if (this.panel && this.animation) { - this.panel.$props.animation = this.animation; - this.panel.$emit(); - } - if (!this.panel || isEqualList(data.toggles, this.toggles)) { - return; - } - data.toggles = this.toggles; - this._destroy(); - this._init(); - }, - methods: { - _init: function _init() { - return this.panel = this.panel || UIkit.lightboxPanel(assign({}, this.$props, { - items: this.toggles.reduce(function(items, el) { - items.push([ "href", "caption", "type", "poster", "alt" ].reduce(function(obj, attr) { - obj[attr === "href" ? "source" : attr] = data(el, attr); - return obj; - }, {})); - return items; - }, []) - })); - }, - _destroy: function _destroy() { - if (this.panel) { - this.panel.$destroy(true); - this.panel = null; - } - }, - show: function show(index) { - if (!this.panel) { - this._init(); - } - return this.panel.show(index); - }, - hide: function hide() { - return this.panel && this.panel.hide(); - } - } - }); - function isEqualList(listA, listB) { - return listA.length === listB.length && listA.every(function(el, i) { - return el === listB[i]; - }); - } - } - function plugin$6(UIkit) { - var obj; - if (plugin$6.installed) { - return; - } - var ref = UIkit.util; - var append = ref.append; - var apply = ref.apply; - var closest = ref.closest; - var css = ref.css; - var pointerEnter = ref.pointerEnter; - var pointerLeave = ref.pointerLeave; - var remove = ref.remove; - var toFloat = ref.toFloat; - var Transition = ref.Transition; - var trigger = ref.trigger; - var containers = {}; - UIkit.component("notification", { - functional: true, - args: [ "message", "status" ], - defaults: { - message: "", - status: "", - timeout: 5e3, - group: null, - pos: "top-center", - clsClose: "uk-notification-close", - clsMsg: "uk-notification-message" - }, - created: function created() { - if (!containers[this.pos]) { - containers[this.pos] = append(UIkit.container, '
        '); - } - var container = css(containers[this.pos], "display", "block"); - this.$mount(append(container, '
        ' + this.message + "
        ")); - }, - ready: function ready() { - var this$1 = this; - var marginBottom = toFloat(css(this.$el, "marginBottom")); - Transition.start(css(this.$el, { - opacity: 0, - marginTop: -this.$el.offsetHeight, - marginBottom: 0 - }), { - opacity: 1, - marginTop: 0, - marginBottom: marginBottom - }).then(function() { - if (this$1.timeout) { - this$1.timer = setTimeout(this$1.close, this$1.timeout); - } - }); - }, - events: (obj = { - click: function click(e) { - if (closest(e.target, 'a[href="#"]')) { - e.preventDefault(); - } - this.close(); - } - }, obj[pointerEnter] = function() { - if (this.timer) { - clearTimeout(this.timer); - } - }, obj[pointerLeave] = function() { - if (this.timeout) { - this.timer = setTimeout(this.close, this.timeout); - } - }, obj), - methods: { - close: function close(immediate) { - var this$1 = this; - var removeFn = function() { - trigger(this$1.$el, "close", [ this$1 ]); - remove(this$1.$el); - if (!containers[this$1.pos].children.length) { - css(containers[this$1.pos], "display", "none"); - } - }; - if (this.timer) { - clearTimeout(this.timer); - } - if (immediate) { - removeFn(); - } else { - Transition.start(this.$el, { - opacity: 0, - marginTop: -this.$el.offsetHeight, - marginBottom: 0 - }).then(removeFn); - } - } - } - }); - UIkit.notification.closeAll = function(group, immediate) { - apply(document.body, function(el) { - var notification = UIkit.getComponent(el, "notification"); - if (notification && (!group || group === notification.group)) { - notification.close(immediate); - } - }); - }; - } - function plugin$8(UIkit) { - if (plugin$8.installed) { - return; - } - var mixin = UIkit.mixin; - var util = UIkit.util; - var css = util.css; - var Dimensions = util.Dimensions; - var each = util.each; - var getImage = util.getImage; - var includes = util.includes; - var isNumber = util.isNumber; - var isUndefined = util.isUndefined; - var toFloat = util.toFloat; - var props = [ "x", "y", "bgx", "bgy", "rotate", "scale", "color", "backgroundColor", "borderColor", "opacity", "blur", "hue", "grayscale", "invert", "saturate", "sepia", "fopacity" ]; - mixin.parallax = { - props: props.reduce(function(props, prop) { - props[prop] = "list"; - return props; - }, { - media: "media" - }), - defaults: props.reduce(function(defaults, prop) { - defaults[prop] = undefined; - return defaults; - }, { - media: false - }), - computed: { - props: function props$1(properties, $el) { - var this$1 = this; - return props.reduce(function(props, prop) { - if (isUndefined(properties[prop])) { - return props; - } - var isColor = prop.match(/color/i); - var isCssProp = isColor || prop === "opacity"; - var pos, bgPos, diff; - var steps = properties[prop].slice(0); - if (isCssProp) { - css($el, prop, ""); - } - if (steps.length < 2) { - steps.unshift((prop === "scale" ? 1 : isCssProp ? css($el, prop) : 0) || 0); - } - var unit = includes(steps.join(""), "%") ? "%" : "px"; - if (isColor) { - var ref = $el.style; - var color = ref.color; - steps = steps.map(function(step) { - return parseColor($el, step); - }); - $el.style.color = color; - } else { - steps = steps.map(toFloat); - } - if (prop.match(/^bg/)) { - css($el, "background-position-" + prop[2], ""); - bgPos = css($el, "backgroundPosition").split(" ")[prop[2] === "x" ? 0 : 1]; - if (this$1.covers) { - var min = Math.min.apply(Math, steps); - var max = Math.max.apply(Math, steps); - var down = steps.indexOf(min) < steps.indexOf(max); - diff = max - min; - steps = steps.map(function(step) { - return step - (down ? min : max); - }); - pos = (down ? -diff : 0) + "px"; - } else { - pos = bgPos; - } - } - props[prop] = { - steps: steps, - unit: unit, - pos: pos, - bgPos: bgPos, - diff: diff - }; - return props; - }, {}); - }, - bgProps: function bgProps() { - var this$1 = this; - return [ "bgx", "bgy" ].filter(function(bg) { - return bg in this$1.props; - }); - }, - covers: function covers$1(_, $el) { - return covers($el); - } - }, - disconnected: function disconnected() { - delete this._image; - }, - update: [ { - read: function read(data) { - var this$1 = this; - data.active = !this.media || window.matchMedia(this.media).matches; - if (data.image) { - data.image.dimEl = { - width: this.$el.offsetWidth, - height: this.$el.offsetHeight - }; - } - if ("image" in data || !this.covers || !this.bgProps.length) { - return; - } - var src = css(this.$el, "backgroundImage").replace(/^none|url\(["']?(.+?)["']?\)$/, "$1"); - if (!src) { - return; - } - data.image = false; - getImage(src).then(function(img) { - data.image = { - width: img.naturalWidth, - height: img.naturalHeight - }; - this$1.$emit(); - }); - }, - write: function write(ref) { - var this$1 = this; - var image = ref.image; - var active = ref.active; - if (!image) { - return; - } - if (!active) { - css(this.$el, { - backgroundSize: "", - backgroundRepeat: "" - }); - return; - } - var dimEl = image.dimEl; - var dim = Dimensions.cover(image, dimEl); - this.bgProps.forEach(function(prop) { - var ref = this$1.props[prop]; - var diff = ref.diff; - var bgPos = ref.bgPos; - var steps = ref.steps; - var attr = prop === "bgy" ? "height" : "width"; - var span = dim[attr] - dimEl[attr]; - if (!bgPos.match(/%$|0px/)) { - return; - } - if (span < diff) { - dimEl[attr] = dim[attr] + diff - span; - } else if (span > diff) { - var bgPosFloat = parseFloat(bgPos); - if (bgPosFloat) { - this$1.props[prop].steps = steps.map(function(step) { - return step - (span - diff) / (100 / bgPosFloat); - }); - } - } - dim = Dimensions.cover(image, dimEl); - }); - css(this.$el, { - backgroundSize: dim.width + "px " + dim.height + "px", - backgroundRepeat: "no-repeat" - }); - }, - events: [ "load", "resize" ] - } ], - methods: { - reset: function reset() { - var this$1 = this; - each(this.getCss(0), function(_, prop) { - return css(this$1.$el, prop, ""); - }); - }, - getCss: function getCss(percent) { - var ref = this; - var props = ref.props; - var translated = false; - return Object.keys(props).reduce(function(css, prop) { - var ref = props[prop]; - var steps = ref.steps; - var unit = ref.unit; - var pos = ref.pos; - var value = getValue(steps, percent); - switch (prop) { - case "x": - case "y": - if (translated) { - break; - } - var ref$1 = [ "x", "y" ].map(function(dir) { - return prop === dir ? value + unit : props[dir] ? getValue(props[dir].steps, percent) + props[dir].unit : 0; - }); - var x = ref$1[0]; - var y = ref$1[1]; - translated = css.transform += " translate3d(" + x + ", " + y + ", 0)"; - break; - - case "rotate": - css.transform += " rotate(" + value + "deg)"; - break; - - case "scale": - css.transform += " scale(" + value + ")"; - break; - - case "bgy": - case "bgx": - css["background-position-" + prop[2]] = "calc(" + pos + " + " + (value + unit) + ")"; - break; - - case "color": - case "backgroundColor": - case "borderColor": - var ref$2 = getStep(steps, percent); - var start = ref$2[0]; - var end = ref$2[1]; - var p = ref$2[2]; - css[prop] = "rgba(" + start.map(function(value, i) { - value = value + p * (end[i] - value); - return i === 3 ? toFloat(value) : parseInt(value, 10); - }).join(",") + ")"; - break; - - case "blur": - css.filter += " blur(" + value + "px)"; - break; - - case "hue": - css.filter += " hue-rotate(" + value + "deg)"; - break; - - case "fopacity": - css.filter += " opacity(" + value + "%)"; - break; - - case "grayscale": - case "invert": - case "saturate": - case "sepia": - css.filter += " " + prop + "(" + value + "%)"; - break; - - default: - css[prop] = value; - } - return css; - }, { - transform: "", - filter: "" - }); - } - } - }; - function parseColor(el, color) { - return css(css(el, "color", color), "color").split(/[(),]/g).slice(1, -1).concat(1).slice(0, 4).map(function(n) { - return toFloat(n); - }); - } - function getStep(steps, percent) { - var count = steps.length - 1; - var index = Math.min(Math.floor(count * percent), count - 1); - var step = steps.slice(index, index + 2); - step.push(percent === 1 ? 1 : percent % (1 / count) * count); - return step; - } - function getValue(steps, percent) { - var ref = getStep(steps, percent); - var start = ref[0]; - var end = ref[1]; - var p = ref[2]; - return (isNumber(start) ? start + Math.abs(start - end) * p * (start < end ? 1 : -1) : +end).toFixed(2); - } - function covers(el) { - var ref = el.style; - var backgroundSize = ref.backgroundSize; - var covers = css(css(el, "backgroundSize", ""), "backgroundSize") === "cover"; - el.style.backgroundSize = backgroundSize; - return covers; - } - } - function plugin$7(UIkit) { - if (plugin$7.installed) { - return; - } - UIkit.use(plugin$8); - var mixin = UIkit.mixin; - var util = UIkit.util; - var clamp = util.clamp; - var css = util.css; - var scrolledOver = util.scrolledOver; - var query = util.query; - UIkit.component("parallax", { - mixins: [ mixin.parallax ], - props: { - target: String, - viewport: Number, - easing: Number - }, - defaults: { - target: false, - viewport: 1, - easing: 1 - }, - computed: { - target: function target(ref, $el) { - var target = ref.target; - return target && query(target, $el) || $el; - } - }, - update: [ { - read: function read(ref) { - var percent = ref.percent; - return { - prev: percent, - percent: ease(scrolledOver(this.target) / (this.viewport || 1), this.easing) - }; - }, - write: function write(ref, ref$1) { - var prev = ref.prev; - var percent = ref.percent; - var active = ref.active; - var type = ref$1.type; - if (type !== "scroll") { - prev = false; - } - if (!active) { - this.reset(); - return; - } - if (prev !== percent) { - css(this.$el, this.getCss(percent)); - } - }, - events: [ "scroll", "load", "resize" ] - } ] - }); - function ease(percent, easing) { - return clamp(percent * (1 - (easing - easing * percent))); - } - } - function SliderReactive(UIkit) { - var ref = UIkit.util; - var removeClass = ref.removeClass; - return { - update: [ { - write: function write() { - if (this.stack.length || this.dragging) { - return; - } - var index = this.getValidIndex(); - delete this.index; - removeClass(this.slides, this.clsActive, this.clsActivated); - this.show(index); - }, - events: [ "load", "resize" ] - } ] - }; - } - function TransitionerPlugin$1(UIkit) { - var ref = UIkit.util; - var assign = ref.assign; - var clamp = ref.clamp; - var createEvent = ref.createEvent; - var css = ref.css; - var Deferred = ref.Deferred; - var includes = ref.includes; - var index = ref.index; - var isRtl = ref.isRtl; - var noop = ref.noop; - var sortBy = ref.sortBy; - var toNodes = ref.toNodes; - var Transition = ref.Transition; - var trigger = ref.trigger; - var Transitioner = assign(function(prev, next, dir, ref) { - var center = ref.center; - var easing = ref.easing; - var list = ref.list; - var deferred = new Deferred(); - var from = prev ? Transitioner.getLeft(prev, list, center) : Transitioner.getLeft(next, list, center) + next.offsetWidth * dir, to = next ? Transitioner.getLeft(next, list, center) : from + prev.offsetWidth * dir * (isRtl ? -1 : 1); - return { - dir: dir, - show: function show(duration, percent, linear) { - if (percent === void 0) percent = 0; - var timing = linear ? "linear" : easing; - duration -= Math.round(duration * clamp(percent, -1, 1)); - this.translate(percent); - prev && this.updateTranslates(); - percent = prev ? percent : clamp(percent, 0, 1); - triggerUpdate(this.getItemIn(), "itemin", { - percent: percent, - duration: duration, - timing: timing, - dir: dir - }); - prev && triggerUpdate(this.getItemIn(true), "itemout", { - percent: 1 - percent, - duration: duration, - timing: timing, - dir: dir - }); - Transition.start(list, { - transform: translate(-to * (isRtl ? -1 : 1), "px") - }, duration, timing).then(deferred.resolve, noop); - return deferred.promise; - }, - stop: function stop() { - return Transition.stop(list); - }, - cancel: function cancel() { - Transition.cancel(list); - }, - reset: function reset() { - css(list, "transform", ""); - }, - forward: function forward(duration, percent) { - if (percent === void 0) percent = this.percent(); - Transition.cancel(list); - return this.show(duration, percent, true); - }, - translate: function translate$1(percent) { - var distance = this.getDistance() * dir * (isRtl ? -1 : 1); - css(list, "transform", translate(clamp(-to + (distance - distance * percent), -Transitioner.getWidth(list), list.offsetWidth) * (isRtl ? -1 : 1), "px")); - this.updateTranslates(); - if (prev) { - percent = clamp(percent, -1, 1); - triggerUpdate(this.getItemIn(), "itemtranslatein", { - percent: percent, - dir: dir - }); - triggerUpdate(this.getItemIn(true), "itemtranslateout", { - percent: 1 - percent, - dir: dir - }); - } - }, - percent: function percent() { - return Math.abs((css(list, "transform").split(",")[4] * (isRtl ? -1 : 1) + from) / (to - from)); - }, - getDistance: function getDistance() { - return Math.abs(to - from); - }, - getItemIn: function getItemIn(out) { - if (out === void 0) out = false; - var actives = this.getActives(); - var all = sortBy(slides(list), "offsetLeft"); - var i = index(all, actives[dir * (out ? -1 : 1) > 0 ? actives.length - 1 : 0]); - return ~i && all[i + (prev && !out ? dir : 0)]; - }, - getActives: function getActives() { - var left = Transitioner.getLeft(prev || next, list, center); - return sortBy(slides(list).filter(function(slide) { - var slideLeft = Transitioner.getElLeft(slide, list); - return slideLeft >= left && slideLeft + slide.offsetWidth <= list.offsetWidth + left; - }), "offsetLeft"); - }, - updateTranslates: function updateTranslates() { - var actives = this.getActives(); - slides(list).forEach(function(slide) { - var isActive = includes(actives, slide); - triggerUpdate(slide, "itemtranslate" + (isActive ? "in" : "out"), { - percent: isActive ? 1 : 0, - dir: slide.offsetLeft <= next.offsetLeft ? 1 : -1 - }); - }); - } - }; - }, { - getLeft: function getLeft(el, list, center) { - var left = this.getElLeft(el, list); - return center ? left - this.center(el, list) : Math.min(left, this.getMax(list)); - }, - getMax: function getMax(list) { - return Math.max(0, this.getWidth(list) - list.offsetWidth); - }, - getWidth: function getWidth(list) { - return slides(list).reduce(function(right, el) { - return el.offsetWidth + right; - }, 0); - }, - getMaxWidth: function getMaxWidth(list) { - return slides(list).reduce(function(right, el) { - return Math.max(right, el.offsetWidth); - }, 0); - }, - center: function center(el, list) { - return list.offsetWidth / 2 - el.offsetWidth / 2; - }, - getElLeft: function getElLeft(el, list) { - return (el.offsetLeft + (isRtl ? el.offsetWidth - list.offsetWidth : 0)) * (isRtl ? -1 : 1); - } - }); - function triggerUpdate(el, type, data) { - trigger(el, createEvent(type, false, false, data)); - } - function slides(list) { - return toNodes(list.children); - } - return Transitioner; - } - function ParallaxPlugin(UIkit, parent) { - UIkit.use(plugin$8); - var mixin = UIkit.mixin; - var UIkit_util = UIkit.util; - var closest = UIkit_util.closest; - var css = UIkit_util.css; - var endsWith = UIkit_util.endsWith; - var noop = UIkit_util.noop; - var Transition = UIkit_util.Transition; - return { - mixins: [ mixin.parallax ], - computed: { - item: function item() { - var slider = UIkit.getComponent(closest(this.$el, ".uk-" + parent), parent); - return slider && closest(this.$el, slider.slidesSelector); - } - }, - events: [ { - name: "itemshown", - self: true, - el: function el() { - return this.item; - }, - handler: function handler() { - css(this.$el, this.getCss(.5)); - } - }, { - name: "itemin itemout", - self: true, - el: function el() { - return this.item; - }, - handler: function handler(ref) { - var type = ref.type; - var ref_detail = ref.detail; - var percent = ref_detail.percent; - var duration = ref_detail.duration; - var timing = ref_detail.timing; - var dir = ref_detail.dir; - Transition.cancel(this.$el); - css(this.$el, this.getCss(getCurrent(type, dir, percent))); - Transition.start(this.$el, this.getCss(isIn(type) ? .5 : dir > 0 ? 1 : 0), duration, timing).catch(noop); - } - }, { - name: "transitioncanceled transitionend", - self: true, - el: function el() { - return this.item; - }, - handler: function handler() { - Transition.cancel(this.$el); - } - }, { - name: "itemtranslatein itemtranslateout", - self: true, - el: function el() { - return this.item; - }, - handler: function handler(ref) { - var type = ref.type; - var ref_detail = ref.detail; - var percent = ref_detail.percent; - var dir = ref_detail.dir; - Transition.cancel(this.$el); - css(this.$el, this.getCss(getCurrent(type, dir, percent))); - } - } ] - }; - function isIn(type) { - return endsWith(type, "in"); - } - function getCurrent(type, dir, percent) { - percent /= 2; - return !isIn(type) ? dir < 0 ? percent : 1 - percent : dir < 0 ? 1 - percent : percent; - } - } - function plugin$9(UIkit) { - if (plugin$9.installed) { - return; - } - UIkit.use(plugin$5); - var mixin = UIkit.mixin; - var UIkit_util = UIkit.util; - var $ = UIkit_util.$; - var $$ = UIkit_util.$$; - var addClass = UIkit_util.addClass; - var css = UIkit_util.css; - var data = UIkit_util.data; - var includes = UIkit_util.includes; - var isNumeric = UIkit_util.isNumeric; - var isUndefined = UIkit_util.isUndefined; - var offset = UIkit_util.offset; - var toggleClass = UIkit_util.toggleClass; - var toFloat = UIkit_util.toFloat; - var Transitioner = TransitionerPlugin$1(UIkit); - UIkit.component("slider-parallax", ParallaxPlugin(UIkit, "slider")); - UIkit.component("slider", { - mixins: [ mixin.class, mixin.slider, SliderReactive(UIkit) ], - props: { - center: Boolean, - sets: Boolean - }, - defaults: { - center: false, - sets: false, - attrItem: "uk-slider-item", - selList: ".uk-slider-items", - selNav: ".uk-slider-nav", - clsContainer: "uk-slider-container", - Transitioner: Transitioner - }, - computed: { - avgWidth: function avgWidth() { - return Transitioner.getWidth(this.list) / this.length; - }, - finite: function finite(ref) { - var finite = ref.finite; - return finite || Transitioner.getWidth(this.list) < this.list.offsetWidth + Transitioner.getMaxWidth(this.list) + this.center; - }, - maxIndex: function maxIndex() { - var this$1 = this; - if (!this.finite || this.center && !this.sets) { - return this.length - 1; - } - if (this.center) { - return this.sets[this.sets.length - 1]; - } - css(this.slides, "order", ""); - var max = Transitioner.getMax(this.list); - var i = this.length; - while (i--) { - if (Transitioner.getElLeft(this$1.list.children[i], this$1.list) < max) { - return Math.min(i + 1, this$1.length - 1); - } - } - return 0; - }, - sets: function sets(ref) { - var this$1 = this; - var sets = ref.sets; - var width = this.list.offsetWidth / (this.center ? 2 : 1); - var left = 0; - var leftCenter = width; - sets = sets && this.slides.reduce(function(sets, slide, i) { - var dim = offset(slide); - if (dim.right > left) { - if (!this$1.center && i > this$1.maxIndex) { - i = this$1.maxIndex; - } - if (!includes(sets, i)) { - var cmp = this$1.slides[i + 1]; - if (this$1.center && cmp && dim.width < leftCenter - offset(cmp).width / 2) { - leftCenter -= dim.width; - } else { - leftCenter = width; - sets.push(i); - left = dim.left + width + (this$1.center ? dim.width / 2 : 0); - } - } - } - return sets; - }, []); - return sets && sets.length && sets; - }, - transitionOptions: function transitionOptions() { - return { - center: this.center, - list: this.list - }; - } - }, - connected: function connected() { - toggleClass(this.$el, this.clsContainer, !$("." + this.clsContainer, this.$el)); - }, - update: { - write: function write() { - var this$1 = this; - $$("[" + this.attrItem + "],[data-" + this.attrItem + "]", this.$el).forEach(function(el) { - var index = data(el, this$1.attrItem); - this$1.maxIndex && toggleClass(el, "uk-hidden", isNumeric(index) && (this$1.sets && !includes(this$1.sets, toFloat(index)) || index > this$1.maxIndex)); - }); - }, - events: [ "load", "resize" ] - }, - events: { - beforeitemshow: function beforeitemshow(e) { - var this$1 = this; - if (!this.dragging && this.sets && this.stack.length < 2 && !includes(this.sets, this.index)) { - this.index = this.getValidIndex(); - } - var diff = Math.abs(this.index - this.prevIndex + (this.dir > 0 && this.index < this.prevIndex || this.dir < 0 && this.index > this.prevIndex ? (this.maxIndex + 1) * this.dir : 0)); - if (!this.dragging && diff > 1) { - for (var i = 0; i < diff; i++) { - this$1.stack.splice(1, 0, this$1.dir > 0 ? "next" : "previous"); - } - e.preventDefault(); - return; - } - this.duration = speedUp(this.avgWidth / this.velocity) * ((this.dir < 0 || !this.slides[this.prevIndex] ? this.slides[this.index] : this.slides[this.prevIndex]).offsetWidth / this.avgWidth); - this.reorder(); - }, - itemshow: function itemshow() { - !isUndefined(this.prevIndex) && addClass(this._getTransitioner().getItemIn(), this.clsActive); - }, - itemshown: function itemshown() { - var this$1 = this; - var actives = this._getTransitioner(this.index).getActives(); - this.slides.forEach(function(slide) { - return toggleClass(slide, this$1.clsActive, includes(actives, slide)); - }); - (!this.sets || includes(this.sets, toFloat(this.index))) && this.slides.forEach(function(slide) { - return toggleClass(slide, this$1.clsActivated, includes(actives, slide)); - }); - } - }, - methods: { - reorder: function reorder() { - var this$1 = this; - css(this.slides, "order", ""); - if (this.finite) { - return; - } - var index = this.dir > 0 && this.slides[this.prevIndex] ? this.prevIndex : this.index; - this.slides.forEach(function(slide, i) { - return css(slide, "order", this$1.dir > 0 && i < index ? 1 : this$1.dir < 0 && i >= this$1.index ? -1 : ""); - }); - if (!this.center) { - return; - } - var next = this.slides[index]; - var width = this.list.offsetWidth / 2 - next.offsetWidth / 2; - var j = 0; - while (width > 0) { - var slideIndex = this$1.getIndex(--j + index, index); - var slide = this$1.slides[slideIndex]; - css(slide, "order", slideIndex > index ? -2 : -1); - width -= slide.offsetWidth; - } - }, - getValidIndex: function getValidIndex(index, prevIndex) { - var this$1 = this; - if (index === void 0) index = this.index; - if (prevIndex === void 0) prevIndex = this.prevIndex; - index = this.getIndex(index, prevIndex); - if (!this.sets) { - return index; - } - var prev; - do { - if (includes(this$1.sets, index)) { - return index; - } - prev = index; - index = this$1.getIndex(index + this$1.dir, prevIndex); - } while (index !== prev); - return index; - } - } - }); - } - function AnimationsPlugin$2(UIkit) { - var mixin = UIkit.mixin; - var UIkit_util = UIkit.util; - var assign = UIkit_util.assign; - var css = UIkit_util.css; - var Animations = assign({}, mixin.slideshow.defaults.Animations, { - fade: { - show: function show() { - return [ { - opacity: 0, - zIndex: 0 - }, { - zIndex: -1 - } ]; - }, - percent: function percent(current) { - return 1 - css(current, "opacity"); - }, - translate: function translate$$1(percent) { - return [ { - opacity: 1 - percent, - zIndex: 0 - }, { - zIndex: -1 - } ]; - } - }, - scale: { - show: function show() { - return [ { - opacity: 0, - transform: scale3d(1 + .5), - zIndex: 0 - }, { - zIndex: -1 - } ]; - }, - percent: function percent(current) { - return 1 - css(current, "opacity"); - }, - translate: function translate$$1(percent) { - return [ { - opacity: 1 - percent, - transform: scale3d(1 + .5 * percent), - zIndex: 0 - }, { - zIndex: -1 - } ]; - } - }, - pull: { - show: function show(dir) { - return dir < 0 ? [ { - transform: translate(30), - zIndex: -1 - }, { - transform: translate(), - zIndex: 0 - } ] : [ { - transform: translate(-100), - zIndex: 0 - }, { - transform: translate(), - zIndex: -1 - } ]; - }, - percent: function percent(current, next, dir) { - return dir < 0 ? 1 - Animations.translated(next) : Animations.translated(current); - }, - translate: function translate$1(percent, dir) { - return dir < 0 ? [ { - transform: translate(30 * percent), - zIndex: -1 - }, { - transform: translate(-100 * (1 - percent)), - zIndex: 0 - } ] : [ { - transform: translate(-percent * 100), - zIndex: 0 - }, { - transform: translate(30 * (1 - percent)), - zIndex: -1 - } ]; - } - }, - push: { - show: function show(dir) { - return dir < 0 ? [ { - transform: translate(100), - zIndex: 0 - }, { - transform: translate(), - zIndex: -1 - } ] : [ { - transform: translate(-30), - zIndex: -1 - }, { - transform: translate(), - zIndex: 0 - } ]; - }, - percent: function percent(current, next, dir) { - return dir > 0 ? 1 - Animations.translated(next) : Animations.translated(current); - }, - translate: function translate$2(percent, dir) { - return dir < 0 ? [ { - transform: translate(percent * 100), - zIndex: 0 - }, { - transform: translate(-30 * (1 - percent)), - zIndex: -1 - } ] : [ { - transform: translate(-30 * percent), - zIndex: -1 - }, { - transform: translate(100 * (1 - percent)), - zIndex: 0 - } ]; - } - } - }); - return Animations; - } - function plugin$10(UIkit) { - if (plugin$10.installed) { - return; - } - UIkit.use(plugin$4); - var mixin = UIkit.mixin; - var height = UIkit.util.height; - var Animations = AnimationsPlugin$2(UIkit); - UIkit.component("slideshow-parallax", ParallaxPlugin(UIkit, "slideshow")); - UIkit.component("slideshow", { - mixins: [ mixin.class, mixin.slideshow, SliderReactive(UIkit) ], - props: { - ratio: String, - minHeight: Boolean, - maxHeight: Boolean - }, - defaults: { - ratio: "16:9", - minHeight: false, - maxHeight: false, - selList: ".uk-slideshow-items", - attrItem: "uk-slideshow-item", - selNav: ".uk-slideshow-nav", - Animations: Animations - }, - update: { - read: function read() { - var ref = this.ratio.split(":").map(Number); - var width = ref[0]; - var height = ref[1]; - height = height * this.$el.offsetWidth / width; - if (this.minHeight) { - height = Math.max(this.minHeight, height); - } - if (this.maxHeight) { - height = Math.min(this.maxHeight, height); - } - return { - height: height - }; - }, - write: function write(ref) { - var hgt = ref.height; - height(this.list, Math.floor(hgt)); - }, - events: [ "load", "resize" ] - } - }); - } - function plugin$11(UIkit) { - var obj; - if (plugin$11.installed) { - return; - } - var mixin = UIkit.mixin; - var util = UIkit.util; - var addClass = util.addClass; - var after = util.after; - var assign = util.assign; - var append = util.append; - var attr = util.attr; - var before = util.before; - var closest = util.closest; - var css = util.css; - var height = util.height; - var fastdom = util.fastdom; - var getPos = util.getPos; - var includes = util.includes; - var index = util.index; - var isInput = util.isInput; - var noop = util.noop; - var offset = util.offset; - var off = util.off; - var on = util.on; - var pointerDown = util.pointerDown; - var pointerMove = util.pointerMove; - var pointerUp = util.pointerUp; - var position = util.position; - var preventClick = util.preventClick; - var Promise = util.Promise; - var remove = util.remove; - var removeClass = util.removeClass; - var toggleClass = util.toggleClass; - var toNodes = util.toNodes; - var Transition = util.Transition; - var trigger = util.trigger; - var within = util.within; - UIkit.component("sortable", { - mixins: [ mixin.class ], - props: { - group: String, - animation: Number, - threshold: Number, - clsItem: String, - clsPlaceholder: String, - clsDrag: String, - clsDragState: String, - clsBase: String, - clsNoDrag: String, - clsEmpty: String, - clsCustom: String, - handle: String - }, - defaults: { - group: false, - animation: 150, - threshold: 5, - clsItem: "uk-sortable-item", - clsPlaceholder: "uk-sortable-placeholder", - clsDrag: "uk-sortable-drag", - clsDragState: "uk-drag", - clsBase: "uk-sortable", - clsNoDrag: "uk-sortable-nodrag", - clsEmpty: "uk-sortable-empty", - clsCustom: "", - handle: false - }, - init: function init() { - var this$1 = this; - [ "init", "start", "move", "end" ].forEach(function(key) { - var fn = this$1[key]; - this$1[key] = function(e) { - this$1.scrollY = window.pageYOffset; - var ref = getPos(e); - var x = ref.x; - var y = ref.y; - this$1.pos = { - x: x, - y: y - }; - fn(e); - }; - }); - }, - events: (obj = {}, obj[pointerDown] = "init", obj), - update: { - write: function write() { - if (this.clsEmpty) { - toggleClass(this.$el, this.clsEmpty, !this.$el.children.length); - } - if (!this.drag) { - return; - } - offset(this.drag, { - top: this.pos.y + this.origin.top, - left: this.pos.x + this.origin.left - }); - var ref = offset(this.drag); - var top = ref.top; - var bottom = top + this.drag.offsetHeight; - var scroll; - if (top > 0 && top < this.scrollY) { - scroll = this.scrollY - 5; - } else if (bottom < height(document) && bottom > height(window) + this.scrollY) { - scroll = this.scrollY + 5; - } - scroll && setTimeout(function() { - return window.scrollTo(window.scrollX, scroll); - }, 5); - } - }, - methods: { - init: function init(e) { - var target = e.target; - var button = e.button; - var defaultPrevented = e.defaultPrevented; - var ref = toNodes(this.$el.children).filter(function(el) { - return within(target, el); - }); - var placeholder = ref[0]; - if (!placeholder || isInput(e.target) || this.handle && !within(target, this.handle) || button > 0 || within(target, "." + this.clsNoDrag) || defaultPrevented) { - return; - } - e.preventDefault(); - this.touched = [ this ]; - this.placeholder = placeholder; - this.origin = assign({ - target: target, - index: index(placeholder) - }, this.pos); - on(document, pointerMove, this.move); - on(document, pointerUp, this.end); - on(window, "scroll", this.scroll); - if (!this.threshold) { - this.start(e); - } - }, - start: function start(e) { - this.drag = append(UIkit.container, this.placeholder.outerHTML.replace(/^
      • $/i, "div>")); - css(this.drag, assign({ - boxSizing: "border-box", - width: this.placeholder.offsetWidth, - height: this.placeholder.offsetHeight - }, css(this.placeholder, [ "paddingLeft", "paddingRight", "paddingTop", "paddingBottom" ]))); - attr(this.drag, "uk-no-boot", ""); - addClass(this.drag, this.clsDrag, this.clsCustom); - height(this.drag.firstElementChild, height(this.placeholder.firstElementChild)); - var ref = offset(this.placeholder); - var left = ref.left; - var top = ref.top; - assign(this.origin, { - left: left - this.pos.x, - top: top - this.pos.y - }); - addClass(this.placeholder, this.clsPlaceholder); - addClass(this.$el.children, this.clsItem); - addClass(document.documentElement, this.clsDragState); - trigger(this.$el, "start", [ this, this.placeholder, this.drag ]); - this.move(e); - }, - move: function move(e) { - if (!this.drag) { - if (Math.abs(this.pos.x - this.origin.x) > this.threshold || Math.abs(this.pos.y - this.origin.y) > this.threshold) { - this.start(e); - } - return; - } - this.$emit(); - var target = e.type === "mousemove" ? e.target : document.elementFromPoint(this.pos.x - document.body.scrollLeft, this.pos.y - document.body.scrollTop); - var sortable = getSortable(target); - var previous = getSortable(this.placeholder); - var move = sortable !== previous; - if (!sortable || within(target, this.placeholder) || move && (!sortable.group || sortable.group !== previous.group)) { - return; - } - target = sortable.$el === target.parentNode && target || toNodes(sortable.$el.children).filter(function(element) { - return within(target, element); - })[0]; - if (move) { - previous.remove(this.placeholder); - } else if (!target) { - return; - } - sortable.insert(this.placeholder, target); - if (!includes(this.touched, sortable)) { - this.touched.push(sortable); - } - }, - scroll: function scroll() { - var scroll = window.pageYOffset; - if (scroll !== this.scrollY) { - this.pos.y += scroll - this.scrollY; - this.scrollY = scroll; - this.$emit(); - } - }, - end: function end(e) { - off(document, pointerMove, this.move); - off(document, pointerUp, this.end); - off(window, "scroll", this.scroll); - if (!this.drag) { - if (e.type !== "mouseup" && within(e.target, "a[href]")) { - location.href = closest(e.target, "a[href]").href; - } - return; - } - preventClick(); - var sortable = getSortable(this.placeholder); - if (this === sortable) { - if (this.origin.index !== index(this.placeholder)) { - trigger(this.$el, "moved", [ this, this.placeholder ]); - } - } else { - trigger(sortable.$el, "added", [ sortable, this.placeholder ]); - trigger(this.$el, "removed", [ this, this.placeholder ]); - } - trigger(this.$el, "stop", [ this ]); - remove(this.drag); - this.drag = null; - var classes = this.touched.map(function(sortable) { - return sortable.clsPlaceholder + " " + sortable.clsItem; - }).join(" "); - this.touched.forEach(function(sortable) { - return removeClass(sortable.$el.children, classes); - }); - removeClass(document.documentElement, this.clsDragState); - }, - insert: function insert(element, target) { - var this$1 = this; - addClass(this.$el.children, this.clsItem); - var insert = function() { - if (target) { - if (!within(element, this$1.$el) || isPredecessor(element, target)) { - before(target, element); - } else { - after(target, element); - } - } else { - append(this$1.$el, element); - } - }; - if (this.animation) { - this.animate(insert); - } else { - insert(); - } - }, - remove: function remove$1(element) { - if (!within(element, this.$el)) { - return; - } - if (this.animation) { - this.animate(function() { - return remove(element); - }); - } else { - remove(element); - } - }, - animate: function animate(action) { - var this$1 = this; - var props = []; - var children = toNodes(this.$el.children); - var reset = { - position: "", - width: "", - height: "", - pointerEvents: "", - top: "", - left: "", - bottom: "", - right: "" - }; - children.forEach(function(el) { - props.push(assign({ - position: "absolute", - pointerEvents: "none", - width: el.offsetWidth, - height: el.offsetHeight - }, position(el))); - }); - action(); - children.forEach(Transition.cancel); - css(this.$el.children, reset); - UIkit.update(this.$el); - fastdom.flush(); - css(this.$el, "minHeight", height(this.$el)); - var positions = children.map(function(el) { - return position(el); - }); - Promise.all(children.map(function(el, i) { - return Transition.start(css(el, props[i]), positions[i], this$1.animation); - })).then(function() { - css(this$1.$el, "minHeight", ""); - css(children, reset); - UIkit.update(this$1.$el); - fastdom.flush(); - }, noop); - } - } - }); - function getSortable(element) { - return element && (UIkit.getComponent(element, "sortable") || getSortable(element.parentNode)); - } - function isPredecessor(element, target) { - return element.parentNode === target.parentNode && index(element) > index(target); - } - } - function plugin$12(UIkit) { - var obj; - if (plugin$12.installed) { - return; - } - var util = UIkit.util; - var mixin = UIkit.mixin; - var append = util.append; - var attr = util.attr; - var flipPosition = util.flipPosition; - var hasAttr = util.hasAttr; - var includes = util.includes; - var isTouch = util.isTouch; - var isVisible = util.isVisible; - var matches = util.matches; - var on = util.on; - var pointerDown = util.pointerDown; - var pointerEnter = util.pointerEnter; - var pointerLeave = util.pointerLeave; - var remove = util.remove; - var within = util.within; - var actives = []; - UIkit.component("tooltip", { - attrs: true, - args: "title", - mixins: [ mixin.container, mixin.togglable, mixin.position ], - props: { - delay: Number, - title: String - }, - defaults: { - pos: "top", - title: "", - delay: 0, - animation: [ "uk-animation-scale-up" ], - duration: 100, - cls: "uk-active", - clsPos: "uk-tooltip" - }, - beforeConnect: function beforeConnect() { - this._hasTitle = hasAttr(this.$el, "title"); - attr(this.$el, { - title: "", - "aria-expanded": false - }); - }, - disconnected: function disconnected() { - this.hide(); - attr(this.$el, { - title: this._hasTitle ? this.title : null, - "aria-expanded": null - }); - }, - methods: { - show: function show() { - var this$1 = this; - if (includes(actives, this)) { - return; - } - actives.forEach(function(active) { - return active.hide(); - }); - actives.push(this); - this._unbind = on(document, "click", function(e) { - return !within(e.target, this$1.$el) && this$1.hide(); - }); - clearTimeout(this.showTimer); - this.tooltip = append(this.container, '
        ' + this.title + "
        "); - attr(this.$el, "aria-expanded", true); - this.positionAt(this.tooltip, this.$el); - this.origin = this.getAxis() === "y" ? flipPosition(this.dir) + "-" + this.align : this.align + "-" + flipPosition(this.dir); - this.showTimer = setTimeout(function() { - this$1.toggleElement(this$1.tooltip, true); - this$1.hideTimer = setInterval(function() { - if (!isVisible(this$1.$el)) { - this$1.hide(); - } - }, 150); - }, this.delay); - }, - hide: function hide() { - var index = actives.indexOf(this); - if (!~index || matches(this.$el, "input") && this.$el === document.activeElement) { - return; - } - actives.splice(index, 1); - clearTimeout(this.showTimer); - clearInterval(this.hideTimer); - attr(this.$el, "aria-expanded", false); - this.toggleElement(this.tooltip, false); - this.tooltip && remove(this.tooltip); - this.tooltip = false; - this._unbind(); - } - }, - events: (obj = {}, obj["focus " + pointerEnter + " " + pointerDown] = function(e) { - if (e.type !== pointerDown || !isTouch(e)) { - this.show(); - } - }, obj.blur = "hide", obj[pointerLeave] = function(e) { - if (!isTouch(e)) { - this.hide(); - } - }, obj) - }); - } - function plugin$13(UIkit) { - if (plugin$13.installed) { - return; - } - var ref = UIkit.util; - var addClass = ref.addClass; - var ajax = ref.ajax; - var matches = ref.matches; - var noop = ref.noop; - var on = ref.on; - var removeClass = ref.removeClass; - var trigger = ref.trigger; - UIkit.component("upload", { - props: { - allow: String, - clsDragover: String, - concurrent: Number, - maxSize: Number, - method: String, - mime: String, - msgInvalidMime: String, - msgInvalidName: String, - msgInvalidSize: String, - multiple: Boolean, - name: String, - params: Object, - type: String, - url: String - }, - defaults: { - allow: false, - clsDragover: "uk-dragover", - concurrent: 1, - maxSize: 0, - method: "POST", - mime: false, - msgInvalidMime: "Invalid File Type: %s", - msgInvalidName: "Invalid File Name: %s", - msgInvalidSize: "Invalid File Size: %s Bytes Max", - multiple: false, - name: "files[]", - params: {}, - type: "", - url: "", - abort: noop, - beforeAll: noop, - beforeSend: noop, - complete: noop, - completeAll: noop, - error: noop, - fail: noop, - load: noop, - loadEnd: noop, - loadStart: noop, - progress: noop - }, - events: { - change: function change(e) { - if (!matches(e.target, 'input[type="file"]')) { - return; - } - e.preventDefault(); - if (e.target.files) { - this.upload(e.target.files); - } - e.target.value = ""; - }, - drop: function drop(e) { - stop(e); - var transfer = e.dataTransfer; - if (!transfer || !transfer.files) { - return; - } - removeClass(this.$el, this.clsDragover); - this.upload(transfer.files); - }, - dragenter: function dragenter(e) { - stop(e); - }, - dragover: function dragover(e) { - stop(e); - addClass(this.$el, this.clsDragover); - }, - dragleave: function dragleave(e) { - stop(e); - removeClass(this.$el, this.clsDragover); - } - }, - methods: { - upload: function upload(files) { - var this$1 = this; - if (!files.length) { - return; - } - trigger(this.$el, "upload", [ files ]); - for (var i = 0; i < files.length; i++) { - if (this$1.maxSize && this$1.maxSize * 1e3 < files[i].size) { - this$1.fail(this$1.msgInvalidSize.replace("%s", this$1.allow)); - return; - } - if (this$1.allow && !match(this$1.allow, files[i].name)) { - this$1.fail(this$1.msgInvalidName.replace("%s", this$1.allow)); - return; - } - if (this$1.mime && !match(this$1.mime, files[i].type)) { - this$1.fail(this$1.msgInvalidMime.replace("%s", this$1.mime)); - return; - } - } - if (!this.multiple) { - files = [ files[0] ]; - } - this.beforeAll(this, files); - var chunks = chunk(files, this.concurrent); - var upload = function(files) { - var data = new FormData(); - files.forEach(function(file) { - return data.append(this$1.name, file); - }); - for (var key in this$1.params) { - data.append(key, this$1.params[key]); - } - ajax(this$1.url, { - data: data, - method: this$1.method, - responseType: this$1.type, - beforeSend: function(env) { - var xhr = env.xhr; - xhr.upload && on(xhr.upload, "progress", this$1.progress); - [ "loadStart", "load", "loadEnd", "abort" ].forEach(function(type) { - return on(xhr, type.toLowerCase(), this$1[type]); - }); - this$1.beforeSend(env); - } - }).then(function(xhr) { - this$1.complete(xhr); - if (chunks.length) { - upload(chunks.shift()); - } else { - this$1.completeAll(xhr); - } - }, function(e) { - return this$1.error(e.message); - }); - }; - upload(chunks.shift()); - } - } - }); - function match(pattern, path) { - return path.match(new RegExp("^" + pattern.replace(/\//g, "\\/").replace(/\*\*/g, "(\\/[^\\/]+)*").replace(/\*/g, "[^\\/]+").replace(/((?!\\))\?/g, "$1.") + "$", "i")); - } - function chunk(files, size) { - var chunks = []; - for (var i = 0; i < files.length; i += size) { - var chunk = []; - for (var j = 0; j < size; j++) { - chunk.push(files[i + j]); - } - chunks.push(chunk); - } - return chunks; - } - function stop(e) { - e.preventDefault(); - e.stopPropagation(); - } - } - UIkit$2.use(plugin); - UIkit$2.use(plugin$1); - UIkit$2.use(plugin$2); - UIkit$2.use(plugin$6); - UIkit$2.use(plugin$7); - UIkit$2.use(plugin$9); - UIkit$2.use(plugin$10); - UIkit$2.use(plugin$11); - UIkit$2.use(plugin$12); - UIkit$2.use(plugin$13); - { - boot(UIkit$2); - } - return UIkit$2; -}); - -(function(global, factory) { - typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define("uikiticons", factory) : global.UIkitIcons = factory(); -})(this, function() { - "use strict"; - var album = ' '; - var ban = ' '; - var behance = ' '; - var bell = ' '; - var bold = ' '; - var bolt = ' '; - var bookmark = ' '; - var calendar = ' '; - var camera = ' '; - var cart = ' '; - var check = ' '; - var clock = ' '; - var close = ' '; - var code = ' '; - var cog = ' '; - var comment = ' '; - var commenting = ' '; - var comments = ' '; - var copy = ' '; - var database = ' '; - var desktop = ' '; - var download = ' '; - var dribbble = ' '; - var expand = ' '; - var facebook = ' '; - var file = ' '; - var flickr = ' '; - var folder = ' '; - var forward = ' '; - var foursquare = ' '; - var future = ' '; - var github = ' '; - var gitter = ' '; - var google = ' '; - var grid = ' '; - var happy = ' '; - var hashtag = ' '; - var heart = ' '; - var history = ' '; - var home = ' '; - var image = ' '; - var info = ' '; - var instagram = ' '; - var italic = ' '; - var joomla = ' '; - var laptop = ' '; - var lifesaver = ' '; - var link = ' '; - var linkedin = ' '; - var list = ' '; - var location = ' '; - var lock = ' '; - var mail = ' '; - var menu = ' '; - var minus = ' '; - var more = ' '; - var move = ' '; - var nut = ' '; - var pagekit = ' '; - var pencil = ' '; - var phone = ' '; - var pinterest = ' '; - var play = ' '; - var plus = ' '; - var pull = ' '; - var push = ' '; - var question = ' '; - var receiver = ' '; - var refresh = ' '; - var reply = ' '; - var rss = ' '; - var search = ' '; - var server = ' '; - var settings = ' '; - var shrink = ' '; - var social = ' '; - var soundcloud = ' '; - var star = ' '; - var strikethrough = ' '; - var table = ' '; - var tablet = ' '; - var tag = ' '; - var thumbnails = ' '; - var trash = ' '; - var tripadvisor = ' '; - var tumblr = ' '; - var tv = ' '; - var twitter = ' '; - var uikit = ' '; - var unlock = ' '; - var upload = ' '; - var user = ' '; - var users = ' '; - var vimeo = ' '; - var warning = ' '; - var whatsapp = ' '; - var wordpress = ' '; - var world = ' '; - var xing = ' '; - var yelp = ' '; - var youtube = ' '; - var Icons = { - album: album, - ban: ban, - behance: behance, - bell: bell, - bold: bold, - bolt: bolt, - bookmark: bookmark, - calendar: calendar, - camera: camera, - cart: cart, - check: check, - clock: clock, - close: close, - code: code, - cog: cog, - comment: comment, - commenting: commenting, - comments: comments, - copy: copy, - database: database, - desktop: desktop, - download: download, - dribbble: dribbble, - expand: expand, - facebook: facebook, - file: file, - flickr: flickr, - folder: folder, - forward: forward, - foursquare: foursquare, - future: future, - github: github, - gitter: gitter, - google: google, - grid: grid, - happy: happy, - hashtag: hashtag, - heart: heart, - history: history, - home: home, - image: image, - info: info, - instagram: instagram, - italic: italic, - joomla: joomla, - laptop: laptop, - lifesaver: lifesaver, - link: link, - linkedin: linkedin, - list: list, - location: location, - lock: lock, - mail: mail, - menu: menu, - minus: minus, - more: more, - move: move, - nut: nut, - pagekit: pagekit, - pencil: pencil, - phone: phone, - pinterest: pinterest, - play: play, - plus: plus, - pull: pull, - push: push, - question: question, - receiver: receiver, - refresh: refresh, - reply: reply, - rss: rss, - search: search, - server: server, - settings: settings, - shrink: shrink, - social: social, - soundcloud: soundcloud, - star: star, - strikethrough: strikethrough, - table: table, - tablet: tablet, - tag: tag, - thumbnails: thumbnails, - trash: trash, - tripadvisor: tripadvisor, - tumblr: tumblr, - tv: tv, - twitter: twitter, - uikit: uikit, - unlock: unlock, - upload: upload, - user: user, - users: users, - vimeo: vimeo, - warning: warning, - whatsapp: whatsapp, - wordpress: wordpress, - world: world, - xing: xing, - yelp: yelp, - youtube: youtube, - "500px": ' ', - "arrow-down": ' ', - "arrow-left": ' ', - "arrow-right": ' ', - "arrow-up": ' ', - "chevron-down": ' ', - "chevron-left": ' ', - "chevron-right": ' ', - "chevron-up": ' ', - "cloud-download": ' ', - "cloud-upload": ' ', - "credit-card": ' ', - "file-edit": ' ', - "git-branch": ' ', - "git-fork": ' ', - "github-alt": ' ', - "google-plus": ' ', - "minus-circle": ' ', - "more-vertical": ' ', - "paint-bucket": ' ', - "phone-landscape": ' ', - "play-circle": ' ', - "plus-circle": ' ', - "quote-right": ' ', - "sign-in": ' ', - "sign-out": ' ', - "tablet-landscape": ' ', - "triangle-down": ' ', - "triangle-left": ' ', - "triangle-right": ' ', - "triangle-up": ' ', - "video-camera": ' ' - }; - function plugin(UIkit) { - if (plugin.installed) { - return; - } - UIkit.icon.add(Icons); - } - if (typeof window !== "undefined" && window.UIkit) { - window.UIkit.use(plugin); - } - return plugin; -}); - -(function() { - "use strict"; - var _$JSONLoader_2 = { - load: load - }; - function load(location, callback) { - var xhr = getXHR(); - xhr.open("GET", location, true); - xhr.onreadystatechange = createStateChangeListener(xhr, callback); - xhr.send(); - } - function createStateChangeListener(xhr, callback) { - return function() { - if (xhr.readyState === 4 && xhr.status === 200) { - try { - callback(null, JSON.parse(xhr.responseText)); - } catch (err) { - callback(err, null); - } - } - }; - } - function getXHR() { - return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); - } - "use strict"; - var _$OptionsValidator_3 = function OptionsValidator(params) { - if (!validateParams(params)) { - throw new Error("-- OptionsValidator: required options missing"); - } - if (!(this instanceof OptionsValidator)) { - return new OptionsValidator(params); - } - var requiredOptions = params.required; - this.getRequiredOptions = function() { - return requiredOptions; - }; - this.validate = function(parameters) { - var errors = []; - requiredOptions.forEach(function(requiredOptionName) { - if (typeof parameters[requiredOptionName] === "undefined") { - errors.push(requiredOptionName); - } - }); - return errors; - }; - function validateParams(params) { - if (!params) { - return false; - } - return typeof params.required !== "undefined" && params.required instanceof Array; - } - }; - "use strict"; - function fuzzysearch(needle, haystack) { - var tlen = haystack.length; - var qlen = needle.length; - if (qlen > tlen) { - return false; - } - if (qlen === tlen) { - return needle === haystack; - } - outer: for (var i = 0, j = 0; i < qlen; i++) { - var nch = needle.charCodeAt(i); - while (j < tlen) { - if (haystack.charCodeAt(j++) === nch) { - continue outer; - } - } - return false; - } - return true; - } - var _$fuzzysearch_1 = fuzzysearch; - "use strict"; - var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy(); - function FuzzySearchStrategy() { - this.matches = function(string, crit) { - return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase()); - }; - } - "use strict"; - var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy(); - function LiteralSearchStrategy() { - this.matches = function(str, crit) { - if (typeof str !== "string") { - return false; - } - str = str.trim(); - return str.toLowerCase().indexOf(crit.toLowerCase()) >= 0; - }; - } - "use strict"; - var _$Repository_4 = { - put: put, - clear: clear, - search: search, - setOptions: setOptions - }; - function NoSort() { - return 0; - } - var data = []; - var opt = {}; - opt.fuzzy = false; - opt.limit = 10; - opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6; - opt.sort = NoSort; - function put(data) { - if (isObject(data)) { - return addObject(data); - } - if (isArray(data)) { - return addArray(data); - } - return undefined; - } - function clear() { - data.length = 0; - return data; - } - function isObject(obj) { - return Boolean(obj) && Object.prototype.toString.call(obj) === "[object Object]"; - } - function isArray(obj) { - return Boolean(obj) && Object.prototype.toString.call(obj) === "[object Array]"; - } - function addObject(_data) { - data.push(_data); - return data; - } - function addArray(_data) { - var added = []; - clear(); - for (var i = 0, len = _data.length; i < len; i++) { - if (isObject(_data[i])) { - added.push(addObject(_data[i])); - } - } - return added; - } - function search(crit) { - if (!crit) { - return []; - } - return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort); - } - function setOptions(_opt) { - opt = _opt || {}; - opt.fuzzy = _opt.fuzzy || false; - opt.limit = _opt.limit || 10; - opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6; - opt.sort = _opt.sort || NoSort; - } - function findMatches(data, crit, strategy, opt) { - var matches = []; - for (var i = 0; i < data.length && matches.length < opt.limit; i++) { - var match = findMatchesInObject(data[i], crit, strategy, opt); - if (match) { - matches.push(match); - } - } - return matches; - } - function findMatchesInObject(obj, crit, strategy, opt) { - for (var key in obj) { - if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) { - return obj; - } - } - } - function isExcluded(term, excludedTerms) { - var excluded = false; - excludedTerms = excludedTerms || []; - for (var i = 0, len = excludedTerms.length; i < len; i++) { - var excludedTerm = excludedTerms[i]; - if (!excluded && new RegExp(term).test(excludedTerm)) { - excluded = true; - } - } - return excluded; - } - "use strict"; - var _$Templater_7 = { - compile: compile, - setOptions: __setOptions_7 - }; - var options = {}; - options.pattern = /\{(.*?)\}/g; - options.template = ""; - options.middleware = function() {}; - function __setOptions_7(_options) { - options.pattern = _options.pattern || options.pattern; - options.template = _options.template || options.template; - if (typeof _options.middleware === "function") { - options.middleware = _options.middleware; - } - } - function compile(data) { - return options.template.replace(options.pattern, function(match, prop) { - var value = options.middleware(prop, data[prop], options.template); - if (typeof value !== "undefined") { - return value; - } - return data[prop] || match; - }); - } - "use strict"; - var _$utils_9 = { - merge: merge, - isJSON: isJSON - }; - function merge(defaultParams, mergeParams) { - var mergedOptions = {}; - for (var option in defaultParams) { - if (Object.prototype.hasOwnProperty.call(defaultParams, option)) { - mergedOptions[option] = defaultParams[option]; - if (typeof mergeParams[option] !== "undefined") { - mergedOptions[option] = mergeParams[option]; - } - } - } - return mergedOptions; - } - function isJSON(json) { - try { - if (json instanceof Object && JSON.parse(JSON.stringify(json))) { - return true; - } - return false; - } catch (err) { - return false; - } - } - var _$src_8 = {}; - (function(window) { - "use strict"; - var options = { - searchInput: null, - resultsContainer: null, - json: [], - searchResultTemplate: '
      • {title}
      • ', - templateMiddleware: function() {}, - sortMiddleware: function() { - return 0; - }, - noResultsText: "No results found", - limit: 10, - fuzzy: false, - exclude: [] - }; - var requiredOptions = [ "searchInput", "resultsContainer", "json" ]; - var optionsValidator = _$OptionsValidator_3({ - required: requiredOptions - }); - window.SimpleJekyllSearch = function(_options) { - var errors = optionsValidator.validate(_options); - if (errors.length > 0) { - throwError("You must specify the following required options: " + requiredOptions); - } - options = _$utils_9.merge(options, _options); - _$Templater_7.setOptions({ - template: options.searchResultTemplate, - middleware: options.templateMiddleware - }); - _$Repository_4.setOptions({ - fuzzy: options.fuzzy, - limit: options.limit, - sort: options.sortMiddleware - }); - if (_$utils_9.isJSON(options.json)) { - initWithJSON(options.json); - } else { - initWithURL(options.json); - } - return { - search: search - }; - }; - window.SimpleJekyllSearch.init = window.SimpleJekyllSearch; - if (typeof window.SimpleJekyllSearchInit === "function") { - window.SimpleJekyllSearchInit.call(this, window.SimpleJekyllSearch); - } - function initWithJSON(json) { - _$Repository_4.put(json); - registerInput(); - } - function initWithURL(url) { - _$JSONLoader_2.load(url, function(err, json) { - if (err) { - throwError("failed to get JSON (" + url + ")"); - } - initWithJSON(json); - }); - } - function emptyResultsContainer() { - options.resultsContainer.innerHTML = ""; - } - function appendToResultsContainer(text) { - options.resultsContainer.innerHTML += text; - } - function registerInput() { - options.searchInput.addEventListener("keyup", function(e) { - if (isWhitelistedKey(e.which)) { - emptyResultsContainer(); - search(e.target.value); - } - }); - } - function search(query) { - if (isValidQuery(query)) { - emptyResultsContainer(); - render(_$Repository_4.search(query)); - } - } - function render(results) { - var len = results.length; - if (len === 0) { - return appendToResultsContainer(options.noResultsText); - } - for (var i = 0; i < len; i++) { - appendToResultsContainer(_$Templater_7.compile(results[i])); - } - } - function isValidQuery(query) { - return query && query.length > 0; - } - function isWhitelistedKey(key) { - return [ 13, 16, 20, 37, 38, 39, 40, 91 ].indexOf(key) === -1; - } - function throwError(message) { - throw new Error("SimpleJekyllSearch --- " + message); - } - })(window); -})(); \ No newline at end of file diff --git a/assets/posts/7C04AAA0EE9E3886.png b/assets/posts/7C04AAA0EE9E3886.png deleted file mode 100644 index 8af764e0b5..0000000000 Binary files a/assets/posts/7C04AAA0EE9E3886.png and /dev/null differ diff --git a/assets/posts/SVG/logo.svg b/assets/posts/SVG/logo.svg deleted file mode 100644 index 886b00f891..0000000000 --- a/assets/posts/SVG/logo.svg +++ /dev/null @@ -1 +0,0 @@ -logo \ No newline at end of file diff --git a/assets/posts/books.svg b/assets/posts/books.svg deleted file mode 100644 index 6e5638ac37..0000000000 --- a/assets/posts/books.svg +++ /dev/null @@ -1,51 +0,0 @@ - - - - Books - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/posts/box.svg b/assets/posts/box.svg deleted file mode 100644 index 625e607a25..0000000000 --- a/assets/posts/box.svg +++ /dev/null @@ -1,100 +0,0 @@ - - - - Box - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/posts/city.svg b/assets/posts/city.svg deleted file mode 100644 index 337229f80d..0000000000 --- a/assets/posts/city.svg +++ /dev/null @@ -1,188 +0,0 @@ - - - - Style 9 - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/posts/imac.svg b/assets/posts/imac.svg deleted file mode 100644 index 881398574d..0000000000 --- a/assets/posts/imac.svg +++ /dev/null @@ -1,47 +0,0 @@ - - - - iMac - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/posts/image1.png b/assets/posts/image1.png deleted file mode 100644 index a7125c3140..0000000000 Binary files a/assets/posts/image1.png and /dev/null differ diff --git a/assets/posts/logo.png b/assets/posts/logo.png deleted file mode 100644 index 4b1793c8b5..0000000000 Binary files a/assets/posts/logo.png and /dev/null differ diff --git a/assets/posts/logo.svg b/assets/posts/logo.svg deleted file mode 100644 index 4cf1acadf5..0000000000 --- a/assets/posts/logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - -logo - - - diff --git a/assets/posts/old.logo.svg b/assets/posts/old.logo.svg deleted file mode 100755 index c12f862550..0000000000 --- a/assets/posts/old.logo.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/assets/posts/safe.svg b/assets/posts/safe.svg deleted file mode 100644 index 4ece36002d..0000000000 --- a/assets/posts/safe.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - Safe - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/posts/sky.svg b/assets/posts/sky.svg deleted file mode 100644 index 3913866cb0..0000000000 --- a/assets/posts/sky.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - Style 1 - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/posts/slideshow-1/image1.png b/assets/posts/slideshow-1/image1.png deleted file mode 100644 index a7125c3140..0000000000 Binary files a/assets/posts/slideshow-1/image1.png and /dev/null differ diff --git a/assets/posts/teacup.svg b/assets/posts/teacup.svg deleted file mode 100644 index 729cf60ed3..0000000000 --- a/assets/posts/teacup.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - Teacup - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/posts/touch-icon.png b/assets/posts/touch-icon.png deleted file mode 100644 index 243615bb9f..0000000000 Binary files a/assets/posts/touch-icon.png and /dev/null differ diff --git a/assets/posts/touch-icon.svg b/assets/posts/touch-icon.svg deleted file mode 100644 index cc1205e398..0000000000 --- a/assets/posts/touch-icon.svg +++ /dev/null @@ -1 +0,0 @@ -Asset 1 \ No newline at end of file diff --git a/changelog.md b/changelog.md deleted file mode 100644 index 8bac050030..0000000000 --- a/changelog.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: changelog -title: Changelog -permalink: /changelog/ ---- - -Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. diff --git a/collections.json b/collections.json deleted file mode 100644 index 594a064876..0000000000 --- a/collections.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": "THEVERSION", - "collections": [ - { - "name": "browser", - "repo": "https://github.com/blockstack/blockstack-browser" - }, - { - "name": "core", - "repo": "https://github.com/blockstack/blockstack-core" - } - ] -} diff --git a/contact.md b/contact.md deleted file mode 100644 index 00c0d0f968..0000000000 --- a/contact.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -layout: contact -title: Got Any Questions -permalink: /contact/ -formspree: - email: my_name@gmail.com - redirect: /thanks/ ---- - -##### Morbi varius in accumsan blandit, elit ligula velit, luctus mattis ante nulla nulla. - -Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. - -{% include map.html latitude="40.6700" longitude="-73.9400" zoom="16" %} diff --git a/contribute.md b/contribute.md new file mode 100644 index 0000000000..b2f6db1b02 --- /dev/null +++ b/contribute.md @@ -0,0 +1,18 @@ +# Contribute + +### Contribute to Stacks Core + +There is a [detailed contribution guide](https://github.com/stacks-network/stacks-core/blob/master/CONTRIBUTING.md) that lives in the Stacks core GitHub repository that is the best place to get started contributing to Stacks. + +### Contribute to these Docs + +The Stacks docs are built using GitBook with a two-way sync with the [docs repository on GitHub](https://github.com/stacks-network/docs). + +Because of this two-way sync, you can contribute to the documentation in one of two ways: + +1. You can fork the docs repo, add your change, and then create a PR to be merged into the main docs +2. You can create an issue, and someone that works on the docs will take a look and implement it if it is a necessary change + +What kinds of changes are we looking for? + +If you see a typo, a missing tutorial, an unclear explanation, or really anything else you think could improve the quality of the documentation, please feel free to open an issue or create a pull request. diff --git a/docs/build/.gitbook.yaml b/docs/build/.gitbook.yaml new file mode 100644 index 0000000000..253e89b1e5 --- /dev/null +++ b/docs/build/.gitbook.yaml @@ -0,0 +1,36 @@ +root: ./ + +redirects: + guides-and-tutorials/hello-stacks-quickstart-tutorial: README.md + guides-and-tutorials/clarity-crash-course: clarity-crash-course.md + + # bitcoin-integration redirects + guides-and-tutorials/bitcoin-integration/sending-bitcoin-with-leather-wallet: bitcoin-integration/sending-bitcoin-with-leather.md + guides-and-tutorials/bitcoin-integration/parsing-a-bitcoin-transaction: bitcoin-integration/parsing-a-bitcoin-transaction.md + guides-and-tutorials/bitcoin-integration/verifying-a-bitcoin-transaction: bitcoin-integration/verifying-a-bitcoin-transaction.md + + # create-tokens redirects + guides-and-tutorials/tokens/creating-a-nft: create-tokens/creating-a-nft.md + guides-and-tutorials/tokens/creating-a-fungible-token: create-tokens/creating-a-ft.md + + # build-a-frontend redirects + guides-and-tutorials/frontend/authentication-with-stacks.js: build-a-frontend/authentication-with-stacks.js.md + guides-and-tutorials/frontend/post-conditions-with-stacks.js: build-a-frontend/post-conditions-with-stacks.js.md + guides-and-tutorials/frontend/sending-transactions-with-stacks.js: build-a-frontend/sending-transactions-with-stacks.js.md + + # testing-smart-contracts redirects + guides-and-tutorials/testing-smart-contracts/fuzz-testing: testing-smart-contracts/fuzz-testing.md + + # sbtc redirects + guides-and-tutorials/sbtc/best-practices-for-running-an-sbtc-signer: sbtc/best-practices-for-running-an-sbtc-signer.md + guides-and-tutorials/sbtc/earn-sbtc-rewards: sbtc/how-to-earn-sbtc-rewards.md + guides-and-tutorials/sbtc/how-to-run-sbtc-signer: sbtc/how-to-run-sbtc-signer.md + guides-and-tutorials/sbtc/how-to-use-the-sbtc-bridge: sbtc/how-to-use-the-sbtc-bridge.md + guides-and-tutorials/sbtc/sbtc-builder-quickstart: sbtc/sbtc-builder-quickstart.md + + # oracles redirects + guides-and-tutorials/oracles: price-oracles.md + + # misc.-guides redirects + guides-and-tutorials/build-a-borrowing-and-lending-protocol: misc.-guides/build-a-borrowing-and-lending-protocol.md + guides-and-tutorials/community-tutorials: misc.-guides/community-tutorials.md diff --git a/docs/build/.gitbook/assets/Frame 316125324 (1).jpg b/docs/build/.gitbook/assets/Frame 316125324 (1).jpg new file mode 100644 index 0000000000..8964e1069c Binary files /dev/null and b/docs/build/.gitbook/assets/Frame 316125324 (1).jpg differ diff --git a/docs/build/.gitbook/assets/Frame 316125324.jpg b/docs/build/.gitbook/assets/Frame 316125324.jpg new file mode 100644 index 0000000000..8964e1069c Binary files /dev/null and b/docs/build/.gitbook/assets/Frame 316125324.jpg differ diff --git a/docs/build/.gitbook/assets/Frame 316126251.jpg b/docs/build/.gitbook/assets/Frame 316126251.jpg new file mode 100644 index 0000000000..ecaaf0c522 Binary files /dev/null and b/docs/build/.gitbook/assets/Frame 316126251.jpg differ diff --git a/docs/build/.gitbook/assets/GcHiZWjXgAA21Kf.jpeg b/docs/build/.gitbook/assets/GcHiZWjXgAA21Kf.jpeg new file mode 100644 index 0000000000..0209b9426a Binary files /dev/null and b/docs/build/.gitbook/assets/GcHiZWjXgAA21Kf.jpeg differ diff --git a/docs/build/.gitbook/assets/Group 316124778 (1)-with-fordefi.png b/docs/build/.gitbook/assets/Group 316124778 (1)-with-fordefi.png new file mode 100644 index 0000000000..5bfe3c487a Binary files /dev/null and b/docs/build/.gitbook/assets/Group 316124778 (1)-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/Group 316124778 (2)-with-fordefi.png b/docs/build/.gitbook/assets/Group 316124778 (2)-with-fordefi.png new file mode 100644 index 0000000000..9b806f1b58 Binary files /dev/null and b/docs/build/.gitbook/assets/Group 316124778 (2)-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/Group 316124778 (3)-with-fordefi.png b/docs/build/.gitbook/assets/Group 316124778 (3)-with-fordefi.png new file mode 100644 index 0000000000..3319d3c070 Binary files /dev/null and b/docs/build/.gitbook/assets/Group 316124778 (3)-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/Group 316124778 (4)-with-fordefi.png b/docs/build/.gitbook/assets/Group 316124778 (4)-with-fordefi.png new file mode 100644 index 0000000000..86eb052ce8 Binary files /dev/null and b/docs/build/.gitbook/assets/Group 316124778 (4)-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/Group 316124779-with-asigna.png b/docs/build/.gitbook/assets/Group 316124779-with-asigna.png new file mode 100644 index 0000000000..72f2b66bcc Binary files /dev/null and b/docs/build/.gitbook/assets/Group 316124779-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/Group 316124780 (1)-with-asigna.png b/docs/build/.gitbook/assets/Group 316124780 (1)-with-asigna.png new file mode 100644 index 0000000000..660e948434 Binary files /dev/null and b/docs/build/.gitbook/assets/Group 316124780 (1)-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/Group 316124780-with-asigna.png b/docs/build/.gitbook/assets/Group 316124780-with-asigna.png new file mode 100644 index 0000000000..d06a74bc72 Binary files /dev/null and b/docs/build/.gitbook/assets/Group 316124780-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/authentication.jpg b/docs/build/.gitbook/assets/authentication.jpg new file mode 100644 index 0000000000..f3dd58eb80 Binary files /dev/null and b/docs/build/.gitbook/assets/authentication.jpg differ diff --git a/docs/build/.gitbook/assets/bitcoin-clarity.png b/docs/build/.gitbook/assets/bitcoin-clarity.png new file mode 100644 index 0000000000..3f5455fa3d Binary files /dev/null and b/docs/build/.gitbook/assets/bitcoin-clarity.png differ diff --git a/docs/build/.gitbook/assets/build-a-frontend.jpg b/docs/build/.gitbook/assets/build-a-frontend.jpg new file mode 100644 index 0000000000..9512ef0c68 Binary files /dev/null and b/docs/build/.gitbook/assets/build-a-frontend.jpg differ diff --git a/docs/build/.gitbook/assets/build-intro.jpg b/docs/build/.gitbook/assets/build-intro.jpg new file mode 100644 index 0000000000..052e7f91c7 Binary files /dev/null and b/docs/build/.gitbook/assets/build-intro.jpg differ diff --git a/docs/build/.gitbook/assets/build-intro.png b/docs/build/.gitbook/assets/build-intro.png new file mode 100644 index 0000000000..3ca2812082 Binary files /dev/null and b/docs/build/.gitbook/assets/build-intro.png differ diff --git a/docs/build/.gitbook/assets/clarinet-diagram.png b/docs/build/.gitbook/assets/clarinet-diagram.png new file mode 100644 index 0000000000..3ce68c0c16 Binary files /dev/null and b/docs/build/.gitbook/assets/clarinet-diagram.png differ diff --git a/docs/build/.gitbook/assets/clarinet-js-sdk.png b/docs/build/.gitbook/assets/clarinet-js-sdk.png new file mode 100644 index 0000000000..59518cd545 Binary files /dev/null and b/docs/build/.gitbook/assets/clarinet-js-sdk.png differ diff --git a/docs/build/.gitbook/assets/clarinet-sdk.png b/docs/build/.gitbook/assets/clarinet-sdk.png new file mode 100644 index 0000000000..cbae820d7a Binary files /dev/null and b/docs/build/.gitbook/assets/clarinet-sdk.png differ diff --git a/docs/build/.gitbook/assets/clarity-extension.png b/docs/build/.gitbook/assets/clarity-extension.png new file mode 100644 index 0000000000..905e73353b Binary files /dev/null and b/docs/build/.gitbook/assets/clarity-extension.png differ diff --git a/docs/build/.gitbook/assets/clarity-playground-console.png b/docs/build/.gitbook/assets/clarity-playground-console.png new file mode 100644 index 0000000000..835c150a8c Binary files /dev/null and b/docs/build/.gitbook/assets/clarity-playground-console.png differ diff --git a/docs/build/.gitbook/assets/clarity-playground.png b/docs/build/.gitbook/assets/clarity-playground.png new file mode 100644 index 0000000000..779f958242 Binary files /dev/null and b/docs/build/.gitbook/assets/clarity-playground.png differ diff --git a/docs/build/.gitbook/assets/clarity.jpg b/docs/build/.gitbook/assets/clarity.jpg new file mode 100644 index 0000000000..4ab9a4fd97 Binary files /dev/null and b/docs/build/.gitbook/assets/clarity.jpg differ diff --git a/docs/build/.gitbook/assets/custom-taproot-deposit-address.jpeg b/docs/build/.gitbook/assets/custom-taproot-deposit-address.jpeg new file mode 100644 index 0000000000..0b67844174 Binary files /dev/null and b/docs/build/.gitbook/assets/custom-taproot-deposit-address.jpeg differ diff --git a/docs/build/.gitbook/assets/custom-wallet-connect-modal.png b/docs/build/.gitbook/assets/custom-wallet-connect-modal.png new file mode 100644 index 0000000000..0d3940e245 Binary files /dev/null and b/docs/build/.gitbook/assets/custom-wallet-connect-modal.png differ diff --git a/docs/build/.gitbook/assets/deploy-contract.jpg b/docs/build/.gitbook/assets/deploy-contract.jpg new file mode 100644 index 0000000000..8964e1069c Binary files /dev/null and b/docs/build/.gitbook/assets/deploy-contract.jpg differ diff --git a/docs/build/.gitbook/assets/devtools-ecosystem.png b/docs/build/.gitbook/assets/devtools-ecosystem.png new file mode 100644 index 0000000000..007cdafd37 Binary files /dev/null and b/docs/build/.gitbook/assets/devtools-ecosystem.png differ diff --git a/docs/build/.gitbook/assets/enable-sbtc-view.png b/docs/build/.gitbook/assets/enable-sbtc-view.png new file mode 100644 index 0000000000..1ef8ebf4e7 Binary files /dev/null and b/docs/build/.gitbook/assets/enable-sbtc-view.png differ diff --git a/docs/build/.gitbook/assets/example-post-condition-leather.png b/docs/build/.gitbook/assets/example-post-condition-leather.png new file mode 100644 index 0000000000..19e2f09f22 Binary files /dev/null and b/docs/build/.gitbook/assets/example-post-condition-leather.png differ diff --git a/docs/build/.gitbook/assets/image (1).png b/docs/build/.gitbook/assets/image (1).png new file mode 100644 index 0000000000..90ba3f571b Binary files /dev/null and b/docs/build/.gitbook/assets/image (1).png differ diff --git a/docs/build/.gitbook/assets/image (10)-sbtc-bridge.png b/docs/build/.gitbook/assets/image (10)-sbtc-bridge.png new file mode 100644 index 0000000000..8177a76839 Binary files /dev/null and b/docs/build/.gitbook/assets/image (10)-sbtc-bridge.png differ diff --git a/docs/build/.gitbook/assets/image (11)-sbtc-bridge.png b/docs/build/.gitbook/assets/image (11)-sbtc-bridge.png new file mode 100644 index 0000000000..ac78447329 Binary files /dev/null and b/docs/build/.gitbook/assets/image (11)-sbtc-bridge.png differ diff --git a/docs/build/.gitbook/assets/image (12)-sbtc-bridge.png b/docs/build/.gitbook/assets/image (12)-sbtc-bridge.png new file mode 100644 index 0000000000..9210b58c1a Binary files /dev/null and b/docs/build/.gitbook/assets/image (12)-sbtc-bridge.png differ diff --git a/docs/build/.gitbook/assets/image (13)-sbtc-bridge.png b/docs/build/.gitbook/assets/image (13)-sbtc-bridge.png new file mode 100644 index 0000000000..715ec833fe Binary files /dev/null and b/docs/build/.gitbook/assets/image (13)-sbtc-bridge.png differ diff --git a/docs/build/.gitbook/assets/image (14)-sbtc-bridge.png b/docs/build/.gitbook/assets/image (14)-sbtc-bridge.png new file mode 100644 index 0000000000..514951c8b0 Binary files /dev/null and b/docs/build/.gitbook/assets/image (14)-sbtc-bridge.png differ diff --git a/docs/build/.gitbook/assets/image (2).png b/docs/build/.gitbook/assets/image (2).png new file mode 100644 index 0000000000..1c637648c3 Binary files /dev/null and b/docs/build/.gitbook/assets/image (2).png differ diff --git a/docs/build/.gitbook/assets/image (4).png b/docs/build/.gitbook/assets/image (4).png new file mode 100644 index 0000000000..445dd9848d Binary files /dev/null and b/docs/build/.gitbook/assets/image (4).png differ diff --git a/docs/build/.gitbook/assets/image (5).png b/docs/build/.gitbook/assets/image (5).png new file mode 100644 index 0000000000..ce90bce016 Binary files /dev/null and b/docs/build/.gitbook/assets/image (5).png differ diff --git a/docs/build/.gitbook/assets/image (6).png b/docs/build/.gitbook/assets/image (6).png new file mode 100644 index 0000000000..850b421678 Binary files /dev/null and b/docs/build/.gitbook/assets/image (6).png differ diff --git a/docs/build/.gitbook/assets/image (7).png b/docs/build/.gitbook/assets/image (7).png new file mode 100644 index 0000000000..35bf7c4f53 Binary files /dev/null and b/docs/build/.gitbook/assets/image (7).png differ diff --git a/docs/build/.gitbook/assets/image (8).png b/docs/build/.gitbook/assets/image (8).png new file mode 100644 index 0000000000..c866b0dc8e Binary files /dev/null and b/docs/build/.gitbook/assets/image (8).png differ diff --git a/docs/build/.gitbook/assets/image (9)-sbtc-bridge.png b/docs/build/.gitbook/assets/image (9)-sbtc-bridge.png new file mode 100644 index 0000000000..8f6083d420 Binary files /dev/null and b/docs/build/.gitbook/assets/image (9)-sbtc-bridge.png differ diff --git a/docs/build/.gitbook/assets/image 11-with-fordefi.png b/docs/build/.gitbook/assets/image 11-with-fordefi.png new file mode 100644 index 0000000000..01595e7d39 Binary files /dev/null and b/docs/build/.gitbook/assets/image 11-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/image 16-with-fordefi.png b/docs/build/.gitbook/assets/image 16-with-fordefi.png new file mode 100644 index 0000000000..099e6b71c7 Binary files /dev/null and b/docs/build/.gitbook/assets/image 16-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/image 2-with-fordefi.png b/docs/build/.gitbook/assets/image 2-with-fordefi.png new file mode 100644 index 0000000000..b9cb30d7a1 Binary files /dev/null and b/docs/build/.gitbook/assets/image 2-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/image 22-with-fordefi.png b/docs/build/.gitbook/assets/image 22-with-fordefi.png new file mode 100644 index 0000000000..ff2615e114 Binary files /dev/null and b/docs/build/.gitbook/assets/image 22-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/image 3-with-fordefi.png b/docs/build/.gitbook/assets/image 3-with-fordefi.png new file mode 100644 index 0000000000..6d0423b88b Binary files /dev/null and b/docs/build/.gitbook/assets/image 3-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/image 33 (1)-with-asigna.png b/docs/build/.gitbook/assets/image 33 (1)-with-asigna.png new file mode 100644 index 0000000000..c61a905f63 Binary files /dev/null and b/docs/build/.gitbook/assets/image 33 (1)-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/image 35-with-asigna.png b/docs/build/.gitbook/assets/image 35-with-asigna.png new file mode 100644 index 0000000000..e37f5211d0 Binary files /dev/null and b/docs/build/.gitbook/assets/image 35-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/image 36-with-asigna.png b/docs/build/.gitbook/assets/image 36-with-asigna.png new file mode 100644 index 0000000000..45a57ac76a Binary files /dev/null and b/docs/build/.gitbook/assets/image 36-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/image 37-with-asigna.png b/docs/build/.gitbook/assets/image 37-with-asigna.png new file mode 100644 index 0000000000..748ca88995 Binary files /dev/null and b/docs/build/.gitbook/assets/image 37-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/image 38-with-asigna.png b/docs/build/.gitbook/assets/image 38-with-asigna.png new file mode 100644 index 0000000000..b676da7c7e Binary files /dev/null and b/docs/build/.gitbook/assets/image 38-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/image 4-with-fordefi.png b/docs/build/.gitbook/assets/image 4-with-fordefi.png new file mode 100644 index 0000000000..3842a7c913 Binary files /dev/null and b/docs/build/.gitbook/assets/image 4-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/image 40.png b/docs/build/.gitbook/assets/image 40.png new file mode 100644 index 0000000000..a6ff2ea3fd Binary files /dev/null and b/docs/build/.gitbook/assets/image 40.png differ diff --git a/docs/build/.gitbook/assets/image 42 (1)-with-asigna.png b/docs/build/.gitbook/assets/image 42 (1)-with-asigna.png new file mode 100644 index 0000000000..16b82a5480 Binary files /dev/null and b/docs/build/.gitbook/assets/image 42 (1)-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/image 43 (1)-with-asigna.png b/docs/build/.gitbook/assets/image 43 (1)-with-asigna.png new file mode 100644 index 0000000000..8e5cfbc05e Binary files /dev/null and b/docs/build/.gitbook/assets/image 43 (1)-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/image 44-with-asigna.png b/docs/build/.gitbook/assets/image 44-with-asigna.png new file mode 100644 index 0000000000..e09c9131d4 Binary files /dev/null and b/docs/build/.gitbook/assets/image 44-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/image 45 (1)-with-asigna.png b/docs/build/.gitbook/assets/image 45 (1)-with-asigna.png new file mode 100644 index 0000000000..30ad78dfbe Binary files /dev/null and b/docs/build/.gitbook/assets/image 45 (1)-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/image 46-with-asigna.png b/docs/build/.gitbook/assets/image 46-with-asigna.png new file mode 100644 index 0000000000..c78859d24f Binary files /dev/null and b/docs/build/.gitbook/assets/image 46-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/image 47-with-asigna.png b/docs/build/.gitbook/assets/image 47-with-asigna.png new file mode 100644 index 0000000000..186243f9cf Binary files /dev/null and b/docs/build/.gitbook/assets/image 47-with-asigna.png differ diff --git a/docs/build/.gitbook/assets/image 5-with-fordefi.png b/docs/build/.gitbook/assets/image 5-with-fordefi.png new file mode 100644 index 0000000000..6edd5519d7 Binary files /dev/null and b/docs/build/.gitbook/assets/image 5-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/image 6-with-fordefi.png b/docs/build/.gitbook/assets/image 6-with-fordefi.png new file mode 100644 index 0000000000..55f9302b83 Binary files /dev/null and b/docs/build/.gitbook/assets/image 6-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/image 7-with-fordefi.png b/docs/build/.gitbook/assets/image 7-with-fordefi.png new file mode 100644 index 0000000000..26d2b2a603 Binary files /dev/null and b/docs/build/.gitbook/assets/image 7-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/image 8-with-fordefi.png b/docs/build/.gitbook/assets/image 8-with-fordefi.png new file mode 100644 index 0000000000..1d60b4bce2 Binary files /dev/null and b/docs/build/.gitbook/assets/image 8-with-fordefi.png differ diff --git a/docs/build/.gitbook/assets/image.png b/docs/build/.gitbook/assets/image.png new file mode 100644 index 0000000000..cc6def5437 Binary files /dev/null and b/docs/build/.gitbook/assets/image.png differ diff --git a/docs/build/.gitbook/assets/onboarding.png b/docs/build/.gitbook/assets/onboarding.png new file mode 100644 index 0000000000..9d194182af Binary files /dev/null and b/docs/build/.gitbook/assets/onboarding.png differ diff --git a/docs/build/.gitbook/assets/post-condition-modes.png b/docs/build/.gitbook/assets/post-condition-modes.png new file mode 100644 index 0000000000..9fffadeaba Binary files /dev/null and b/docs/build/.gitbook/assets/post-condition-modes.png differ diff --git a/docs/build/.gitbook/assets/post-condition-stack.png b/docs/build/.gitbook/assets/post-condition-stack.png new file mode 100644 index 0000000000..e7ef91906a Binary files /dev/null and b/docs/build/.gitbook/assets/post-condition-stack.png differ diff --git a/docs/build/.gitbook/assets/post-conditions-explorer.png b/docs/build/.gitbook/assets/post-conditions-explorer.png new file mode 100644 index 0000000000..fe32989f86 Binary files /dev/null and b/docs/build/.gitbook/assets/post-conditions-explorer.png differ diff --git a/docs/build/.gitbook/assets/post-conditions.jpg b/docs/build/.gitbook/assets/post-conditions.jpg new file mode 100644 index 0000000000..5cab5989fe Binary files /dev/null and b/docs/build/.gitbook/assets/post-conditions.jpg differ diff --git a/docs/build/.gitbook/assets/pyth-pull-flow-diagram.png b/docs/build/.gitbook/assets/pyth-pull-flow-diagram.png new file mode 100644 index 0000000000..49b2b3fac8 Binary files /dev/null and b/docs/build/.gitbook/assets/pyth-pull-flow-diagram.png differ diff --git a/docs/build/.gitbook/assets/sbtc-clarinet-testing.jpg b/docs/build/.gitbook/assets/sbtc-clarinet-testing.jpg new file mode 100644 index 0000000000..02201cb8ef Binary files /dev/null and b/docs/build/.gitbook/assets/sbtc-clarinet-testing.jpg differ diff --git a/docs/build/.gitbook/assets/sbtc-faucet.png b/docs/build/.gitbook/assets/sbtc-faucet.png new file mode 100644 index 0000000000..00b9cf6339 Binary files /dev/null and b/docs/build/.gitbook/assets/sbtc-faucet.png differ diff --git a/docs/build/.gitbook/assets/sbtc-integrations.png b/docs/build/.gitbook/assets/sbtc-integrations.png new file mode 100644 index 0000000000..c7fd31dc6a Binary files /dev/null and b/docs/build/.gitbook/assets/sbtc-integrations.png differ diff --git a/docs/build/.gitbook/assets/send-transactions.jpg b/docs/build/.gitbook/assets/send-transactions.jpg new file mode 100644 index 0000000000..b4a30bd236 Binary files /dev/null and b/docs/build/.gitbook/assets/send-transactions.jpg differ diff --git a/docs/build/.gitbook/assets/stacks-bitcoin-address.png b/docs/build/.gitbook/assets/stacks-bitcoin-address.png new file mode 100644 index 0000000000..421d595fd8 Binary files /dev/null and b/docs/build/.gitbook/assets/stacks-bitcoin-address.png differ diff --git a/docs/build/.gitbook/assets/stacks-connect-modal.png b/docs/build/.gitbook/assets/stacks-connect-modal.png new file mode 100644 index 0000000000..78474940ad Binary files /dev/null and b/docs/build/.gitbook/assets/stacks-connect-modal.png differ diff --git a/docs/build/.gitbook/assets/stacks-connect.jpg b/docs/build/.gitbook/assets/stacks-connect.jpg new file mode 100644 index 0000000000..521c9e1b18 Binary files /dev/null and b/docs/build/.gitbook/assets/stacks-connect.jpg differ diff --git a/docs/build/.gitbook/assets/stacks-devs-twitter-header.png b/docs/build/.gitbook/assets/stacks-devs-twitter-header.png new file mode 100644 index 0000000000..5198600901 Binary files /dev/null and b/docs/build/.gitbook/assets/stacks-devs-twitter-header.png differ diff --git a/docs/build/.gitbook/assets/stacksjs-learn.png b/docs/build/.gitbook/assets/stacksjs-learn.png new file mode 100644 index 0000000000..62cf4ac9cd Binary files /dev/null and b/docs/build/.gitbook/assets/stacksjs-learn.png differ diff --git a/docs/build/.gitbook/assets/tx-confirmation-popup.png b/docs/build/.gitbook/assets/tx-confirmation-popup.png new file mode 100644 index 0000000000..e934e83ff3 Binary files /dev/null and b/docs/build/.gitbook/assets/tx-confirmation-popup.png differ diff --git a/docs/build/.gitbook/assets/use-cases.png b/docs/build/.gitbook/assets/use-cases.png new file mode 100644 index 0000000000..b28c194aab Binary files /dev/null and b/docs/build/.gitbook/assets/use-cases.png differ diff --git a/docs/build/.gitbook/assets/wallet-communication-flow.png b/docs/build/.gitbook/assets/wallet-communication-flow.png new file mode 100644 index 0000000000..1ba115e3b0 Binary files /dev/null and b/docs/build/.gitbook/assets/wallet-communication-flow.png differ diff --git a/docs/build/.gitbook/assets/wallet-extension-template.png b/docs/build/.gitbook/assets/wallet-extension-template.png new file mode 100644 index 0000000000..af0d603117 Binary files /dev/null and b/docs/build/.gitbook/assets/wallet-extension-template.png differ diff --git a/docs/build/.gitbook/assets/wallet-implementation.png b/docs/build/.gitbook/assets/wallet-implementation.png new file mode 100644 index 0000000000..5b1ad2a54a Binary files /dev/null and b/docs/build/.gitbook/assets/wallet-implementation.png differ diff --git a/docs/build/README.md b/docs/build/README.md new file mode 100644 index 0000000000..f39c8c8bd6 --- /dev/null +++ b/docs/build/README.md @@ -0,0 +1,63 @@ +--- +description: Build powerful apps, secured by Bitcoin. +--- + +# Introduction + +
        + +
        + +New to building with Stacks? Check out these weekly virtual meets! + +* **Stacks DevRel office hours**: Follow and enable notifications for [@StacksDevs](https://x.com/StacksDevs) on Twitter to catch alerts for our weekly livestreams every Thursday at 10am EST. Office Hours are the easiest way to stay in the loop on product drops, live demos, community builder spotlights, and more. Stay up-to-date with release discussions, real-time walkthroughs, and builder highlights that matter. +* **Clarity Working Group**: An open, developer-focused initiative dedicated to supporting builders across the Stacks ecosystem. The group brings together experienced Clarity engineers (“Clarity giga chads”), auditors, educators, grant project teams, and new developers to collaborate, learn, and advance smart contract development on Bitcoin. Check out the calendar [link](https://www.addevent.com/event/yc0x95fky8y4) to join every other Tuesday. +* **AI BTC Working Group:** Join the **AI BTC Working Group**'s (WG) weekly meeting on AIBTC's twitter account, where they delve into the exciting intersection of AI and Bitcoin. Check out the calendar [link](https://www.addevent.com/event/c3qjy462xr82) to join every Thursday. +* **Runes Capsule Webinars:** Join a weekly deep-dive into Runes Capsule architecture, Bitcoin bridging, and trust minimized design patterns on Stacks. Check out this [link](https://calendar.google.com/calendar/u/0/r?cid=b247d75eb1a11dc3ebbf0c62eb4a1c83b4a53d4d2f903eaa19d685f28f087f92%40group.calendar.google.com) to join every Sundays. + +
        + +{% hint style="info" %} +Stacks ranks #5 among all crypto ecosystems for new developers in 2025! \[source: Electric Capital] +{% endhint %} + +### Hello, Builders 👋 + +Stacks is a fast, low-cost, builder-friendly layer 2 network on Bitcoin. It’s built on Bitcoin, inheriting Bitcoin’s battle-tested security. By jumping into our docs, you’re joining the Stacks builder community that’s bringing a global onchain economy to Bitcoin. + +If you're here on this page, hopefully you've already gotten a good sense of _what_ Stacks' purpose is, if not, head to the [Learn](https://app.gitbook.com/o/hoh4mQXTl8NvI3cETroY/s/H74xqoobupBWwBsVMJhK/) section. Or if you're still on the edge of _why_ you should build with Stacks, head to [Why Build with Stacks](get-started/introduction/why-build-with-stacks.md). + +*** + +### Pick your learning path + +We all have different styles of learning. If you've already got a good concept of web3 fundamentals and want to get a quick taste of what the DevEx is like on Stacks, then check out the [Developer Quickstart](get-started/developer-quickstart.md). Or find the path that clicks for you — and if bandwidth allows, tackle them all! + +

        Try the Developer Quickstart

        Your 0→1 guide for building a Clarity contract and app on Stacks.developer-quickstart.md

        Start Learning Clarity

        An easy starting point for learning smart contracts.clarity-crash-course.md

        Bitcoin Primer Tutorial

        A comprehensive end-to-end experience to building full-stack dApps on Bitcoin.Bitcoin Primer

        Earn a Stacks Developer Degree

        A hands-on Stacks bootcamp by LearnWeb3.https://learnweb3.io/degrees/stacks-developer-degree/

        Watch Our Hands-On Videos

        Developer insights and workshops from the Stacks ecosystemhttps://www.youtube.com/@stacks-developers

        Clarity Development Using Clarinet

        The smart contract toolkit for StacksBroken link
        + + + +*** + +### Who should use what + +| If you are… | First check out... | +| ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| Completely new to building with Stacks | [Developer Quickstart](get-started/developer-quickstart.md) | +| Learning smart contract development | [Clarity Crash Course](get-started/clarity-crash-course.md), [Learn Clarinet](/broken/pages/UK5Kgh2MHLoQvfoFVnLr) | +| Preferring a structured, guided course that shows you every step to build full-stack apps | [Stacks Developer Degree](https://learnweb3.io/degrees/stacks-developer-degree/), [Bitcoin Primer](https://app.gitbook.com/s/pJEGnyzUL9M4FaVMEB15/) | +| Wanting to integrate sBTC in your app | [sBTC guides](more-guides/sbtc/) | +| Launching a token | [Create Tokens](get-started/create-a-token/) | +| Interested in security / auditing / grants | [Stacks Bug Bounties](https://immunefi.com/bug-bounty/stacks/information/), [Stacks Foundation](https://stacks.org/grants) | +| Curious about current use cases | [Use Cases](get-started/use-cases/) | +| Searching for a complete list of devtools | [Stacks Devtools Ecosystem](stacks-devtools-ecosystem.md) | + +*** + +### Still got questions? + +We know, it's a lot. But we, along with our large community, are always more than welcome to help you throughout your journey with Stacks. + +* [**Stacks Discord**](https://discord.gg/stacks): Connect with other developers and our team +* [**Stacks Forum**](https://forum.stacks.org/): Ask questions and share projects +* [**Stacks Twitter**](https://x.com/StacksDevs)**:** Follow us on Twitter and ask us questions there diff --git a/docs/build/SUMMARY.md b/docs/build/SUMMARY.md new file mode 100644 index 0000000000..79a243112d --- /dev/null +++ b/docs/build/SUMMARY.md @@ -0,0 +1,103 @@ +# Table of contents + +## Get Started + +* [Introduction](README.md) + * [Why Build with Stacks](get-started/introduction/why-build-with-stacks.md) +* [Developer Quickstart](get-started/developer-quickstart.md) +* [Clarity Crash Course](get-started/clarity-crash-course.md) +* [Create a Token](get-started/create-a-token/README.md) + * [Fungible Tokens](get-started/create-a-token/fungible-tokens.md) + * [Non-Fungible Tokens](get-started/create-a-token/non-fungible-tokens.md) + * [Semi-Fungible Tokens](get-started/create-a-token/semi-fungible-tokens.md) +* [Build a Frontend](get-started/build-a-frontend/README.md) + * [Authentication](get-started/build-a-frontend/authentication.md) + * [Post-Conditions](get-started/build-a-frontend/post-conditions.md) + * [Sending Transactions](get-started/build-a-frontend/sending-transactions.md) +* [Use Cases](get-started/use-cases/README.md) + * [Payments](get-started/use-cases/payments.md) + * [Art](get-started/use-cases/art.md) + * [DeFi](get-started/use-cases/defi.md) + * [Gaming](get-started/use-cases/gaming.md) + * [AI](get-started/use-cases/ai.md) + +## What's New? + +* [Latest Updates](whats-new/latest-updates.md) + +## Learn Clarinet + +* [Overview](clarinet/overview.md) +* [Quickstart](clarinet/quickstart.md) +* [Project Structure](clarinet/project-structure.md) +* [Project Development](clarinet/project-development.md) +* [Contract Interaction](clarinet/contract-interaction.md) +* [Validation and Analysis](clarinet/validation-and-analysis.md) +* [Clarity Formatter](clarinet/clarity-formatter.md) +* [Local Blockchain Development](clarinet/local-blockchain-development.md) +* [Unit Testing](clarinet/testing-with-clarinet-sdk.md) +* [Mainnet Execution Simulation](clarinet/mainnet-execution-simulation.md) +* [Contract Deployment](clarinet/contract-deployment.md) +* [FAQ](clarinet/faq.md) +* [Integrations](clarinet/integrations/README.md) + * [Clarity VSCode Extension](clarinet/integrations/clarity-vscode-extension.md) + * [Chainhook Integration](clarinet/integrations/chainhook.md) + * [Stacks.js Integration](clarinet/integrations/stacks.js.md) + * [sBTC Integration](clarinet/integrations/sbtc.md) + +## Learn Rendezvous + +* [Overview](rendezvous/overview.md) +* [Quickstart](rendezvous/quickstart.md) + +## Learn Stacks.js + +* [Overview](stacks.js/overview.md) +* [Accounts & Addresses](stacks.js/accounts-and-addresses.md) +* [Private Keys](stacks.js/private-keys.md) +* [Networks](stacks.js/networks.md) +* [Read Only Calls](stacks.js/read-only-calls.md) +* [Build Transactions](stacks.js/build-transactions.md) +* [Contract Calls](stacks.js/contract-calls.md) +* [Contract Deployment](stacks.js/contract-deployment.md) +* [Address Validation](stacks.js/address-validation.md) +* [Encoding & Decoding](stacks.js/encoding-and-decoding.md) +* [Network Configuration](stacks.js/network-configuration.md) +* [Unit Conversion](stacks.js/unit-conversion.md) +* [React Native Integration](stacks.js/react-native-integration.md) + +## Stacks Connect + +* [Connect Wallet](stacks-connect/connect-wallet.md) +* [Broadcast Transactions](stacks-connect/broadcast-transactions.md) +* [Message Signing](stacks-connect/message-signing.md) +* [Migration Guide](stacks-connect/migration-guide.md) +* [Wallet Support](stacks-connect/wallet-support.md) +* [Wallet Implementation](stacks-connect/wallet-implementation.md) + +## Post-Conditions + +* [Overview](post-conditions/overview.md) +* [Implementing Post-Conditions](post-conditions/implementation.md) + +## More Guides + +* [sBTC](more-guides/sbtc/README.md) + * [sBTC Builder Quickstart](more-guides/sbtc/sbtc-builder-quickstart.md) + * [Bridging Bitcoin](more-guides/sbtc/bridging-bitcoin/README.md) + * [Depositing: Pegging BTC into sBTC](more-guides/sbtc/bridging-bitcoin/btc-to-sbtc.md) + * [Withdrawing: Pegging sBTC into BTC](more-guides/sbtc/bridging-bitcoin/sbtc-to-btc.md) +* [Price Oracles](more-guides/price-oracles/README.md) + * [Using Pyth with Stacks](more-guides/price-oracles/pyth.md) + * [Using DIA with Stacks](more-guides/price-oracles/dia.md) +* [Onboarding](more-guides/onboarding/README.md) + * [Signing with Turnkey](more-guides/onboarding/signing-with-turnkey.md) +* [Verifying Bitcoin Transactions in Clarity](more-guides/verify-bitcoin-transactions-clarity/README.md) + * [Creating a Bitcoin Transaction](more-guides/verify-bitcoin-transactions-clarity/creating-btc-tx.md) + * [Parsing a Bitcoin Transaction](more-guides/verify-bitcoin-transactions-clarity/parsing-a-bitcoin-transaction.md) +* [c32check](more-guides/c32check.md) +* [Community Tutorials](more-guides/community-tutorials.md) + +*** + +* [Stacks Devtools Ecosystem](stacks-devtools-ecosystem.md) diff --git a/docs/build/clarinet/clarity-formatter.md b/docs/build/clarinet/clarity-formatter.md new file mode 100644 index 0000000000..8a0cfb218f --- /dev/null +++ b/docs/build/clarinet/clarity-formatter.md @@ -0,0 +1,151 @@ +# Clarity Formatter + +The Clarity formatter automatically shapes your smart contract code to follow standardized style rules. Consistent formatting improves readability and makes collaboration easier across teams. + +## Formatting philosophy + +The formatter applies an opinionated standard designed to make Clarity code more readable: + +* **Line length** – wraps lines at 80 characters by default +* **Indentation** – uses two spaces for consistency +* **Structure** – enforces consistent patterns for functions, let bindings, and control flow + +You can customize these defaults to match your preferences. + +### Integration points + +{% stepper %} +{% step %} +#### Clarity VS Code Extension + +Format directly in the editor. +{% endstep %} + +{% step %} +#### Clarinet CLI + +Format via command line, including entire projects. +{% endstep %} +{% endstepper %} + +## Comparison table + +| Aspect | Manual formatting | Clarity formatter | +| ----------------- | ---------------------- | --------------------------- | +| Consistency | Varies by developer | Uniform across the codebase | +| Speed | Time-consuming | Instant | +| Error-prone | Yes | No | +| Team coordination | Requires a style guide | Automatic enforcement | + +## Best practices + +* **Format on save** – enable automatic formatting in VS Code +* **Pre-commit hooks** – ensure code is formatted before commits +* **Team adoption** – share consistent settings with your team + +## Formatting rules in detail + +### Function definitions + +Functions span multiple lines with consistent indentation: + +```clarity +(define-public (my-func + (amount uint) + (sender principal) + ) + (ok true) +) +``` + +Single arguments can remain on the first line: + +```clarity +(define-read-only (get-balance (who principal)) + (ok u0) +) +``` + +### Let expressions + +Bindings are placed on separate lines with consistent indentation: + +```clarity +(let ( + (a u1) + (b u2) +) + (body-expression) +) +``` + +### Control flow (if, match) + +Each branch receives its own line: + +```clarity +(if condition + (then-expression) + (else-expression) +) + +(match optional-value + value (handle-some value) + (handle-none) +) +``` + +### Tuples and maps + +The formatter automatically converts to sugared syntax with proper formatting: + +```clarity +;; Input: (tuple (n1 u1) (n2 u2)) +;; Output: +{ + n1: u1, + n2: u2, +} +``` + +## Usage examples + +### VS Code integration + +```json +// settings.json +"[clarity]": { + "editor.formatOnSave": true +} +``` + +### CLI usage + +```bash +clarinet format --in-place +``` + +Format with custom settings: + +```bash +clarinet format -i 4 -l 120 --in-place +``` + +Check formatting in CI/CD pipelines: + +```bash +clarinet format --check +``` + +The `--check` flag validates that all Clarity files are properly formatted without changing them, which is ideal for continuous integration workflows. + +## Ignoring blocks of code + +Prevent formatting for specific code blocks: + +```clarity +;; @format-ignore +(define-constant something (list + 1 2 3 ;; Preserves custom spacing + 4 5 )) +``` diff --git a/docs/build/clarinet/contract-deployment.md b/docs/build/clarinet/contract-deployment.md new file mode 100644 index 0000000000..76ba10123e --- /dev/null +++ b/docs/build/clarinet/contract-deployment.md @@ -0,0 +1,437 @@ +# Contract Deployment + +Clarinet provides deployment tooling that helps you move from local development to production networks. Whether you're testing on devnet, staging on testnet, or launching on mainnet, Clarinet streamlines the process. + +## Generating deployment plans + +Deployment plans are YAML files that describe how contracts are published or called. Be sure to have a valid 24 word mnemonic seed phrase specified in the target network's `.toml` file in `settings/` and then generate a plan for any network: + +```bash +$ clarinet deployments generate --testnet --medium-cost +Analyzing contracts... +Calculating deployment costs... +Generating deployment plan +Created file deployments/default.testnet-plan.yaml +``` + +Example output structure: + +{% code title="deployments/default.devnet-plan.yaml" %} +```yaml +--- +id: 0 +name: Devnet deployment +network: devnet +stacks-node: "http://localhost:20443" +bitcoin-node: "http://devnet:devnet@localhost:18443" +plan: + batches: + - id: 0 + transactions: + - contract-publish: + contract-name: counter + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 6940 + path: contracts/counter.clar + anchor-block-only: true + clarity-version: 2 + epoch: "2.5" +``` +{% endcode %} + +### Deployment plan structure + +| Field | Description | +| -------------- | -------------------------------------------- | +| `id` | Unique identifier for the deployment | +| `name` | Human-readable deployment name | +| `network` | Target network (devnet, testnet, or mainnet) | +| `stacks-node` | RPC endpoint for the Stacks node | +| `bitcoin-node` | RPC endpoint for the Bitcoin node | +| `plan.batches` | Array of deployment batches | + +### Deployment specifications + +Deployment behavior is configured in two places: + +* **Project configuration (`Clarinet.toml`)** – Clarity versions, dependencies, epoch requirements +* **Network configuration (`settings/.toml`)** – Account details, balances, endpoints + +Example network configuration: + +{% code title="settings/Testnet.toml" %} +```toml +[network] +name = "testnet" +deployment_fee_rate = 10 + +[accounts.deployer] +mnemonic = "" +balance = 100_000_000_000_000 +derivation = "m/44'/5757'/0'/0/0" +``` +{% endcode %} + +## Working with contract requirements + +Reference contracts that already exist on-chain—useful for standardized traits. + +### Adding requirements + +```bash +clarinet requirements add SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait +``` + +Clarinet updates your configuration automatically: + +```toml +[project] +name = "my-nft-project" +requirements = [ + { contract_id = "SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait" } +] +``` + +During deployment Clarinet downloads the contract, remaps the principal for devnet, and ensures the requirement is available before your contracts deploy. + +## Deploying to different networks + +### Devnet + +Devnet deploys contracts automatically when it starts: + +```bash +clarinet devnet start +``` + +To deploy manually against a running devnet: + +```bash +clarinet deployments apply --devnet +``` + +See [local development](local-blockchain-development.md) for more devnet configuration tips. + +### Testnet + +{% hint style="info" %} +Prerequisites: + +* Request testnet STX from the faucet +* Configure your account in `settings/Testnet.toml` +* Validate contracts with `clarinet check` +{% endhint %} + +Generate a deployment plan with cost estimation: + +```bash +clarinet deployments generate --testnet --medium-cost +``` + +Deploy to testnet: + +```bash +clarinet deployments apply --testnet +``` + +### Mainnet + +{% hint style="warning" %} +Mainnet checklist +{% endhint %} + +{% stepper %} +{% step %} +Finish thorough testing on testnet +{% endstep %} + +{% step %} +Audit contracts for security +{% endstep %} + +{% step %} +Ensure sufficient STX for fees +{% endstep %} + +{% step %} +Back up deployment keys securely +{% endstep %} + +{% step %} +Prefer a hardware wallet for deployment +{% endstep %} +{% endstepper %} + +Create a mainnet plan: + +```bash +clarinet deployments generate --mainnet --high-cost +``` + +Deploy with confirmation: + +```bash +clarinet deployments apply --mainnet +``` + +## Cost estimation and optimization + +Choose the right fee level for your deployment: + +```bash +clarinet deployments generate --testnet --manual-cost +``` + +Fee options: + +* `--low-cost` – minimize fees, slower confirmation +* `--medium-cost` – balanced approach +* `--high-cost` – priority inclusion +* `--manual-cost` – interactive selection + +Analyze costs before deploying: + +```bash +clarinet deployments generate --testnet --medium-cost +``` + +## Advanced deployment configurations + +### Multi-batch deployments + +Deploy complex systems with batches: + +{% code title="" %} +```yaml +plan: + batches: + - id: 0 + transactions: + - contract-publish: + contract-name: trait-definitions + path: contracts/traits.clar + clarity-version: 2 + - id: 1 + transactions: + - contract-publish: + contract-name: token + path: contracts/token.clar + - contract-publish: + contract-name: oracle + path: contracts/oracle.clar + - id: 2 + transactions: + - contract-publish: + contract-name: defi-pool + path: contracts/defi-pool.clar +``` +{% endcode %} + +Batches guarantee that dependencies deploy first, allow parallel transactions within a batch, and run batches sequentially. + +### Transaction types + +Deployment plans support different transaction types: + +| Transaction type | Description | +| ------------------- | ------------------------------------------------------------------- | +| Contract operations | Publish or call contracts, specifying sender, cost, and source path | +| Asset transfers | Transfer STX or BTC by setting sender, recipient, and amounts | + +**Contract operations** + +```yaml +- contract-publish: + contract-name: my-contract + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 5960 + path: contracts/my-contract.clar + clarity-version: 2 + +- contract-call: + contract-id: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.my-contract + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + method: initialize + parameters: + - u1000000 + - "Token Name" + cost: 5960 +``` + +**Asset transfers** + +```yaml +- stx-transfer: + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + recipient: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG + mstx-amount: 1000000 + memo: '0x48656c6c6f' + +- btc-transfer: + expected-sender: mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d + recipient: bcrt1qnxknq3wqtphv7sfwy07m7e4sr6ut9yt6ed99jg + sats-amount: 100000000 + sats-per-byte: 10 +``` + +### Manual customization + +You can edit deployment plans for complex scenarios. + +{% hint style="info" %} +Manual edits + +When Clarinet prompts to overwrite your plan, answer `no` to keep custom changes. +{% endhint %} + +Example contract initialization batch: + +```yaml +- id: 3 + transactions: + - contract-call: + contract-id: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token + method: initialize + parameters: + - u1000000 + - { name: "My Token", symbol: "MTK", decimals: u6 } + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM +``` + +### Encrypted mnemonics + +`clarinet 3.11.0` contains support for encrypted mnemonics. This feature gives users the option to encrypt the mnemonic seed phrase in their deployment files, so if a user's machine is compromised by a filesystem reading vulnerability, the seed phrase is not leaked to the attacker. + +To use this feature, a user must first run `clarinet deployments encrypt`, which will prompt the user for the seed phrase and a password, then print the encrypted mnemonic to the console. The user can then put the resulting ciphertext into their deployment config file using the key `encrypted_mnemonic`. The next time the user runs `clarinet deployments apply`, they will be prompted for the password, and the mnemonic will be decrypted for use in that session. + +For example, if your `settings/Mainnet.toml` file looks like this: + +{% code title="settings/Mainnet.toml" %} +```toml +[network] +name = "mainnet" +stacks_node_rpc_address = "https://api.hiro.so" +deployment_fee_rate = 10 + +[accounts.deployer] +mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw" +``` +{% endcode %} + +You would then run: + +{% code title="terminal" %} +``` +user@host package % clarinet deployments encrypt +Enter mnemonic to encrypt: +twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw +Enter password: + +encrypted_mnemonic = "" +``` +{% endcode %} + +You would then replace the `mnemonic` field in your settings file with the `encrypted_mnemonic` output above: + +{% code title="settings/Mainnet.toml" %} +```toml +[accounts.deployer] +encrypted_mnemonic = "47hYHSp4gtoBabz4X8cByJtRbvD3tBemS1zZJTkxYh2LJ7cVAHY6z74Td8bF5Dcsdpv45gDELPwfBP8Mfk64Q8TsBJNU9sf5hWMrTKPtr5h9abSdmxu4m2BewbUCi4o8znn42nAd7yphcb345YCrYLJFqFC7k9LqXvxgbQxUiFpWeyTVJPkGFa3aiQ8G5uhrv7pLCer4kRmXsmXbBvEqwEQLG7eM3TUMzUP79mHqJ1HGe2XWn" +``` +{% endcode %} + +Then the next time you deploy your package, you will be prompted for the password: + +{% code title="terminal" %} +``` +user@host package % clarinet deployments apply --mainnet + +Enter password to decrypt mnemonic for account deployer: +``` +{% endcode %} + +## Common issues + +### Insufficient STX balance + +**Error**: “Insufficient STX balance for deployment” + +Solutions: + +{% stepper %} +{% step %} +Request testnet STX from the faucet +{% endstep %} + +{% step %} +Reduce the fee rate with `--low-cost` +{% endstep %} + +{% step %} +Check your balance: + +```bash +clarinet console --testnet +stx-account 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG +``` +{% endstep %} +{% endstepper %} + +### Contract already exists + +**Error**: “Contract `token` already deployed at this address” + +Solutions: + +{% stepper %} +{% step %} +Use a different contract name +{% endstep %} + +{% step %} +Deploy from another address +{% endstep %} + +{% step %} +On testnet, switch to a fresh account +{% endstep %} +{% endstepper %} + +### Network connection failures + +**Error**: “Failed to connect to testnet node” + +Check your network settings: + +```toml +[network] +name = "testnet" +node_rpc_address = "https://stacks-node-api.testnet.stacks.co" +``` + +Alternative endpoints: + +* Hiro: `https://api.testnet.hiro.so` +* Your own node + +Debug the connection: + +```bash +curl -I https://api.testnet.hiro.so/v2/info +``` + +### Invalid deployment plan + +**Common YAML errors** + +* Incorrect indentation +* Missing required fields +* Invalid contract paths + +Validate and regenerate as needed: + +```bash +clarinet deployments check +clarinet deployments generate --testnet +ls contracts/ +``` diff --git a/docs/build/clarinet/contract-interaction.md b/docs/build/clarinet/contract-interaction.md new file mode 100644 index 0000000000..c34d27116d --- /dev/null +++ b/docs/build/clarinet/contract-interaction.md @@ -0,0 +1,178 @@ +# Contract Interaction + +Clarinet provides powerful tools for interacting with your contracts during development. The console gives you an interactive REPL where you can call functions, inspect state, and debug issues in real time. This interactive REPL environment in the console is also referred to as the simnet. + +
        + +What is a simnet? + +* Simnet is a lightweight environment optimized for fast feedback loops, introspection and portability. +* Simnet focuses on letting you quickly iterate on your code and test the code of the contract itself through unit testing. It’s a good preliminary debugging step before introducing the additional variables that come with a fully-fledged blockchain environment. +* Simnet enables you to create a bunch of reports about contract analysis, execution costs, and more and is a useful tool for unit testing your smart contracts. +* In simnet, the blockchain environment is simulated and can be run anywhere (in the terminal with clarinet console, web browsers, GitHub actions, etc). + +
        + +## Starting the console + +Use `clarinet console` to launch an interactive session with your contracts deployed to a local simulated blockchain REPL. This is also referred to as simnet. + +```bash +clarinet console +``` + +Sample startup output: + +``` +clarity-repl v3.3.0 +Enter "::help" for usage hints. +Connected to a transient in-memory database. +``` + +The console supports several useful flags for different development scenarios: + +| Option | Description | +| --------------------------------------- | ----------------------------------------------------- | +| `--enable-remote-data` | Connect to mainnet or testnet to query real contracts | +| `--deployment-plan-path ` | Use a specific deployment plan | +| `--manifest-path ` | Use an alternate `Clarinet.toml` location | +| `--remote-data-api-url ` | Specify a custom Stacks API endpoint | +| `--remote-data-initial-height ` | Set the starting block height for remote data | + +## Working with remote data + +One of the most powerful features is the ability to interact with real mainnet or testnet contracts from your local console. This lets you test against actual deployed contracts: + +```bash +clarinet console --enable-remote-data +``` + +Example contract calls: + +```clarity +(contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token get-decimals) +;; (ok u8) + +(contract-call? 'SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token get-name) +;; (ok "Arkadiko Token") +``` + +These capabilities help you: + +* Test integrations with existing protocols +* Verify contract behavior against live chain state +* Develop contracts that depend on mainnet deployments + +> **Warning: Remote data requirements** +> +> Before using remote data, add the target contract to `Clarinet.toml` with `clarinet requirements add SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token`. + +### Using the Hiro API key + +Avoid rate limits by setting the `HIRO_API_KEY` environment variable before launching the console. Clarinet forwards this key in the `x-api-key` header for all requests: + +```bash +export HIRO_API_KEY=your_api_key_here +clarinet console --enable-remote-data +``` + +You can request a free API key from the Hiro Platform. + +### Working with contracts + +List all available contracts in the session: + +```clarity +::get_contracts +;; +---------------------------------------------------------+----------------------+ +;; | Contract identifier | Public functions | +;; |---------------------------------------------------------+----------------------| +;; | ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter | (count-up) | +;; | | (get-count (who ...))| +;; +---------------------------------------------------------+----------------------+ + +(contract-call? .counter count-up) +;; (ok true) + +(contract-call? .counter get-count tx-sender) +;; u1 +``` + +### Working with different principals + +Switch between the provided test wallets to validate multi-user flows: + +```clarity +::get_assets_maps +;; +-------------------------------------------+-----------------+ +;; | Address | uSTX | +;; |-------------------------------------------+-----------------| +;; | ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM | 100000000000000 | +;; | ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 | 100000000000000 | +;; ... + +::set_tx_sender ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 +;; tx-sender switched to ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 +``` + +### Working with block heights + +Advance the chain to test time-dependent logic: + +```clarity +::get_block_height +;; Current block height: 4 + +::advance_chain_tip 100 +;; new burn height: 3 +;; new stacks height: 104 + +::get_block_height +;; Current block height: 104 +``` + +> **Tip: Console reference** +> +> For a complete list of console commands, see the [CLI reference](../../reference/clarinet/cli-reference.md). + +## Common issues + +
        + +Contract not found errors + +If you see `use of unresolved contract` errors, the contract may not be deployed or the name might be incorrect: + +```clarity +(contract-call? .missing-contract get-value) +;; error: use of unresolved contract +``` + +Solutions: + +* Check for typos in the contract identifier +* Confirm the contract is deployed in the current session with `::get_contracts` +* Use the correct prefix (`.` for local contracts) + +
        + +
        + +Remote data connection issues + +When you enable remote data, rate limits or connectivity problems can occur: + +```clarity +(contract-call? 'SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token get-name) +;; error: API rate limit exceeded +``` + +**Solutions:** + +1. Set your Hiro API key: `export HIRO_API_KEY=your_key_here` +2. Use a custom API endpoint: `--remote-data-api-url https://your-node.com` +3. Wait for rate limit to reset (usually 1 minute) + +
        + +## diff --git a/docs/build/clarinet/faq.md b/docs/build/clarinet/faq.md new file mode 100644 index 0000000000..9828aff908 --- /dev/null +++ b/docs/build/clarinet/faq.md @@ -0,0 +1,190 @@ +# FAQ + +Common questions and solutions for Clarinet development. + +This page addresses common issues encountered when building with Clarinet, based on community feedback and support interactions. + +
        + +How do I test with sBTC tokens in my development environment? + +To test with sBTC tokens, add the mainnet sBTC contract as a requirement and mint tokens using the deployer address. + +* Add sBTC as a requirement:\ + `clarinet requirements add SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token` +* Mint sBTC in your tests\ + // The sBTC multisig address that can mint\ + `const sbtcDeployer = "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4";`\ + // Mint sBTC to your test wallet\ + `const mintTx = simnet.callPublicFn( "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token", "mint", [Cl.uint(1000000), Cl.principal(wallet1)], sbtcDeployer);`\ + \ + This approach lets you work with sBTC in unit tests without complex Bitcoin transaction simulation. + +
        + +
        + +Why am I getting an error when using mainnet addresses during mainnet simulation? + +When you run mainnet execution simulation, the target contract may expect mainnet addresses instead of the default testnet wallets. As of Clarinet v3.4.0, you can enable mainnet wallets in simnet with `use_mainnet_wallets = true`: + +```toml +[repl.remote_data] +enabled = true +initial_height = 522000 +use_mainnet_wallets = true +``` + +If you prefer to manage addresses manually, skip `simnet.getAccounts()` and use the specific mainnet principals you need: + +```ts +// Instead of using simnet.getAccounts() +const mainnetAddress = "SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY"; + +// Mint STX to any mainnet address +simnet.mintSTX(mainnetAddress, 1000000n); + +// Call functions with a mainnet address +const result = simnet.callReadOnlyFn( + "SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3", + "get-price", + [priceFeed], + mainnetAddress +); +``` + +The simnet accepts any valid Stacks address when mainnet simulation is enabled. + +
        + +
        + +How do I migrate from expectSTXTransferEvent to the new SDK? + +Clarinet v2 relies on standard Vitest matchers instead of the legacy event helpers. + +Old approach (Clarinet v1): + +```ts +block.receipts[0].events.expectSTXTransferEvent( + amount, + sender, + recipient +); +``` + +New approach (Clarinet v2): + +```ts +// Check for an exact event match +expect(events).toContainEqual({ + event: "stx_transfer_event", + data: { + amount: "1000000", + memo: "", + recipient: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + sender: "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5", + }, +}); + +// Or assert specific properties only +expect(events).toContainEqual({ + event: "stx_transfer_event", + data: expect.objectContaining({ + sender: address1, + recipient: contractAddress, + }), +}); +``` + +For Clarity value assertions, use the built-in matchers: + +```ts +expect(result).toBeOk(Cl.bool(true)); +expect(result).toBeErr(Cl.uint(500)); +``` + +
        + +
        + +Why am I getting "bip39 error" when generating deployment plans? + +Starting with Clarinet 2.15.0, deployment configurations require 24-word mnemonics. Twelve-word mnemonics are no longer supported. + +Update your configuration with a full 24-word phrase: + +```toml +[accounts.deployer] + +# Use a 24-word mnemonic +mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw" +``` + +Generate a new 24-word mnemonic using a BIP39 generator if needed. The longer phrase improves security for production deployments. + +
        + +
        + +Can I test Bitcoin transaction verification in Clarinet? + +Testing contracts that use `clarity-bitcoin-lib` for Bitcoin transaction verification has limitations in simnet and devnet environments. + +Current limitations: + +* No real Bitcoin blocks or transactions in simnet +* Mock blocks do not contain verifiable Bitcoin transactions +* `get-burn-block-info?` returns mock data unsuitable for verification + +Workarounds: + +* Test Bitcoin verification logic on mainnet or with mainnet execution simulation +* Write unit tests that mock expected behavior instead of full verification +* Consider separating Bitcoin verification logic so it can be tested independently + +The Clarinet team continues to investigate better support for Bitcoin-focused testing. + +
        + +
        + +Why does my devnet freeze at the epoch 3.0 transition? + +The epoch 3.0 transition in devnet can be unstable, with success rates between 50–80% depending on your setup. + +Temporary workarounds: + +* Restart devnet if it freezes around blocks 139–140 +* Try Clarinet 2.14.0, which some users report as more stable +* Watch for the upcoming feature to start devnet directly in epoch 3.0 + +You can also monitor the transition manually: + +``` +# Watch for the transition around these blocks +Block 139: Epoch 2.5 +Block 140: Should transition to 3.0 +``` + +The Clarinet team is working on improving epoch transition stability and plans to allow starting devnet in epoch 3.0. + +
        + +
        + +What's the difference between simnet and devnet? + +A simnet is a lightweight environment optimized for fast feedback loops, introspection and portability. The scenarios you would use simnet would be during Clarinet console interaction, unit testing, and execution costs reporting. + +A devnet refers to a local blockchain development environment in which your smart contracts and front end application can interact with simulated blockchain entities. The scenarios you would use devnet is during your front end development, interaction with web wallets, and creating custom blockchain configurations for testing. + +| | Simnet | Devnet | +| ------------------- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | +| Testing Scope | good for unit/integration tests | good for end to end testing with deployment, real multi-contract interactions with wallets, web interactions, etc | +| State Persistence | no persistent state, resets with each run | persistent state, can retain data across sessions | +| Network Behavior | does not simulate real network behavior (ie you control block heights and mining manually) | simulates network behavior automatically like block progress, txs, etc | +| Consensus Mechanism | not applicable, operates in a simulated context | can implement and test against consensus rules | +| Transaction Fees | no tx fees or gas | can simulate these in real world scenarios | + +
        diff --git a/docs/build/clarinet/integrations/README.md b/docs/build/clarinet/integrations/README.md new file mode 100644 index 0000000000..f996ed9ff2 --- /dev/null +++ b/docs/build/clarinet/integrations/README.md @@ -0,0 +1,5 @@ +# Integrations + +Explore powerful external tools and resources you can integrate into your Clarity smart contract workflow with Clarinet to level up your development experience. + +

        Clarity VSC Extension

        clarity-vscode-extension.md

        Chainhooks

        chainhook.md

        Stacks.js

        stacks.js.md

        sBTC

        sbtc.md
        diff --git a/docs/build/clarinet/integrations/chainhook.md b/docs/build/clarinet/integrations/chainhook.md new file mode 100644 index 0000000000..a341bb858c --- /dev/null +++ b/docs/build/clarinet/integrations/chainhook.md @@ -0,0 +1,160 @@ +# Chainhook Integration + +Learn how to register Chainhooks on Clarinet devnet so you can monitor smart contract events during local development. + +## What you'll learn + +* Create Chainhook predicate files for event monitoring +* Register Chainhooks with Clarinet devnet +* Monitor contract calls and blockchain events +* Set up webhooks for real-time notifications + +{% hint style="info" %} +Prerequisites + +* Clarinet `2.1.0` or later (`clarinet --version`) +* Node.js `16` or later (`node --version`) +{% endhint %} + +## Quickstart + +{% stepper %} +{% step %} +#### Create your Chainhook predicates + +Create predicate files in a `chainhooks/` directory alongside your contracts: + +* contracts/ + * counter.clar +* chainhooks/ + * increment.json + * decrement.json +* tests/ + * counter.test.ts +* Clarinet.toml + +Example predicate for monitoring increment events: + +{% code title="chainhooks/increment.json" %} +```json +{ + "chain": "stacks", + "uuid": "increment-hook", + "name": "Increment Counter Hook", + "version": 1, + "networks": { + "devnet": { + "if_this": { + "scope": "contract_call", + "contract_identifier": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter", + "method": "increment" + }, + "then_that": { + "http_post": { + "url": "http://localhost:3000/api/increment", + "authorization_header": "Bearer my-secret" + } + } + } + } +} +``` +{% endcode %} +{% endstep %} + +{% step %} +#### Start devnet with Chainhooks + +From your project root, start devnet. Clarinet registers every predicate automatically: + +{% code title="Start devnet" %} +```bash +clarinet devnet start +``` +{% endcode %} + +Check the logs for a confirmation message such as: + +{% code title="Clarinet log" %} +``` +INFO Feb 5 15:20:07.233382 2 chainhooks registered +``` +{% endcode %} +{% endstep %} + +{% step %} +#### Monitor Chainhook activity + +Trigger contract actions and watch for Chainhook alerts: + +{% code title="Clarinet log" %} +``` +INFO Feb 5 15:21:07.233382 1 hooks triggered +``` +{% endcode %} + +Verify the payload based on your `then_that` configuration: + +* `http_post` – confirm your endpoint received the POST request +* `file_append` – ensure the file was created or updated +{% endstep %} +{% endstepper %} + +## Common patterns + +### Contract deployment hook + +Monitor when specific contracts are deployed: + +{% code title="chainhooks/deploy.json" %} +```json +{ + "chain": "stacks", + "uuid": "deploy-hook", + "name": "Contract Deploy Monitor", + "version": 1, + "networks": { + "devnet": { + "if_this": { + "scope": "contract_deployment", + "deployer": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" + }, + "then_that": { + "file_append": { + "path": "./deployments.log" + } + } + } + } +} +``` +{% endcode %} + +### STX transfer monitoring + +Track STX transfers above a certain threshold: + +{% code title="chainhooks/stx-transfer.json" %} +```json +{ + "chain": "stacks", + "uuid": "stx-transfer-hook", + "name": "Large STX Transfer Monitor", + "version": 1, + "networks": { + "devnet": { + "if_this": { + "scope": "stx_event", + "actions": ["transfer"], + "amount_upper_bound": "1000000000000" + }, + "then_that": { + "http_post": { + "url": "http://localhost:3000/api/large-transfer" + } + } + } + } +} +``` +{% endcode %} diff --git a/docs/build/clarinet/integrations/clarity-vscode-extension.md b/docs/build/clarinet/integrations/clarity-vscode-extension.md new file mode 100644 index 0000000000..006aa8a034 --- /dev/null +++ b/docs/build/clarinet/integrations/clarity-vscode-extension.md @@ -0,0 +1,126 @@ +# VSCode Extension + +{% hint style="info" %} +The [VSCode extension](https://marketplace.visualstudio.com/items?itemName=StacksLabs.clarity-stacks) for Clarity is now published under the Stacks Labs organization. +{% endhint %} + +## Features + +### Smart auto-completion + +The extension provides intelligent code completion that understands Clarity's context. When you start typing any Clarity function, you get instant suggestions with documentation: + +```clarity +;; Type "stx-tr" and get: +(stx-transfer? amount sender recipient) +;; ^ ^ ^ +;; Placeholders for easy navigation +``` + +Use `Tab` to jump between placeholders and `Escape` to exit placeholder mode. + +### Documentation on hover + +Access comprehensive documentation without leaving your editor. Hover over any Clarity function or keyword to see: + +```clarity +;; Hover over 'map-set' to see: +;; - Function signature +;; - Parameter descriptions +;; - Return type +;; - Usage examples +(map-set my-map {key: "value"} "data") +``` + +### Go-to definition + +Navigate your codebase efficiently with jump-to-definition features: + +* `F12` or `Ctrl+Click` - Go to definition +* `Alt+F12` - Peek definition without leaving current file +* Works across contract files and contract calls + +### Real-time error checking + +The extension validates your code continuously, providing immediate feedback: + +* **Red squiggles** - Syntax errors, unknown keywords +* **Yellow squiggles** - Warnings for potentially unsafe code +* **Error list** - All issues in the Problems panel (`Ctrl+Shift+M`) + +Common errors caught include undefined variables, type mismatches, missing trait implementations, and invalid function signatures. + +### Local contract resolution + +Auto-completion works across your entire project. Reference functions from other contracts in your workspace: + +```clarity +;; Auto-complete local contract calls +(contract-call? .my-token transfer amount sender recipient) +;; ^ +;; Suggests contracts in your project +``` + +### Trait support + +When implementing traits (like SIP-009 NFTs or SIP-010 tokens), the extension verifies: + +* All required functions are implemented +* Function signatures match trait definitions +* Return types are correct + +```clarity +;; Extension warns if missing required trait functions +(impl-trait .sip-010-trait.sip-010-trait) + +;; ⚠️ Warning: Missing required function 'get-balance' +``` + +### Visual debugging + +{% stepper %} +{% step %} +**Set breakpoints** + +Set breakpoints by clicking line numbers in the editor. +{% endstep %} + +{% step %} +**Start debugging** + +Press `F5` or use Run → Start Debugging to begin a debugging session. +{% endstep %} + +{% step %} +**Step through code** + +Step through code line-by-line to follow execution. +{% endstep %} + +{% step %} +**Inspect state** + +Inspect variables and stack state while paused at breakpoints. +{% endstep %} +{% endstepper %} + +{% hint style="warning" %} +Visual debugging requires VS Code Desktop and Clarinet installed locally. +{% endhint %} + +## Comparison table + +| Feature | Basic Editor | VS Code Extension | +| --------------------- | --------------- | ----------------------- | +| Syntax highlighting | Limited | Full Clarity support | +| Auto-completion | None | Context-aware with docs | +| Error checking | On deploy only | Real-time validation | +| Documentation | External lookup | Inline hover docs | +| Debugging | Console only | Visual debugger | +| Cross-file navigation | Manual | Jump to definition | + +*** + +### Additional Resources + +* \[[zed.dev](https://zed.dev/extensions?query=clarity)] Zed extension for Clarity diff --git a/docs/build/clarinet/integrations/sbtc.md b/docs/build/clarinet/integrations/sbtc.md new file mode 100644 index 0000000000..fe3d163238 --- /dev/null +++ b/docs/build/clarinet/integrations/sbtc.md @@ -0,0 +1,225 @@ +# sBTC Integration + +

        source: Hiro blog

        + +Clarinet can automatically wire up the official sBTC contracts so you can build and test SIP-010 flows locally. + +## What you'll learn + +* Add sBTC smart contracts to your Clarinet project +* Test contracts with automatic sBTC funding in devnet +* Work with sBTC as a SIP-010 fungible token +* Deploy sBTC contracts to testnet and mainnet + +## Prerequisites + +* Clarinet 2.15.0 or later required for automatic sBTC integration. + +## Quickstart + +{% stepper %} +{% step %} +**Add sBTC to your project** + +Add the sBTC contracts to your project requirements: + +```bash +clarinet requirements add SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-deposit +``` + +This pulls in: + +* `sbtc-token` – SIP-010 fungible token contract +* `sbtc-registry` – configuration registry +* `sbtc-deposit` – deposit and withdrawal logic + +Clarinet auto-funds devnet wallets with sBTC when these are present. +{% endstep %} + +{% step %} +**Create an sBTC-enabled contract** + +Example NFT marketplace that accepts sBTC payments: + +```clarity +(define-non-fungible-token marketplace-nft uint) +(define-data-var mint-price uint u100) +(define-data-var next-id uint u0) + +(define-public (mint-with-sbtc) + (begin + (try! (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token transfer + (var-get mint-price) + tx-sender + (as-contract tx-sender) + none)) + (try! (nft-mint? marketplace-nft (var-get next-id) tx-sender)) + (ok (var-set next-id (+ (var-get next-id) u1))) + ) +) + +(define-read-only (get-sbtc-balance (owner principal)) + (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token get-balance owner) +) +``` +{% endstep %} + +{% step %} +**Test in the Clarinet console** + +Launch the console and try the contract using auto-funded wallets: + +```bash +clarinet console +``` + +```clarity +(contract-call? .nft-marketplace get-sbtc-balance tx-sender) +(contract-call? .nft-marketplace mint-with-sbtc) +(nft-get-owner? .nft-marketplace marketplace-nft u0) +``` +{% endstep %} + +{% step %} +**Write unit tests** + +Sample Vitest test for sBTC payments: + +```ts +import { describe, expect, it } from 'vitest'; +import { Cl } from '@stacks/transactions'; + +describe('NFT Marketplace', () => { + it('mints NFT with sBTC payment', () => { + const accounts = simnet.getAccounts(); + const wallet1 = accounts.get('wallet_1')!; + + const initial = simnet.callReadOnlyFn( + 'nft-marketplace', + 'get-sbtc-balance', + [Cl.standardPrincipal(wallet1.address)], + wallet1.address + ); + + const mint = simnet.callPublicFn( + 'nft-marketplace', + 'mint-with-sbtc', + [], + wallet1 + ); + + expect(mint.result).toBeOk(); + + const final = simnet.callReadOnlyFn( + 'nft-marketplace', + 'get-sbtc-balance', + [Cl.standardPrincipal(wallet1.address)], + wallet1.address + ); + + expect(Number(Cl.parse(final.result))).toBeLessThan( + Number(Cl.parse(initial.result)) + ); + }); +}); +``` +{% endstep %} + +{% step %} +**Deploy to testnet** + +Generate a plan to confirm remapped addresses for official sBTC contracts: + +```bash +clarinet deployments generate --testnet +``` + +Deploy when ready: + +```bash +clarinet deployments apply --testnet +``` +{% endstep %} +{% endstepper %} + +## Common patterns + +### Working with sBTC addresses + +Clarinet handles sBTC contract address mapping across networks: + +| Network | sBTC Contract Address | +| ------------- | ------------------------------------------------------ | +| Simnet/Devnet | `SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token` | +| Testnet | `ST1F7QA2MDF17S807EPA36TSS8AMEFY4KA9TVGWXT.sbtc-token` | +| Mainnet | Contract address remains unchanged | + +Your contract code always references the simnet address. Clarinet automatically remaps during deployment. + +## Manual sBTC minting in unit tests + +While Clarinet 2.15.0+ automatically funds wallets with sBTC in devnet, you may need to manually mint sBTC in unit tests for specific scenarios. + +### Minting sBTC using the deployer address + +The sBTC token contract allows the deployer (multisig) address to mint tokens. Use this approach in your tests: + +```ts +import { describe, expect, it } from "vitest"; +import { Cl } from "@stacks/transactions"; + +describe("Manual sBTC minting", () => { + it("mints sBTC to custom addresses", () => { + // The sBTC multisig address that can mint + const sbtcDeployer = "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4"; + const customWallet = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM"; + + // Mint 1000 sats to custom wallet + const mintResult = simnet.callPublicFn( + "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token", + "mint", + [ + Cl.uint(1000), // amount in sats + Cl.principal(customWallet) // recipient + ], + sbtcDeployer // sender must be deployer + ); + + expect(mintResult.result).toBeOk(Cl.bool(true)); + + // Verify balance + const balance = simnet.callReadOnlyFn( + "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token", + "get-balance", + [Cl.principal(customWallet)], + customWallet + ); + + expect(balance.result).toBeOk(Cl.uint(1000)); + }); +}); +``` + +### Testing with mainnet execution simulation + +When using mainnet execution simulation, you can mint sBTC using the actual mainnet multisig: + +```ts +const mainnetMultisig = "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4"; +const mainnetWallet = "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR"; + +// Mint sBTC to any mainnet address +simnet.callPublicFn( + "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token", + "mint", + [Cl.uint(100000), Cl.principal(mainnetWallet)], + mainnetMultisig +); +``` + +This approach is useful for: + +* Testing specific sBTC amounts +* Simulating different wallet balances +* Testing edge cases with precise token amounts +* Integration testing with mainnet contracts diff --git a/docs/build/clarinet/integrations/stacks.js.md b/docs/build/clarinet/integrations/stacks.js.md new file mode 100644 index 0000000000..80843af077 --- /dev/null +++ b/docs/build/clarinet/integrations/stacks.js.md @@ -0,0 +1,212 @@ +# Stacks.js Integration + +Use Stacks.js to interact with your Clarinet devnet from JavaScript applications. + +## What you'll learn + +* Configure Stacks.js for a local devnet connection +* Make STX transfers between devnet accounts +* Call smart contract functions from JavaScript +* Deploy contracts programmatically + +## Quickstart + +{% stepper %} +{% step %} +#### Install Stacks.js packages + +Add the required libraries to your frontend project: + +```bash +npm install @stacks/transactions @stacks/network +``` +{% endstep %} + +{% step %} +#### Configure for devnet + +Create a network helper: + +```ts +import { StacksDevnet } from '@stacks/network'; + +export const devnet = new StacksDevnet({ + url: 'http://localhost:3999' +}); + +export const accounts = { + deployer: { + address: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + key: 'cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01' + }, + wallet1: { + address: 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5', + key: '7287ba251d44a4d3fd9276c88ce34c5c52a038955511cccaf77e61068649c17801' + } +}; +``` +{% endstep %} + +{% step %} +#### Test STX transfers + +Send a transfer between devnet accounts: + +```ts +import { makeSTXTokenTransfer, broadcastTransaction, AnchorMode } from '@stacks/transactions'; +import { devnet, accounts } from './devnet-config'; + +async function transferSTX() { + const tx = await makeSTXTokenTransfer({ + amount: 1_000_000n, + recipient: accounts.wallet1.address, + senderKey: accounts.deployer.key, + network: devnet, + anchorMode: AnchorMode.Any, + }); + + const result = await broadcastTransaction(tx, devnet); + console.log('Transaction ID:', result.txid); +} + +transferSTX().catch(console.error); +``` + +Run the transfer with `ts-node stx-transfer.ts`. +{% endstep %} + +{% step %} +#### Call smart contracts + +Interact with contracts deployed on devnet: + +```ts +import { makeContractCall, broadcastTransaction, AnchorMode } from '@stacks/transactions'; +import { devnet, accounts } from './devnet-config'; + +async function callContract() { + const tx = await makeContractCall({ + contractAddress: accounts.deployer.address, + contractName: 'counter', + functionName: 'increment', + functionArgs: [], + senderKey: accounts.wallet1.key, + network: devnet, + anchorMode: AnchorMode.Any, + }); + + await broadcastTransaction(tx, devnet); +} + +async function readCount() { + const response = await fetch( + `${devnet.coreApiUrl}/v2/contracts/call-read/${accounts.deployer.address}/counter/get-count`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + sender: accounts.wallet1.address, + arguments: [] + }) + } + ); + const data = await response.json(); + console.log(data); +} + +callContract().catch(console.error); +readCount().catch(console.error); +``` +{% endstep %} + +{% step %} +#### Deploy contracts programmatically + +Deploy a contract from your application code: + +```ts +import { makeContractDeploy, broadcastTransaction, AnchorMode } from '@stacks/transactions'; +import { devnet, accounts } from './devnet-config'; +import counterContract from './contracts/counter.clar?raw'; + +async function deployCounter() { + const tx = await makeContractDeploy({ + contractName: 'counter', + codeBody: counterContract, + senderKey: accounts.deployer.key, + network: devnet, + anchorMode: AnchorMode.Any, + }); + + return broadcastTransaction(tx, devnet); +} + +deployCounter().catch(console.error); +``` +{% endstep %} +{% endstepper %} + +## Common patterns + +### Watching for transaction confirmation + +Monitor when transactions are confirmed on devnet: + +```ts +async function waitForTransaction(txid: string) { + let attempts = 0; + const maxAttempts = 10; + + while (attempts < maxAttempts) { + const response = await fetch( + `${devnet.coreApiUrl}/extended/v1/tx/${txid}` + ); + + const tx = await response.json(); + + if (tx.tx_status === 'success') { + console.log('Transaction confirmed!'); + return tx; + } + + if (tx.tx_status === 'abort_by_response') { + throw new Error(`Transaction failed: ${tx.tx_result.repr}`); + } + + // Wait for next block + await new Promise(resolve => setTimeout(resolve, 5000)); + attempts++; + } + + throw new Error('Transaction timeout'); +} +``` + +### Post conditions for safety + +Add post conditions to ensure contract calls behave as expected: + +```ts +import { Pc } from '@stacks/transactions'; + +const postConditions = [ + // Ensure sender's balance decreases by exactly the amount + Pc.principal(accounts.wallet1.address) + .willSendEq(1000000n) + .ustx(), + + // Ensure recipient receives the amount + Pc.principal(accounts.deployer.address) + .willReceiveEq(1000000n) + .ustx() +]; + +const tx = await makeSTXTokenTransfer({ + amount: 1000000n, + recipient: accounts.deployer.address, + senderKey: accounts.wallet1.key, + network: devnet, + postConditions, + anchorMode: AnchorMode.Any, +}); +``` diff --git a/docs/build/clarinet/local-blockchain-development.md b/docs/build/clarinet/local-blockchain-development.md new file mode 100644 index 0000000000..8d693a5a76 --- /dev/null +++ b/docs/build/clarinet/local-blockchain-development.md @@ -0,0 +1,492 @@ +# Local Blockchain Development + +Clarinet ships with a complete local blockchain environment so you can build, test, and debug smart contracts without deploying to a public network. This local blockchain environment is what is referred to as a devnet. + +
        + +What is a devnet? + +* A devnet refers to a local blockchain development environment in which your smart contracts and front end application can interact with simulated blockchain entities. +* With a devnet, your smart contract application can interact with simulated blockchain entities (miners, nodes, and a stream of mined blocks), all within your local machine. +* When devnets simulate a blockchain environment, the entities created—the other contracts, transactions, or nodes—resemble the conditions your application will inhabit once in production. +* Devnets enable you to create different blockchain configurations. +* You can share a simnet environment with other devs and collaborate with them. +* You can start a devnet at an arbitrary block height with a specified network upgrade at a later block, and with many simulated users, to see how your application responds. + +
        + +## Starting your local blockchain + +Launch devnet with all required services: + +```bash +clarinet devnet start +``` + +Useful flags: + +| Option | Description | +| -------------------------------- | ------------------------------------------------- | +| `--manifest-path ` | Use an alternate `Clarinet.toml` | +| `--no-dashboard` | Stream logs instead of showing the interactive UI | +| `--deployment-plan-path ` | Apply a specific deployment plan | +| `--use-on-disk-deployment-plan` | Use an existing plan without recomputing | +| `--use-computed-deployment-plan` | Recompute and overwrite the plan | +| `--package ` | Load a packaged devnet configuration | + +{% hint style="info" %} +Prerequisites + +Devnet requires Docker. If you see “clarinet was unable to create network,” ensure Docker Desktop is running or the Docker daemon is started. +{% endhint %} + +By default the dashboard displays service health, recent transactions, block production, contract deployments, and resource usage. Use `--no-dashboard` in CI or when you prefer streaming logs. + +## Core services and features + +Devnet starts these services for you: + +| Service | Port | Purpose | +| ---------------- | ----- | ---------------------------------------- | +| Stacks node | 20443 | Processes transactions and mines blocks | +| Bitcoin node | 18443 | Provides block anchoring in regtest mode | +| Stacks API | 3999 | REST API for blockchain data | +| Postgres | 5432 | Indexes blockchain data | +| Stacks Explorer | 8000 | Browse transactions in a web UI | +| Bitcoin Explorer | 8001 | View the Bitcoin regtest chain | + +Devnet includes pre-funded accounts: + +```clarity +::get_assets_maps +;; +-------------------------------------------+-----------------+ +;; | Address | STX Balance | +;; |-------------------------------------------+-----------------| +;; | ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM | 100000000000000 | +;; | ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 | 100000000000000 | +;; | ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG | 100000000000000 | +;; | ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC | 100000000000000 | +;; | ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND | 100000000000000 | +;; +-------------------------------------------+-----------------+ +``` + +When devnet starts it automatically deploys your project contracts so you can interact immediately. + +``` +$ clarinet devnet start +Deploying contracts... +Deploying counter.clar ✓ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter +Deploying token.clar ✓ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token +Deploying marketplace.clar ✓ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.marketplace + +All contracts deployed successfully +``` + +## Configuration and customization + +Devnet behavior is controlled by configuration files in your project. + +### Basic configuration + +`settings/Devnet.toml` defines network settings: + +```toml +[network] +name = "devnet" + +# Service ports +stacks_node_rpc_port = 20443 +stacks_api_port = 3999 +stacks_explorer_port = 8000 +bitcoin_node_rpc_port = 18443 + +[network.devnet] +bitcoin_controller_block_time = 30_000 # 30 seconds + +disable_bitcoin_explorer = false +disable_stacks_explorer = false +disable_stacks_api = false +``` + +### Port configuration + +Avoid local conflicts by customizing ports: + +```toml +stacks_node_rpc_port = 30443 +stacks_api_port = 4999 +postgres_port = 6432 +stacks_explorer_port = 4020 +``` + +### Mining intervals + +Control block production speed: + +```toml +bitcoin_controller_block_time = 1_000 # Fast development (1 second) +bitcoin_controller_block_time = 30_000 # Standard testing (30 seconds) +bitcoin_controller_block_time = 120_000 # Realistic timing (2 minutes) +``` + +### Custom accounts + +Add accounts with specific balances: + +```toml +[accounts.treasury] +mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin" +balance = 10_000_000_000_000 + +[accounts.alice] +mnemonic = "female adjust gallery certain visit token during great side clown fitness like" +balance = 5_000_000_000_000 +``` + +## Accessing services + +Devnet exposes several ways to interact with the blockchain. + +### Stacks Explorer + +Visit the explorer to browse transactions, blocks, contract state, and account balances: + +``` +http://localhost:8000 +``` + +### API endpoints + +Query blockchain data with the Stacks API: + +```bash +curl http://localhost:3999/v2/info +``` + +Common endpoints: + +* `/v2/info` – network information +* `/v2/accounts/{address}` – account details +* `/v2/contracts/source/{address}/{name}` – contract source code +* `/extended/v1/tx/{txid}` – transaction details + +### Direct RPC + +Submit transactions directly to the Stacks node: + +```bash +curl -X POST http://localhost:20443/v2/transactions \ + -H "Content-Type: application/json" \ + -d @transaction.json +``` + +Useful RPC endpoints: + +* `/v2/transactions` – broadcast transactions +* `/v2/contracts/call-read` – read-only contract calls +* `/v2/fees/transfer` – fee estimates for STX transfers + +## Advanced configuration + +### Performance optimization + +For faster development cycles: + +{% code title="settings/Devnet.toml" %} +```toml +[network.devnet] +bitcoin_controller_block_time = 1_000 + +disable_bitcoin_explorer = true +disable_stacks_explorer = true +disable_stacks_api = false +``` +{% endcode %} + +### Epoch configuration + +Test different Stacks versions: + +```toml +[epochs] +epoch_2_0 = 0 # Stacks 2.0 from genesis +epoch_2_05 = 0 # Stacks 2.05 from genesis +epoch_2_1 = 0 # Stacks 2.1 from genesis +epoch_2_2 = 0 # Pox-2 from genesis +epoch_2_3 = 0 # Pox-3 from genesis +epoch_2_4 = 0 # Pox-4 from genesis +epoch_3_0 = 101 # Nakamoto activation at block 101 +``` + +### Custom node/signer images + +Clarinet runs Devnet with specific tags for each Docker image. For example, Clarinet v3.10.0 uses the following images: + +* stacks node: `blockstack/stacks-blockchain:3.3.0.0.1-alpine` +* stacks signer: `blockstack/stacks-signer:3.3.0.0.1.0-alpine` + +We recommend Devnet users let Clarinet handle it and use the default version. This ensures that your Clarinet version can handle and properly configure the images it uses. + +In some cases, you may need to use other images. Clarinet lets you do this by configuring it in `settings/Devnet.toml`. For example, if you don't want to run the `alpine` images: + +```toml +# setting/Devnet.toml +[network] +name = "devnet" +deployment_fee_rate = 10 + +# ... + +[devnet] +stacks_node_image_url = "blockstack/stacks-blockchain:3.3.0.0.1" +stacks_signer_image_url = "blockstack/stacks-signer:3.3.0.0.1.0" +``` + +
        + +Build an image locally and use it + +* Clone the stacks-core repository (or a fork) and checkout the desired branch. + +``` +git clone git@github.com:stacks-network/stacks-core.git +cd stacks-core +git checkout develop +``` + +* Build the Docker image `stacks-node:local`: + +``` +docker build -t stacks-node:local -f ./Dockerfile ./ +``` + +* Clarinet needs the image to be available in a registry. You can host a local one and push the image to it. + +``` +docker run -d -e REGISTRY_HTTP_ADDR=0.0.0.0:5001 -p 5001:5001 --name registry registry:2 +docker tag stacks-node:local localhost:5001/stacks-node:local +docker push localhost:5001/stacks-node:local +``` + +* Set the image to be used: + +``` +# setting/Devnet.toml +[network] +name = "devnet" +deployment_fee_rate = 10 + +# ... + +[devnet] +stacks_node_image_url = "localhost:5001/stacks-node:local" +``` + +* Then start Devnet: + +``` +clarinet devnet sta +``` + +
        + +### Package deployment + +Create reusable devnet configurations: + +```bash +$ clarinet devnet package --name demo-env +Packaging devnet configuration... +Created demo-env.json +``` + +Use a packaged configuration: + +```bash +$ clarinet devnet start --package demo-env.json +``` + +## Common issues + +
        + +Docker connection errors — “clarinet was unable to create network” + +Follow these steps to fix Docker connection issues: + +Ensure Docker Desktop is running (macOS/Windows).Start the Docker daemon (sudo systemctl start docker) on Linux.Confirm permissions with docker ps.Reset Docker to factory defaults if problems persist. + +Verify Docker status: + +```bash +docker --version +docker ps +``` + +
        + +
        + +Port already in use — “bind: address already in use” + +Find and stop the conflicting process (macOS/Linux): + +```bash +lsof -i :3999 +kill -9 $(lsof -t -i:3999) +``` + +Windows equivalent: + +```bash +netstat -ano | findstr :3999 +taskkill /PID /F +``` + +Or update ports in `settings/Devnet.toml`: + +```toml +stacks_api_port = 4999 +stacks_explorer_port = 4020 +postgres_port = 6432 +``` + +
        + +
        + +High resource usage (slow performance, high CPU or memory) + +Optimizations: + +```toml +disable_bitcoin_explorer = true +disable_stacks_explorer = true +bitcoin_controller_block_time = 60_000 +``` + +Set Docker resource limits: + +```bash +docker update --memory="2g" --cpus="1" +``` + +Clean up old data: + +```bash +clarinet devnet stop +docker system prune -a +rm -rf tmp/devnet +``` + +
        + +
        + +Network already exists — “network with name `.devnet` already exists” + +Remove the orphaned network: + +```bash +docker network rm .devnet +``` + +If you're unsure of the name: + +```bash +docker network ls | grep devnet +docker network rm +``` + +Prevent the issue by stopping devnet with `Ctrl+C` and pruning orphaned networks: + +```bash +docker network prune +``` + +
        + +
        + +Docker stream error during startup — “Fatal: unable to create image: Docker stream error” + +**Error**: "Fatal: unable to create image: Docker stream error" + +This error often occurs when Docker images are corrupted or when explorers fail to start properly. + +**Solution 1 - Disable explorers**: + +If you don't need the web explorers, disable them in `settings/Devnet.toml`: + +``` +disable_bitcoin_explorer = true +disable_stacks_explorer = true +``` + +**Solution 2 - Clean Docker environment**: + +Remove all containers and images, then restart: + +Terminal + +``` +docker stop $(docker ps -a -q) +docker system prune -a +docker volume prune +``` + +**Solution 3 - Full cleanup and restart**: + +Terminal + +``` +docker stop $(docker ps -a -q) +docker network rm .devnet +docker system prune --all --volumes +clarinet devnet start +``` + +This ensures a clean Docker environment for devnet to start fresh. + +
        + +
        + +Contract deployment failures + +Ensure dependencies deploy first in `Clarinet.toml`: + +```toml +[contracts.sip-010-trait] +path = "contracts/sip-010-trait.clar" + +[contracts.token] +path = "contracts/token.clar" +``` + +Validate contracts before deployment: + +```bash +clarinet check +``` + +Check logs: + +```bash +clarinet devnet start --no-dashboard +``` + +Deploy manually if needed: + +```bash +clarinet deployments generate --devnet +clarinet deployments apply --devnet +``` + +
        + +*** + +### Additional Resources + +* \[[Hiro Blog](https://www.hiro.so/blog/5-ways-to-interact-with-devnet-in-the-hiro-platform)] 5 Ways to Interact With Devnet in the Hiro Platform diff --git a/docs/build/clarinet/mainnet-execution-simulation.md b/docs/build/clarinet/mainnet-execution-simulation.md new file mode 100644 index 0000000000..2f8a402f95 --- /dev/null +++ b/docs/build/clarinet/mainnet-execution-simulation.md @@ -0,0 +1,156 @@ +# Mainnet Execution Simulation + +Mainnet execution simulation (MXS) lets you test your Clarity contracts against real mainnet data without deploying experimental code. You can reproduce historical state, validate complex integrations, and debug edge cases while keeping the speed of local development. + +## What you'll learn + +* Set up MXS in a Clarinet project +* Write tests that interact with mainnet contracts +* Simulate historical transactions +* Understand MXS limitations + +## What is Mainnet execution simulation? + +Testing smart contracts in realistic conditions is essential. Simnet offers an isolated environment but lacks the live Stacks mainnet's complexity and history. + +MXS fills this gap by enabling unit tests with the Clarinet JS SDK and Vitest to simulate the Stacks mainnet state at a specific block height. This allows you to: + +* **Validate contract logic with real data:** Directly test mainnet contracts or data within your tests. +* **(Re)simulate transactions:** Analyze mainnet transactions' results, execution, or costs without deploying or using actual STX. + +## Enable MXS in your project + +Add the following configuration to your `Clarinet.toml` file: + +```toml +[repl.remote_data] + +# Enable mainnet execution simulation +enabled = true + +# Specify the Stacks block height to fork from +initial_height = 522000 + +# API URL (optional, defaults to https://api.hiro.so) +api_url = 'https://api.hiro.so' +``` + +{% hint style="info" %} +Pro tip + +Set a specific `initial_height` to keep tests reproducible. +{% endhint %} + +## Using mainnet addresses + +When testing contracts that check or require mainnet addresses, set `use_mainnet_wallets = true`. This enables your simnet tests to use mainnet addresses (SP/SM) instead of testnet addresses (ST). + +```toml +[repl.remote_data] +enabled = true +initial_height = 522000 +use_mainnet_wallets = true # !mark +``` + +This is particularly useful when: + +* Testing against mainnet-only contracts like DEX protocols +* Your contract includes [`(is-standard standard-or-contract-principal)`](mainnet-execution-simulation.md) validation +* Simulating transactions that require mainnet address formats + +## Configure API access + +While MXS works without an API key, you may encounter rate limits. Set up an API key for reliable access: + +```bash +export HIRO_API_KEY="" +``` + +## Write tests with mainnet data + +Once MXS is enabled, your tests automatically operate against the mainnet state snapshot. Here's an example testing against the mainnet `pox-4` contract: + +```ts +import { describe, it, expect } from "vitest"; +import { Cl } from "@stacks/transactions"; + +const accounts = simnet.getAccounts(); +const deployer = accounts.get("deployer")!; + +describe("pox-4 mainnet interaction", () => { + it("reads current reward cycle from mainnet", () => { + // Call the mainnet pox-4 contract + const call = simnet.callReadOnlyFn( + "SP000000000000000000002Q6VF78.pox-4", // Mainnet contract + "current-pox-reward-cycle", + [], + deployer + ); + + // Assert the result (adjust based on your initial_height) + expect(call.result).toBeUint(109); + + console.log("Current POX reward cycle:", Cl.prettyPrint(call.result)); + }); +}); +``` + +The test uses `simnet.callReadOnlyFn` just like in standard unit tests, but because MXS is enabled, it targets the actual `pox-4` contract state at the specified block height. + +## Try it out + +Run your test to see MXS in action: + +```bash +npm run test +``` + +## Common issues + +
        + +Rate limit errors + +Solution: Set up the `HIRO_API_KEY` environment variable. + +```bash +export HIRO_API_KEY="" +``` + +
        + +
        + +Inconsistent results + +Solution: Fix `initial_height` in configuration so tests are run against a reproducible block snapshot. + +
        + +
        + +Function not found + +Solution: Check the contract exists at your block height. + +
        + +## Testing in the playground + +{% stepper %} +{% step %} +**Visit the playground** + +Go to: [https://play.hiro.so/?remote\_data=true](https://play.hiro.so/?remote_data=true) +{% endstep %} + +{% step %} +**Run a mainnet contract call** + +Example Clarity call: + +```clarity +> contract-call? 'SP000000000000000000002Q6VF78.pox-4 current-pox-reward-cycle +``` +{% endstep %} +{% endstepper %} diff --git a/docs/build/clarinet/overview.md b/docs/build/clarinet/overview.md new file mode 100644 index 0000000000..75d05dd246 --- /dev/null +++ b/docs/build/clarinet/overview.md @@ -0,0 +1,98 @@ +--- +description: >- + Clarinet is everything you need to write, test, and deploy Clarity smart + contracts on Stacks. +--- + +# Overview + +Clarinet is a development framework and Clarity runtime packaged as a command line tool, designed to facilitate smart contract understanding, development, testing and deployment. It contains a suite of tools for building, testing, and deploying Clarity smart contracts for the Stacks blockchain. + +Clarinet is the fastest way to build, test, and deploy smart contracts on the Stacks blockchain. It gives you a local devnet, REPL, testing framework, and debugging tools to ship high-quality Clarity code with confidence. + +{% hint style="success" %} +For the latest releases and versions of Clarinet, check out the open-source repo [here](https://github.com/stx-labs/clarinet). +{% endhint %} + +
        + +#### Announcements + +{% updates format="full" %} +{% update date="2025-11-05" %} +## Clarinet was migrated to Stacks Labs + +* The Clarinet repository now belongs to the stx-labs organization. +* The NPM packages are now published under the `@stacks` organization. +* The VSCode extension is now published under the Stacks Labs organization. +* Check out the [announcement](/broken/pages/MJwczGlRXtiBGKy9CNxu) to see how to migrate older projects. +{% endupdate %} +{% endupdates %} + +## Key features + +* [**Leverage a powerful CLI**](https://app.gitbook.com/s/GVj1Z9vMuEOMe7oH7Wnq/clarinet/cli-reference) - Create new projects, manage your smart contracts and their dependencies using clarinet requirements, and interact with your code through the built-in REPL. +* [**Write unit tests with the SDK**](testing-with-clarinet-sdk.md) - Use the Clarinet SDK to write unit tests in a familiar JS environment and validate contract behavior. +* [**Run a private blockchain environment**](local-blockchain-development.md) - Spin up a local devnet with nodes, miners, and APIs so you can test and integrate your code. +* [**VSCode extension**](integrations/clarity-vscode-extension.md) - Linter, step by step debugger, helps writing smart contracts (autocompletion, documentation etc). + +## Installation + +{% tabs %} +{% tab title="Homebrew" %} +```bash +brew install clarinet +``` +{% endtab %} + +{% tab title="Winget" %} +```bash +winget install clarinet +``` +{% endtab %} + +{% tab title="Source" %} +```bash +sudo apt install build-essential pkg-config libssl-dev +git clone https://github.com/stx-labs/clarinet +cd clarinet +cargo clarinet-install +``` +{% endtab %} + +{% tab title="Binary" %} +```bash +wget -nv https://github.com/stx-labs/clarinet/releases/latest/download/clarinet-linux-x64-glibc.tar.gz -O clarinet-linux-x64.tar.gz +tar -xf clarinet-linux-x64.tar.gz +chmod +x ./clarinet +mv ./clarinet /usr/local/bin +``` +{% endtab %} +{% endtabs %} + +## Networks + +Clarinet supports different network types to cater to various development and testing needs. + +| Network | Description | Use case | +| --------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| `simnet` | Optimized for fast feedback loops, introspection, and portability. | Ideal for initial development and unit-testing. | +| `devnet` | Local Stacks and Bitcoin nodes running on Docker for faster feedback loops. | Use for integration tests or local frontend development. | +| `testnet` | A pre-production network that offers a realistic environment for testing. | Ideal for final testing before deploying to Mainnet. | +| `mainnet` | The production network where real transactions occur. | Use when you're ready to deploy your smart contract to production. | + +For a deeper understanding of when to use these networks, check out the dedicated blog post by Hiro [here](https://www.hiro.so/blog/devnet-vs-testnet-vs-mainnet-what-do-they-mean-for-web3-developers). + +*** + +### Additional Resources + +* \[[Hiro Blog](https://www.hiro.so/blog/clarinet-roadmap-looking-to-the-future)] The Humble Beginning of Clarinet + +*** + +{% hint style="info" %} +Help: Need help building with Clarinet? + +Reach out to us on the **#clarinet** channel on [Discord](https://stacks.chat/) under the Developer Tools section. +{% endhint %} diff --git a/docs/build/clarinet/project-development.md b/docs/build/clarinet/project-development.md new file mode 100644 index 0000000000..6c226defca --- /dev/null +++ b/docs/build/clarinet/project-development.md @@ -0,0 +1,149 @@ +# Project Development + +Clarinet streamlines the entire lifecycle of Clarity smart contract development. From project initialization to contract management and code formatting, you'll have the tools needed for professional workflows. + +## Creating a new project + +The `clarinet new` command creates a complete project structure with all necessary configuration files: + +```bash +$ clarinet new my-defi-app +``` + +| Option | Description | Example | +| --------------------- | ------------------------------- | ----------------------------------------- | +| `--disable-telemetry` | Opt out of telemetry collection | `clarinet new my-app --disable-telemetry` | + +For a deeper look at what Clarinet generates, see the [project structure](project-structure.md) guide. + +## Managing contracts + +### Creating new contracts + +The `clarinet contract new` command generates both a contract file and a matching test file: + +```bash +$ clarinet contract new token +Created file contracts/token.clar +Created file tests/token.test.ts +Updated Clarinet.toml +``` + +The generated contract includes a minimal template: + +```clarity +;; token +;; + +;; constants +;; + +;; data vars +;; + +;; data maps +;; + +;; public functions +;; + +;; read only functions +;; + +;; private functions +;; +``` + +### Removing contracts + +Clean up unused contracts with the `rm` command: + +```bash +$ clarinet contract rm old-token +Removed file contracts/old-token.clar +Removed file tests/old-token.test.ts +Updated Clarinet.toml +``` + +## Checking project contract syntax + +Validate your entire project setup: + +```bash +$ clarinet check +✔ 3 contracts checked +``` + +Check specific contracts: + +```bash +$ clarinet check contracts/token.clar +✔ contracts/token.clar Syntax of contract successfully checked +``` + +## Code formatting + +Clarinet includes a formatter to maintain consistent style across your project. + +Format all contracts in your project: + +```bash +$ clarinet format --in-place +Formatted 5 contracts +``` + +### Formatting options + +Customize formatting to match your team's style guide: + +| Option | Description | Example | +| ------------------- | ------------------------------------------------------ | --------------------------------------- | +| `--dry-run` | Preview changes without modifying files | `clarinet format --dry-run` | +| `--in-place` | Replace file contents (required for actual formatting) | `clarinet format --in-place` | +| `--max-line-length` | Set maximum line length | `clarinet format --max-line-length 100` | +| `--indent` | Set indentation size | `clarinet format --indent 2` | +| `--tabs` | Use tabs instead of spaces | `clarinet format --tabs` | + +### Format single files + +```bash +$ clarinet format contracts/messy-contract.clar --in-place +``` + +Format specific contracts with glob patterns: + +```bash +$ clarinet format contracts/token*.clar --in-place +``` + +## Project configuration + +### Working with requirements + +Add mainnet contracts as dependencies: + +```bash +$ clarinet requirements add SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait +Added requirement SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait +Updated Clarinet.toml +``` + +Clarinet adds the dependency to `Clarinet.toml`: + +```toml +[project] +requirements = [ + { contract_id = "SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait" } +] +``` + +You can now implement traits from mainnet contracts: + +```clarity +(impl-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait) + +(define-non-fungible-token my-nft uint) +;; ... implement required functions +``` + +## diff --git a/docs/build/clarinet/project-structure.md b/docs/build/clarinet/project-structure.md new file mode 100644 index 0000000000..fd80458d04 --- /dev/null +++ b/docs/build/clarinet/project-structure.md @@ -0,0 +1,240 @@ +--- +description: Understand the complete structure and configuration of a Clarinet project. +--- + +# Project Structure + +A Clarinet project follows a carefully designed structure that separates contracts, tests, and configuration. Understanding this structure helps you organize code effectively and configure tools for an efficient development workflow. + +## Core project layout + +Every Clarinet project contains these essential directories and files: + +``` +- my-project/ + - .vscode/ + - contracts/ + - main.clar + - trait.clar + - deployments/ + - settings/ + - Devnet.toml + - Mainnet.toml + - Testnet.toml + - tests/ + - main.test.ts + - .gitignore + - Clarinet.toml + - package.json + - tsconfig.json + - vitest.config.js +``` + +Each component serves a specific purpose in your development workflow. The sections below explain how they work together to create a complete development environment. + +## The project manifest + +### Clarinet.toml + +The **Clarinet.toml** file is the heart of your project. It defines project metadata and tracks all contracts: + +```toml +[project] +name = "counter" +description = "A counter smart contract" + +[contracts.traits] +path = "contracts/traits.clar" +clarity_version = 4 +epoch = "latest" + +[contracts.counter] +path = "contracts/counter.clar" +clarity_version = 4 +epoch = "latest" +``` + +The manifest handles several critical functions: + +* **Contract registration**: Every contract must be listed here +* **Stacks epoch and Clarity version**: Specifies Clarity version and epoch for each contract +* **Boot sequence**: Lists contracts to deploy on `clarinet devnet start` + +### Epoch configuration + +You can specify the epoch in two ways: + +```toml +# Use a specific epoch version +epoch = 3.1 +``` + +```toml +# Use the latest available epoch (default) +epoch = "latest" +``` + +Using `"latest"` ensures your contracts always use the newest Clarity features and optimizations available in your version of Clarinet. + +## Testing infrastructure + +### Package configuration + +The **package.json** defines your testing environment and dependencies: + +```json +{ + "name": "counter-tests", + "version": "1.0.0", + "description": "Run unit tests on this project.", + "type": "module", + "private": true, + "scripts": { + "test": "vitest run", + "test:report": "vitest run -- --coverage --costs", + "test:watch": "chokidar \"tests/**/*.ts\" \"contracts/**/*.clar\" -c \"npm run test:report\"" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@stacks/clarinet-sdk": "^3.9.1", + "@stacks/transactions": "^7.2.0", + "@types/node": "^24.4.0", + "chokidar-cli": "^3.0.0", + "vitest": "^4.0.7", + "vitest-environment-clarinet": "^3.0.0" + } +} +``` + +| Package | Purpose | +| ----------------------------- | ------------------------------------------------------- | +| `@stacks/clarinet-sdk` | WebAssembly-compiled Clarinet for Node.js | +| `@stacks/transactions` | Clarity value manipulation in TypeScript | +| `vitest` | Modern testing framework with native TypeScript support | +| `vitest-environment-clarinet` | Simnet bootstrapping for tests | + +### Vitest configuration + +The **vitest.config.js** configures the testing framework: + +```js + +/// + +import { defineConfig } from "vite"; +import { vitestSetupFilePath, getClarinetVitestsArgv } from "@stacks/clarinet-sdk/vitest"; + +export default defineConfig({ + test: { + environment: "clarinet", // use vitest-environment-clarinet + pool: "forks", + poolOptions: { + threads: { singleThread: true }, + forks: { singleFork: true }, + }, + setupFiles: [ + vitestSetupFilePath, + // custom setup files can be added here + ], + environmentOptions: { + clarinet: { + ...getClarinetVitestsArgv(), + // add or override options + }, + }, + }, +}); +``` + +This configuration enables: + +* **Clarinet environment**: Automatic `simnet` setup for each test +* **Single fork mode**: Efficient test execution with proper isolation +* **Coverage tracking**: Generate reports in multiple formats +* **Custom setup**: Add project-specific test utilities + +### TypeScript configuration + +The **tsconfig.json** provides TypeScript support: + +```json +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext"], + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + "strict": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": [ + "node_modules/@stacks/clarinet-sdk/vitest-helpers/src", + "tests" + ] +} +``` + +Properly setting the `include` property ensures TypeScript picks up the helpers defined in the Clarinet SDK package along with your tests. + +## Network configurations + +### Environment settings + +Each network has its own configuration file in the **settings** directory: + +```toml +[network] +name = "devnet" +deployment_fee_rate = 10 + +[accounts.deployer] +mnemonic = "twice kind fence tip hidden..." +balance = 100_000_000_000_000 + +[accounts.wallet_1] +mnemonic = "sell invite acquire kitten..." +balance = 10_000_000_000_000 +``` + +These settings control: + +* **Network ports**: API, RPC, and explorer endpoints +* **Account configuration**: Test wallets with STX balances +* **Chain parameters**: Network-specific blockchain settings + +{% hint style="warning" %} +Never commit mainnet private keys or mnemonics. Use environment variables for production credentials. +{% endhint %} + +## Common issues + +
        + +Imports failing in tests + +If you're encountering import errors in your tests, update your TypeScript configuration to use Vite's bundler resolution: + +```json +{ + "compilerOptions": { + "moduleResolution": "bundler", + "allowImportingTsExtensions": true + } +} +``` + +This configuration ensures TypeScript understands Vite's module resolution strategy and allows importing `.ts` files directly. + +
        diff --git a/docs/build/clarinet/quickstart.md b/docs/build/clarinet/quickstart.md new file mode 100644 index 0000000000..7fa0907f30 --- /dev/null +++ b/docs/build/clarinet/quickstart.md @@ -0,0 +1,141 @@ +--- +description: >- + In this guide, you'll build a simple counter smart contract and interact with + it in a local environment. +--- + +# Quickstart + +## What you'll learn + +* Create a Clarity smart contract project +* Write Clarity code with maps and public functions +* Test and validate your contracts using Clarinet's console + +## Prerequisites + +* Clarinet installed on your machine. Follow the [installation guide](overview.md#installation) if needed. +* A code editor like VS Code for editing Clarity files. + +{% stepper %} +{% step %} +#### Create your project + +Let's start by creating a new Clarinet project. The `clarinet new` command sets up everything you need for smart contract development, including a testing framework, deployment configurations, and a local development environment: + +```bash +clarinet new counter +``` + +Clarinet creates a complete project structure for you. Each folder serves a specific purpose in your development workflow: + +``` +- counter/ + - contracts/ + - settings/ + - Devnet.toml + - Mainnet.toml + - Testnet.toml + - tests/ + - Clarinet.toml + - package.json + - vitest.config.js +``` +{% endstep %} + +{% step %} +#### Generate your contract + +Now that we have our project structure, let's create a smart contract. Navigate into your project directory and use Clarinet's contract generator: + +```bash +$ cd counter +$ clarinet contract new counter +Created file contracts/counter.clar +Created file tests/counter.test.ts +Updated Clarinet.toml with contract counter +``` + +Clarinet automatically creates both your contract file and a corresponding test file. This follows the best practice of writing tests alongside your contract code: + +| File | Purpose | +| ------------------------ | --------------------------- | +| `contracts/counter.clar` | Your smart contract code | +| `tests/counter.test.ts` | Test file for your contract | + +{% hint style="info" %} +Notice that Clarinet also updated your `Clarinet.toml` file. This configuration file tracks all contracts in your project and their deployment settings. +{% endhint %} +{% endstep %} + +{% step %} +#### Write your contract code + +Open `contracts/counter.clar` and replace its contents with our counter implementation. This contract will maintain a separate count for each user who interacts with it: + +{% code title="contracts/counter.clar" %} +```lisp +;; Define a map to store counts for each user +(define-map counters principal uint) + +;; Increment the count for the caller +(define-public (count-up) + (ok (map-set counters tx-sender (+ (get-count tx-sender) u1))) +) + +;; Get the current count for a user +(define-read-only (get-count (who principal)) + (default-to u0 (map-get? counters who)) +) +``` +{% endcode %} + +Let's understand what each part does: + +* `define-map` creates a persistent storage map that associates each user (principal) with their count +* `tx-sender` is a built-in variable that contains the address of whoever calls the function +* `define-public` declares functions that can modify contract state +* `define-read-only` declares functions that only read data without modifying it +{% endstep %} + +{% step %} +#### Validate your contract + +Before we can test our contract, let's make sure it's syntactically correct and type-safe. Clarinet's check command analyzes your contract without deploying it: + +```bash +clarinet check +``` + +If you see errors instead, here are the most common issues and how to fix them: + +| Error | Fix | +| --------------------- | ----------------------------------------------------------------------------- | +| `Unknown keyword` | Check spelling of Clarity functions | +| `Type mismatch` | Ensure you're using correct types (uint, principal, etc.) | +| `Unresolved contract` | Verify contract name in `Clarinet.toml` matches the contract name in the file | +{% endstep %} + +{% step %} +#### Test in the console + +Now for the exciting part—let's interact with our contract! Clarinet provides an interactive console where you can call functions and see results immediately. Start the console with: + +```bash +clarinet console +``` + +Once the console loads, you can call your contract functions directly. Here are a few examples you can try: + +```lisp +$ (contract-call? .counter count-up) +(ok true) +$ (contract-call? .counter get-count tx-sender) +u1 +$ (contract-call? .counter count-up) +(ok true) +$ (contract-call? .counter get-count tx-sender) +u2 +``` +{% endstep %} +{% endstepper %} diff --git a/docs/build/clarinet/testing-with-clarinet-sdk.md b/docs/build/clarinet/testing-with-clarinet-sdk.md new file mode 100644 index 0000000000..a95ea31497 --- /dev/null +++ b/docs/build/clarinet/testing-with-clarinet-sdk.md @@ -0,0 +1,448 @@ +--- +description: Practical guide to testing smart contracts with the Clarinet JS SDK. +--- + +# Unit Testing + +
        + +Unit testing verifies that individual contract functions behave as expected. The Clarinet JS SDK pairs with Vitest to help you catch bugs early in development. The Clarinet JS SDK provides a powerful testing framework for Clarity smart contracts. It integrates with Vitest to let you run comprehensive tests against a simulated blockchain environment. This library will expose many of Clarinet’s features to the JavaScript ecosystem, including “simnet[^1]”. + +{% hint style="success" %} +For the latest releases and versions of `@stacks/clarinet-sdk`, check out its npm page [here](https://www.npmjs.com/package/@stacks/clarinet-sdk). + +* For the [SDK](https://app.gitbook.com/s/GVj1Z9vMuEOMe7oH7Wnq/clarinet-js-sdk/sdk-reference) references of all available methods and definitions. +* For the [Browser SDK](https://app.gitbook.com/s/GVj1Z9vMuEOMe7oH7Wnq/clarinet-js-sdk/browser-sdk-reference) reference, which is a browser build of the Clarinet SDK lets you interact with simnet directly from web experiences. +{% endhint %} + +## What you'll learn + +* Set up a Clarinet project for unit testing +* Write tests for public and read-only functions +* Handle error conditions and edge cases +* Run tests and generate coverage reports + +{% stepper %} +{% step %} +**Set up your project** + +Create a new Clarinet project and install dependencies: + +```bash +clarinet new stx-defi +cd stx-defi +npm install +``` +{% endstep %} + +{% step %} +**Create the contract** + +Generate a contract stub: + +```bash +clarinet contract new defi +``` + +Replace `contracts/defi.clar` with the following implementation: + +{% code title="defi.clar" expandable="true" %} +```clarity +;; Holds the total amount of deposits in the contract +(define-data-var total-deposits uint u0) + +;; Maps a user's principal address to their deposited amount +(define-map deposits { owner: principal } { amount: uint }) + +;; Public function for users to deposit STX into the contract +(define-public (deposit (amount uint)) + (let + ( + ;; Fetch the current balance or default to 0 if none exists + (current-balance (default-to u0 (get amount (map-get? deposits { owner: tx-sender })))) + ) + ;; Transfer the STX from sender to contract + (try! (stx-transfer? amount tx-sender (as-contract tx-sender))) + ;; Update the user's deposit amount in the map + (map-set deposits { owner: tx-sender } { amount: (+ current-balance amount) }) + ;; Update the total deposits variable + (var-set total-deposits (+ (var-get total-deposits) amount)) + ;; Return success + (ok true) + ) +) + +;; Read-only function to get the balance by tx-sender +(define-read-only (get-balance-by-sender) + (ok (map-get? deposits { owner: tx-sender })) +) +``` +{% endcode %} + +Validate the contract: + +```bash +clarinet check +``` +{% endstep %} + +{% step %} +**Write your unit test** + +Create `tests/defi.test.ts` with a basic happy-path test: + +{% hint style="warning" %} +Be sure you're on the latest version of `@stacks/clarinet-sdk` +{% endhint %} + +{% code title="tests/defi.test.ts" expandable="true" %} +```ts +import { describe, it, expect } from 'vitest'; +import { Cl } from '@stacks/transactions'; + +const accounts = simnet.getAccounts(); +const wallet1 = accounts.get('wallet_1')!; + +describe('stx-defi', () => { + it('allows users to deposit STX', () => { + // Define the amount to deposit + const amount = 1000; + + // Call the deposit function + const deposit = simnet.callPublicFn('defi', 'deposit', [Cl.uint(amount)], wallet1); + + // Assert the deposit was successful + expect(deposit.result).toBeOk(Cl.bool(true)); + + // Verify the contract's total deposits + const totalDeposits = simnet.getDataVar('defi', 'total-deposits'); + expect(totalDeposits).toBeUint(amount); + + // Check the user's balance + const balance = simnet.callReadOnlyFn('defi', 'get-balance-by-sender', [], wallet1); + expect(balance.result).toBeOk( + Cl.some( + Cl.tuple({ + amount: Cl.uint(amount), + }) + ) + ); + }); +}); +``` +{% endcode %} + +The `simnet` object is automatically available and exposes a simulated Stacks blockchain. + +{% hint style="info" %} +Key calls: + +* `simnet.callPublicFn` – executes public functions +* `expect(...).toBeOk()` – asserts a Clarity `ok` response +* `simnet.getDataVar` – reads a contract data variable +* `Cl.*` helpers – build Clarity values in JavaScript +{% endhint %} +{% endstep %} + +{% step %} +**Try it out** + +Run the tests: + +```bash +$ npm run test + PASS tests/defi.test.ts + stx-defi + allows users to deposit STX (5 ms) + +Test Files 1 passed (1) + Tests 1 passed (1) +``` +{% endstep %} + +{% step %} +**Generate coverage reports** + +```bash +npm run test:report +``` + +This command produces: + +* `lcov.info` – code coverage data +* `costs-reports.json` – gas cost analysis + +View the HTML report (macOS example): + +```bash +$ brew install lcov +$ genhtml lcov.info --branch-coverage -o coverage +$ open coverage/index.html +``` +{% endstep %} +{% endstepper %} + +## Configuration options + +### Clarinet configuration + +Define your contracts in `Clarinet.toml`: + +```toml +[project] +name = "my-project" + +[contracts.counter] +path = "contracts/counter.clar" +``` + +### TypeScript setup + +Configure TypeScript for the SDK: + +```json +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "node", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "types": ["vitest/globals"] + }, + "include": ["tests/**/*.ts"], + "exclude": ["node_modules"] +} +``` + +### Vitest configuration + +Set up Vitest so the SDK can bootstrap the testing environment: + +```js +import { defineConfig } from "vitest/config"; +import { vitestSetupFilePath } from "@stacks/clarinet-sdk/vitest"; + +export default defineConfig({ + test: { + environment: "node", + globals: true, + setupFiles: [vitestSetupFilePath], + }, +}); +``` + +{% hint style="info" %} +Include `vitestSetupFilePath` in `setupFiles` so the SDK can prepare the `simnet` instance before tests run. +{% endhint %} + +### Package scripts + +Add convenient test scripts to `package.json`: + +```json +"scripts": { + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage" +} +``` + +## Common patterns + +### Testing read-only functions + +Use `callReadOnlyFn` for functions that do not modify state: + +```ts +const getCountCall = simnet.callReadOnlyFn( + "counter", + "get-count", + [Cl.principal(wallet)], + wallet +); +expect(getCountCall.result).toBeUint(1); +``` + +### Testing public functions with parameters + +Pass parameters with the appropriate Clarity helpers: + +```ts +const depositCall = simnet.callPublicFn( + "defi", + "deposit", + [Cl.uint(1000)], + wallet +); +expect(depositCall.result).toBeOk(Cl.bool(true)); +``` + +### Accessing contract state + +Inspect data variables and maps directly: + +```ts +const totalDeposits = simnet.getDataVar("defi", "total-deposits"); +expect(totalDeposits).toBeUint(1000); + +const balance = simnet.getMapEntry("defi", "balances", Cl.principal(wallet)); +expect(balance).toBeUint(1000); +``` + +## Other Examples + +### Testing contract deployment + +Ensure the contract was deployed: + +```ts +it("ensures the contract is deployed", () => { + const contractSource = simnet.getContractSource("counter"); + expect(contractSource).toBeDefined(); +}); +``` + +### Testing error conditions + +Verify error handling logic: + +```ts +it("fails when borrowing too much", () => { + const borrowCall = simnet.callPublicFn( + "defi", + "borrow", + [Cl.uint(10000)], // Amount exceeds allowed + wallet + ); + expect(borrowCall.result).toBeErr(Cl.uint(300)); // err-overborrow +}); +``` + +### Testing with multiple accounts + +Simulate cross-account interactions: + +```ts +const wallet1 = accounts.get("wallet_1")!; +const wallet2 = accounts.get("wallet_2")!; + +// Wallet 1 deposits +simnet.callPublicFn("defi", "deposit", [Cl.uint(1000)], wallet1); + +// Wallet 2 tries to withdraw wallet 1's funds (should fail) +const withdrawCall = simnet.callPublicFn( + "defi", + "withdraw", + [Cl.uint(1000)], + wallet2 +); +expect(withdrawCall.result).toBeErr(Cl.uint(401)); // err-unauthorized +``` + +## Running tests + +Execute the full suite: + +```bash +npm run test +``` + +Generate coverage reports: + +```bash +npm run test:report +``` + +This command produces: + +* `lcov.info` – code coverage data +* `costs-reports.json` – gas cost analysis + +View the HTML report (macOS example): + +```bash +brew install lcov +genhtml lcov.info --branch-coverage -o coverage +open coverage/index.html +``` + +## Advanced usage + +### Using the SDK in existing projects + +If your project has a custom structure, keep contracts and tests together and point the SDK to the manifest: + +``` +- my-app/ + - blockchain/ + - contracts/ + - token.clar + - tests/ + - token.test.ts + - Clarinet.toml + - frontend/ + - App.tsx + - package.json + - vitest.config.js +``` + +Update `vitest.config.js` to reference the correct manifest path: + +```js +export default defineConfig({ + test: { + environment: "node", + globals: true, + setupFiles: [vitestSetupFilePath], + env: { + CLARINET_MANIFEST_PATH: "./blockchain/Clarinet.toml" + } + }, +}); +``` + +## Advanced testing patterns + +Test error conditions: + +```ts +it('fails when depositing zero', () => { + const deposit = simnet.callPublicFn('defi', 'deposit', [Cl.uint(0)], wallet1); + expect(deposit.result).toBeErr(Cl.uint(100)); // err-invalid-amount +}); +``` + +Test with multiple accounts: + +```ts +const wallet2 = accounts.get('wallet_2')!; + +it('tracks deposits from multiple users', () => { + simnet.callPublicFn('defi', 'deposit', [Cl.uint(1000)], wallet1); + simnet.callPublicFn('defi', 'deposit', [Cl.uint(2000)], wallet2); + + const total = simnet.getDataVar('defi', 'total-deposits'); + expect(total).toBeUint(3000); +}); +``` + +## Common issues + +| Issue | Solution | +| ----------------------------- | --------------------------------------------------------------------- | +| `toBeOk is not a function` | Ensure the Clarinet SDK matchers are loaded via `vitestSetupFilePath` | +| `Contract not found` | Re-run `clarinet check` to compile and register contracts | +| Type errors | Use the `Cl` helpers to construct Clarity values | +| State pollution between tests | Each test runs in isolation - state doesn't carry over | +| Timing issues | Use `simnet.mineEmptyBlocks()` to advance block height | +| Complex assertions | Break down into smaller, focused tests | + +*** + +### Additional Resources + +* \[[Hiro Blog](https://www.hiro.so/blog/announcing-the-clarinet-sdk-a-javascript-programming-model-for-easy-smart-contract-testing)] Announcing the Clarinet SDK: a JavaScript Programming Model For Easy Smart Contract Testing + +[^1]: Simnet allows developers to interact with a virtual and light Stacks network, embedding the same Clarity VM as the one used in production on Stacks mainnet. With this new JavaScript SDK, it is now possible to control the simnet from Node.js in a standard and familiar way. diff --git a/docs/build/clarinet/validation-and-analysis.md b/docs/build/clarinet/validation-and-analysis.md new file mode 100644 index 0000000000..2ebd193a59 --- /dev/null +++ b/docs/build/clarinet/validation-and-analysis.md @@ -0,0 +1,241 @@ +# Validation and Analysis + +Clarinet provides powerful tools for validating, analyzing, and debugging your smart contracts. From static type checking to real-time cost analysis, you can ensure your contracts are correct and efficient before deployment. + +Contract validation spans static analysis, runtime debugging, and cost optimization. Each discipline helps you gain confidence in contract behavior. + +## Understanding contract validation + +**Static analysis vs. runtime debugging** + +| Static analysis | Runtime debugging | +| --------------------------------------- | -------------------------------------- | +| Catches issues before deployment | Reveals behavior during execution | +| Flags type mismatches and syntax errors | Shows actual execution costs | +| Ensures trait compliance | Exposes state changes and side effects | +| Detects undefined variables | Highlights transaction flow | +| Validates function signatures | Surfaces performance bottlenecks | + +## Static analysis + +Run comprehensive validation with `clarinet check`: + +```bash +clarinet check +``` + +Successful output resembles: + +``` +✔ 3 contracts checked +``` + +When validation fails, Clarinet provides detailed diagnostics: + +``` +✖ 1 error detected + +Error in contracts/token.clar:15:10 + | +15| (ok (+ balance amount)) + | ^^^^^^^ + | + = Type error: expected uint, found (response uint uint) +``` + +{% stepper %} +{% step %} +#### Run basic checks + +Use `clarinet check` to validate your contracts and catch type/syntax errors before deployment. + +```bash +clarinet check +``` +{% endstep %} + +{% step %} +#### Check a specific contract + +Focus validation during development on a single contract file: + +```bash +clarinet check contracts/nft.clar +``` +{% endstep %} + +{% step %} +#### Integrate into CI + +Automate validation in continuous integration pipelines. Example GitHub Actions workflow: + +```yaml +name: Contract Validation +on: [push, pull_request] + +jobs: + sanity-checks: + runs-on: ubuntu-latest + container: ghcr.io/stx-labs/clarinet:latest + steps: + - uses: actions/checkout@v4 + - name: Check Clarity contracts check + run: clarinet check --use-on-disk-deployment-plan + - name: Check Clarity contracts format + run: clarinet fmt --check +``` +{% endstep %} +{% endstepper %} + +**Validation scope** + +Clarinet validates multiple aspects of your contracts: + +| Validation type | What it checks | +| ------------------------ | -------------------------------------------------- | +| **Type safety** | Function parameters, return values, variable types | +| **Trait compliance** | Implementation matches trait definitions | +| **Response consistency** | `ok`/`err` branches return the same types | +| **Variable scope** | Variables defined before use | +| **Function visibility** | Proper use of public, private, and read-only | + +## Runtime analysis + +The Clarinet console offers runtime tools that help you inspect behavior during execution. + +### Cost analysis with `::toggle_costs` + +Enable automatic cost display after every expression: + +```clarity +::toggle_costs +;; Always show costs: true + +(contract-call? .counter count-up) +;; +----------------------+----------+------------+------------+ +;; | | Consumed | Limit | Percentage | +;; |----------------------+----------+------------+------------| +;; | Runtime | 4775 | 5000000000 | 0.00 % | +;; | Read count | 5 | 15000 | 0.03 % | +;; | Read length (bytes) | 268 | 100000000 | 0.00 % | +;; | Write count | 1 | 15000 | 0.01 % | +;; | Write length (bytes) | 41 | 15000000 | 0.00 % | +;; +----------------------+----------+------------+------------+ +;; (ok true) +``` + +### Execution tracing with `::trace` + +Trace function calls to understand execution flow: + +```clarity +::trace (contract-call? .defi-pool swap u100 'token-a 'token-b) +;; (contract-call? .defi-pool swap u100 'token-a 'token-b) +;; ( get-pool-balance 'token-a ) defi-pool:15:8 +;; ↳ args: 'token-a +;; u50000 +;; ( get-pool-balance 'token-b ) defi-pool:16:8 +;; ↳ args: 'token-b +;; u75000 +;; ( calculate-output u100 u50000 u75000 ) defi-pool:18:12 +;; ↳ args: u100, u50000, u75000 +;; u149 +;; (ok u149) +``` + +### Interactive debugging with `::debug` + +Set breakpoints and step through execution: + +```clarity +::debug (contract-call? .complex-contract process-batch) +break validate-input +;; Breakpoint set at validate-input +continue +;; Hit breakpoint at validate-input:23 +``` + +Common navigation commands: + +{% hint style="info" %} +* `step` or `s` – step into subexpressions +* `finish` or `f` – complete the current expression +* `next` or `n` – step over subexpressions +* `continue` or `c` – resume execution +{% endhint %} + +### Using `::get_costs` for targeted analysis + +```clarity +::get_costs (contract-call? .defi-pool add-liquidity u1000 u1000) +;; +----------------------+----------+------------+------------+ +;; | | Consumed | Limit | Percentage | +;; |----------------------+----------+------------+------------| +;; | Runtime | 12250 | 5000000000 | 0.00 % | +;; | Read count | 6 | 15000 | 0.04 % | +;; | Read length (bytes) | 192 | 100000000 | 0.00 % | +;; | Write count | 3 | 15000 | 0.02 % | +;; | Write length (bytes) | 96 | 15000000 | 0.00 % | +;; +----------------------+----------+------------+------------+ +;; (ok {lp-tokens: u1000}) +``` + +### Spotting costly operations with `::trace` + +```clarity +::trace (contract-call? .complex-algo process-large-dataset) +``` + +Review the trace for loops with high iteration counts, nested map/filter operations, repeated contract calls, and large data structure manipulations. + +## Debugging workflows + +Master interactive debugging to identify issues quickly: + +```clarity +::debug (contract-call? .counter count-up) +;; ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter:9:3 +;; 6 +;; 7 ;; Increment the count for the caller +;; 8 (define-public (count-up) +;; ->9 (ok (map-set counters tx-sender (+ (get-count tx-sender) u1))) +;; ^ +;; 10 ) +``` + +### Analyzing failed transactions with `::trace` + +```clarity +::trace (contract-call? .marketplace purchase u999) +;; (contract-call? .marketplace purchase u999) +;; ( get-listing u999 ) marketplace:45:12 +;; ↳ args: u999 +;; none +;; (err u404) # Listing not found +``` + +### Using `::encode` and `::decode` for inspection + +```clarity +::encode { id: u1, active: true } +;; 0c0000000206616374697665030269640100000000000000000000000000000001 + +::decode 0d0000000b48656c6c6f20776f726c64 +;; "Hello world" +``` + +### Testing time-dependent logic + +```clarity +::get_block_height +;; Current block height: 4 + +::advance_chain_tip 100 +;; new burn height: 3 +;; new stacks height: 104 + +(contract-call? .vesting claim) +;; (ok {claimed: u2500, remaining: u7500}) +``` + +## diff --git a/docs/build/get-started/build-a-frontend/README.md b/docs/build/get-started/build-a-frontend/README.md new file mode 100644 index 0000000000..a3aa67410b --- /dev/null +++ b/docs/build/get-started/build-a-frontend/README.md @@ -0,0 +1,25 @@ +--- +description: Interact with your contracts with a proper frontend app +--- + +# Build a Frontend + +

        source: Hiro Blog

        + +A major part of building full-stack Stacks applications is creating a well designed UI with a solid UX. One of your primary tools for this is stacks.js, a JavaScript/Typescript library that simplifies working with contracts, wallets, and the Stacks network. + +{% hint style="info" %} +This section assumes you have basic knowledge in front-end development and bootstrapping frontend frameworks. +{% endhint %} + +### Components to your Stacks frontend app + +* [Authentication](authentication.md) +* [Post-Conditions](post-conditions.md) +* [Sending Transactions](sending-transactions.md) + +### Additional Resources + +* \[[Hiro Blog](https://www.hiro.so/blog/rich-app-templates-in-the-hiro-platform)] Rich App Templates in the Hiro Platform: Accelerating Web3 Development +* \[[Hiro Blog](https://www.hiro.so/blog/lean-devops-strategies-for-your-web3-project)] Lean DevOps Strategies for Your Web3 Project +* \[[Hiro Blog](https://www.hiro.so/blog/introducing-stacks-js-starters-launch-a-frontend-in-just-a-few-clicks)] Introducing Stacks.js Starters: Launch a Frontend in Just a Few Clicks diff --git a/docs/build/get-started/build-a-frontend/authentication.md b/docs/build/get-started/build-a-frontend/authentication.md new file mode 100644 index 0000000000..1d82697bf2 --- /dev/null +++ b/docs/build/get-started/build-a-frontend/authentication.md @@ -0,0 +1,69 @@ +# Authentication + +

        source: Hiro Blog

        + +Authenticating (connecting wallets) with a Stacks-supported wallet is a common task when building Stacks apps. On a web2 app, authentication usually means sending credentials to a central provider, which then verifies you and controls access. In web3, authentication uses a wallet and cryptographic signatures with libraries like stacks.js, letting users prove identity without a central party holding the keys. + +{% hint style="info" %} +To learn more about how wallets and accounts work with Stacks, check out this [section](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/network-fundamentals/accounts) in Learn. +{% endhint %} + +Below is a simple example showing how to set up front-end authentication with `@stacks/connect` and access user data in the UI. The stacks.js monorepo contains several underlying packages specific to different use cases. The package `@stacks/connect` is the main connectivity package used in Stacks. + +## Authentication on the frontend + +Using `@stacks/connect` on the frontend will allow our frontend app to authenticate wallets, call our contract functions, and interact with the Stacks network. + +In the snippet below, you'll notice we have 3 functions setup to handle `connectWallet` , `disconnectWallet`, and for `getBns` . All 3 functions will be integral in how we want to display the 'Connect' and 'Disconnect' button in the UI. + +{% hint style="info" %} +Retrieving a wallet account's associated [BNS](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/network-fundamentals/bitcoin-name-system) is a staple of Stacks and for web3 identity. Check out [BNSv2](https://www.bnsv2.com/) for more information and for availably public API endpoints you could use. +{% endhint %} + +
        import { connect, disconnect } from '@stacks/connect'
        +import type { GetAddressesResult } from '@stacks/connect/dist/types/methods'
        +import { useState } from 'react'
        +
        +function App() {
        +  let [isConnected, setIsConnected] = useState<boolean>(false)
        +  let [walletInfo, setWalletInfo] = useState<any>(null)
        +  let [bns, setBns] = useState<string>('')
        +
        +  async function connectWallet() {
        +    let connectionResponse: GetAddressesResult = await connect()
        +    let bnsName = await getBns(connectionResponse.addresses[2].address)
        +
        +    setIsConnected(true)
        +    setWalletInfo(connectionResponse)
        +    setBns(bnsName)
        +  }
        +
        +  async function disconnectWallet() {
        +    disconnect();
        +  }
        +  
        +  async function getBns(stxAddress: string) {
        +    let response = await fetch(`https://api.bnsv2.com/testnet/names/address/${stxAddress}/valid`)
        +    let data = await response.json()
        +
        +    return data.names[0].full_name
        +  }
        +  
        +  return (
        +    <>
        +      <h3>Stacks Dev Quickstart Message Board</h3>
        +      {isConnected ? (
        +        <button onClick={disconnectWallet}>{
        +          bns ? bns : walletInfo.addresses[2].address
        +        }</button>
        +      ) : (
        +        <button onClick={connectWallet}>connect wallet</button>
        +      )}
        +    </>
        +  )
        +}
        +
        + +The `connect()` method comes with the ability to configure how you want the wallet selector modal to appear for your app. You can decide which wallets to have only appear as an option or allow any wallet that follows the SIP-030 standard to appear as an available Stacks wallet. + +For the complete guides check out the [Stacks Connect](/broken/pages/JLRpUuDHPMxXaInZ9Vll) section. diff --git a/docs/build/get-started/build-a-frontend/post-conditions.md b/docs/build/get-started/build-a-frontend/post-conditions.md new file mode 100644 index 0000000000..ea986b6436 --- /dev/null +++ b/docs/build/get-started/build-a-frontend/post-conditions.md @@ -0,0 +1,107 @@ +--- +description: A unique security mechanism on Stacks to protect user funds +--- + +# Post-Conditions + +### What are post-conditions? + +Post-conditions are assertions about an on-chain transaction that must be met; otherwise, the transaction will abort during execution. In other words, post-conditions act as a safety net, allowing you to specify what state changes can occur in a transaction. This logic helps limit the amount of damage that can be done to a user and their assets, whether due to a bug or malicious behavior. + +Post conditions are an additional safety feature built into the Stacks chain itself that help to protect end users. Rather than being a function of Clarity smart contracts, they are implemented on the client side and meant to be an additional failsafe against malicious contracts. + +Put simply, post conditions are a set of conditions that must be met before a user's transaction will execute. The primary goal behind post conditions is to limit the amount of damage that can be done to a user's assets due to a bug, intentional or otherwise. + +They are sent as part of the transaction when the user initiates it, meaning we need to implement post-conditions on the frontend. Whenever you are transferring an asset (fungible or non-fungible) from one address to another, you should take advantage of post conditions. + +{% hint style="info" %} +Head to the dedicated section on [post-conditions](/broken/pages/0KPrPPKItMGZZL2u4tiF) for more in-depth information. +{% endhint %} + +We're going to use [stacks.js](https://github.com/stx-labs/stacks.js/tree/master/packages/transactions#post-conditions) to familiarize ourselves with constructing post-conditions on the frontend. And there are several different ways to construct post-conditions based on asset type. + +### Simple example + +In this example, we are attached a post-condition statement to a contract call transaction to protect the user's assets in the event the contract is malicious. + +
        import { request } from '@stacks/connect'
        +import type { TransactionResult } from '@stacks/connect/dist/types/methods'
        +import { Pc } from '@stacks/transactions'
        +
        +let postCond_1 = Pc.principal('ST11V9ZN6E6VG72SHMAVM9GDE30VD3VGW5Q1W9WX3')
        +  .willSendEq(1)
        +  .ft('ST1F7QA2MDF17S807EPA36TSS8AMEFY4KA9TVGWXT.sbtc-token', 'sbtc-token')
        +
        +let result: TransactionResult = await request('stx_callContract', {
        +  // ...
        +  postConditions: [postCond_1],
        +  postConditionMode: 'deny',
        +  // ...
        +})
        +
        + +Let's walkthrough the example above line-by-line to see what's happening when you attach a post-condition statement to a transaction. + +{% stepper %} +{% step %} +#### Declare post-condition statement + +```typescript +let postCond_1 = Pc + // Specify who the sender of the expected sBTC transfer will originate from + .principal('ST11V9ZN6E6VG72SHMAVM9GDE30VD3VGW5Q1W9WX3') + // Specify the equality operator of the amount expected to be sent + .willSendEq(1) + // Specify the fungible token’s contract principal and asset name + .ft('ST1F7QA2MDF17S807EPA36TSS8AMEFY4KA9TVGWXT.sbtc-token', 'sbtc-token') +``` + +In this statement, you are declaring that the principal (usually the user that'll call this function) `ST11V9ZN6E6VG72SHMAVM9GDE30VD3VGW5Q1W9WX3` should only expect to send out _exactly_ 1 satoshi of sBTC during the execution of this contract call transaction. +{% endstep %} + +{% step %} +#### Include post-condition statement to transaction params + +
        let result: TransactionResult = await request('stx_callContract', {
        +  // ...
        +  postConditions: [postCond_1],
        +  // ...
        +})
        +
        + +The transaction param of `postConditions` accepts an array of different post-condition statements. This means you can declare many post-condition statements of what the user should expect to happen during the execution of the transaction. This include any asset transfers coming from the user or from a contract. +{% endstep %} + +{% step %} +#### Determine post-condition mode + +The other related transaction param of `postConditionMode` is a special setting that is useful when you want to deal with other unexpected/unforeseen asset transfer events that the developer or user may not be aware of. + +
        let result: TransactionResult = await request('stx_callContract', {
        +  // ...
        +  postConditions: [postCond_1],
        +  postConditionMode: 'deny',
        +  // ...
        +})
        +
        + +By setting the `postConditionMode` to `deny` we are stating that if any other asset transfers, besides the ones we've declared, happen during the execution of the transaction, then force the entire transaction to fail. +{% endstep %} +{% endstepper %} + +### How post-conditions appear to the user + +Since post-conditions are declared on your frontend code, they also need to be visually displayed to users. Stacks-supported wallets handle that by displaying post-conditions on the transaction confirmation modals that popup when a user needs to confirm/approve a transaction. + +

        Post-conditions when appeared in a wallet's transaction confirmation modal

        + +After transaction confirmation and broadcasting, users are also able to see what post-conditions were set in their transaction on the transaction page of the Stacks Explorer. This gives users and developers more confidence in analyzing transactions. + +

        Analyze post-conditions set in transactions on the transactions page of the Stacks Explorer

        + +*** + +### Additional Resources + +* \[[Post-Conditions](/broken/pages/0KPrPPKItMGZZL2u4tiF)] Dedicated section on post-conditions in these docs +* \[[Hiro YT](https://youtu.be/xXgQB8NfdEY?si=eSZp4tlLOCkkqGRS)] ELI5: Post-Conditions on Stacks diff --git a/docs/build/get-started/build-a-frontend/sending-transactions.md b/docs/build/get-started/build-a-frontend/sending-transactions.md new file mode 100644 index 0000000000..ff55ffeea3 --- /dev/null +++ b/docs/build/get-started/build-a-frontend/sending-transactions.md @@ -0,0 +1,88 @@ +# Sending Transactions + +
        + +Any Stacks app is going to require sending transactions at some point, so how do we do that? + +{% hint style="warning" %} +When you send Stacks transactions, don't forget to utilize post-conditions. +{% endhint %} + +### Invoking contract call transactions + +In our example below, we're going to be showing how to invoke a contract function from the frontend which will prompt a user's wallet to confirm transaction. For this, we'll setup a `stx_callContract` to invoke the `add-message` public function of our contract taken from the [Developer Quickstart](../developer-quickstart.md). This function will accept a string content to be passed into our contract call. + +
        import { request } from '@stacks/connect'
        +import type { TransactionResult } from '@stacks/connect/dist/types/methods'
        +import { Cl, Pc } from '@stacks/transactions'
        +import { useState } from 'react'
        +
        +function App() {
        +  // ...
        +  let [content, setContent] = useState<string>('')
        +
        +  async function addMessage() {
        +    let postCond_1 = Pc.principal('ST11V9ZN6E6VG72SHMAVM9GDE30VD3VGW5Q1W9WX3')
        +      .willSendEq(1)
        +      .ft('ST1F7QA2MDF17S807EPA36TSS8AMEFY4KA9TVGWXT.sbtc-token', 'sbtc-token')
        +  
        +    let result: TransactionResult = await request('stx_callContract', {
        +      contract: 'ST11V9ZN6E6VG72SHMAVM9GDE30VD3VGW5Q1W9WX3.stacks-dev-quickstart-message-board',
        +      functionName: 'add-message',
        +      functionArgs: [Cl.stringUtf8(content)],
        +      network: 'testnet',
        +      postConditions: [postCond_1],
        +      postConditionMode: 'deny',
        +      sponsored: false
        +    })
        +  
        +    setContent('')
        +  }
        +
        +  return (
        +    <>
        +      // ...
        +      <span className='input-container'>
        +        <button onClick={addMessage}>add-message</button>
        +        <input type="text" onChange={e => setContent(e.target.value)}/>
        +      </span>
        +    </>
        +  )
        +}
        +
        + +Let's breakdown some of the transaction params we specified in our string literal method of `stx_callContract` + +* `contract` : this requires the full contract principal of the target contract. +* `functionName` : the name of the public function in the contract. +* `functionArgs` : an array of the required function arguments in the same order as coded in the actual function of the Clarity contract. The `Cl` namespace has type converter methods that help with converting your argument into the required Clarity type. +* `network` : the target network the contract is deployed on. +* `postConditions` & `postConditionMode` : [Post-Conditions](../../post-conditions/overview.md) for the frontend are declared to protect user assets. The `Pc` helper from `@stacks/transactions` helps us to declare post-condition statements for any type of asset and equality operator. +* `sponsored` : if the transaction fees will be sponsored or not. + +Invoking our `addMessage` function will prompt the user's connected wallet to prompt a transaction confirmation popup. This popup will display all of the relevant information of the transaction as well as the post-condition statements that we've declared. + +Invoking a contract function is just one example of sending transactions from the frontend. There is also calling read-only functions, initiating asset transfer transactions, and etc. + +### Invoking asset transfer transactions + +The `request` object also supports native asset transfers for both STX, fungible tokens, and non-fungible tokens. Here is an example of creating an sBTC transfer transaction on the frontend. + +Since sBTC is a SIP-010 fungible token, we'll invoke the `stx_transferSip10Ft` method. + +```typescript +import { request } from '@stacks/connect'; + +let response = await request('stx_transferSip10Ft', { + asset: 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token', + amount: 1, + recipient: 'SP202X1VHZMN5S90T44N7SSQ496PYQD66XCPA7BNK', + network: 'mainnet', +}); +``` + +*** + +### Additional Resources + +* \[[Learn Stacks.js](/broken/pages/dH5waQhE6Vb7rhcrUG7z)] Head to the Learn Stacks.js section to see more examples diff --git a/docs/build/get-started/clarity-crash-course.md b/docs/build/get-started/clarity-crash-course.md new file mode 100644 index 0000000000..a7eb039f3a --- /dev/null +++ b/docs/build/get-started/clarity-crash-course.md @@ -0,0 +1,322 @@ +--- +description: >- + The Stacks ecosystem has its own smart contract programming language called + Clarity. +--- + +# Clarity Crash Course + +

        source: Hiro Blog

        + +### Intro + +This is designed for people with some programming experience who are new to Clarity. You don't need prior smart contract development experience, but if you have experience with languages like Solidity, you'll pick this up quickly. + +Once you've briefly familiarized yourself with the language, consider the [Clarity Book](https://book.clarity-lang.org/) or the course [Clarity Universe](https://clarity-lang.org/universe) to continue your learning. + +{% hint style="info" %} +Clarity is developed as a joint effort of [Hiro PBC](https://hiro.so/), [Algorand](http://algorand.com/), and various other stakeholders, that originally targets the Stacks blockchain. +{% endhint %} + +### Your First Clarity Smart Contract + +We're going to walkthrough a basic Clarity smart contract using the [Clarity Playground](https://play.hiro.so/), an online REPL environment where you can write and run Clarity code in the browser. Visit that link and it will open up a new example contract for you on the left view, with an interactive REPL on the right view. + +

        The example counter contract provided when visiting the Clarity Playground

        + +{% hint style="info" %} +Clarity Playground is a new tool to write and run Clarity code directly in the browser. With Clarity Playground, developers can test out concepts, try new ideas, or just, well…play around. Learn more [here](https://www.hiro.so/blog/meet-clarity-playground). +{% endhint %} + +The example contract you'll see is a simple counter contract that will store the value of a `count` in a data variable and `increment` the count value by invoking a defined public function. + +{% code title="counter.clar" %} +```clarity +(define-data-var count uint u0) +(define-data-var contract-owner principal tx-sender) +(define-data-var cost uint u10) + +(define-read-only (get-count) + (var-get count) +) + +(define-public (increment) + (begin + (print u"incrementing count") + (ok (var-set count (+ (var-get count) u1))) + ) +) +``` +{% endcode %} + +Clarity's syntax is inspired by LISP: everything is an expression wrapped in parentheses. Function definitions, variable declarations, and parameters are lists inside lists. This makes Clarity concise and readable once you get used to it. Here are some characteristics of Clarity you'll notice: + +{% stepper %} +{% step %} +#### Everything in parentheses is an expression + +Clarity treats everything as expressions inside parentheses. Function definitions are calls to built-in functions; the function body is an expression. This uniformity helps reasoning about programs in Clarity. +{% endstep %} + +{% step %} +#### Uses LISP-like nesting + +Expect nested parentheses and expressions. You’ll often read code as lists inside lists, where each parentheses-enclosed group represents a call or expression. +{% endstep %} +{% endstepper %} + +{% hint style="info" %} +In Clarity, there are public, private, and read-only functions: + +* public: can modify chain state and be called externally. +* private: can modify state but only be called within the contract. +* read-only: will fail if they attempt to modify state. +{% endhint %} + +Let's expand on these ideas by walking through that example counter contract line by line. + +#### Defining data variables + +The built-in Clarity function of `define-data-var` allows you to define a new persisted variable for the contract. Only modifiable by the contract. + +```clarity +;; defining a `count` variable to store a variable unsigned integer value +(define-data-var count uint u0) + +;; defining a `contract-owner` for a specific `principal` value +(define-data-var contract-owner principal tx-sender) + +;; defining a `cost` variable with an initial unsigned integer value of 10 +(define-data-var cost uint u10) +``` + +#### Defining a read-only function to read the current count value + +The built-in Clarity function of `define-read-only` defines a public read-only function. Cannot modify data maps or call mutating functions. May return any type. + +```clarity +;; allows anyone to read the current `count` value in the contract +(define-read-only (get-count) + (var-get count) +) +``` + +#### Defining a public function to increment the count value + +This function prints a log event saying it's incrementing a counter, then reads the current counter, adds 1, saves it back on-chain, and returns success. + +```clarity +;; Defines a public function named increment that anyone can call +(define-public (increment) + ;; Starts a begin block, which allows multiple expressions to run in order. + (begin + ;; Logs/prints the text "incrementing count" (as a Unicode string) to + ;; the transaction output or event stream. + (print u"incrementing count") + ;; adds u1 to the current count and wraps the resulting value in a response type + (ok (var-set count (+ (var-get count) u1))) + ) +) +``` + +### Interact With Your Contract + +The Clarity Playground allows you to call your functions on the right side view via a REPL console that runs a simnet environment. + +
        + +What is Simnet? + +Simnet is optimized for providing fast feedback loops at the cost of correctness. Simnet does not provide a full simulated blockchain environment, so there are no concepts of transaction fees, new blocks, or consensus mechanisms. + +Instead, simnet focuses on letting you quickly iterate on your code and test the code of the contract locally through unit testing and integration testing. It’s a good preliminary debugging step before introducing the additional variables that come with a fully-fledged blockchain environment. + +Simnet is a local environment spun up on your machine and is a private instance—you cannot share a simnet environment with other devs and collaborate with them—and further, simnet has no persistent state. It resets with each run. + +
        + +On page load of the Clarity Playground, the example counter contract is automatically deployed to the REPL console on the right side. If you made any changes to the contract in the code editor on the left view, be sure to click on Deploy. + +Calling contracts in the console or calling any externally deployed contracts will need to be passed into the built-in Clarity function called `contract-call?` . + +Follow the steps below to interact with your counter contract: + +{% stepper %} +{% step %} +#### Call the read-only \`get-count\` function + +In the bottom right Clarity command console, paste in the below command to call your `get-count` function to see the current `count` value. + +{% code title="clarity command console" %} +```clarity +(contract-call? .contract-0 get-count) +``` +{% endcode %} + +The console should return an initial value of `u0` since we haven't incremented the `count` yet. +{% endstep %} + +{% step %} +#### Call the public \`increment\` function + +Now let's finally increment our count value. In the bottom right Clarity command console, paste in the below command to call your `increment` function, which will increment the `count` value by 1. + +The console should return a value of `(ok true)` . This means the public function executed successfully and the count should have incremented. + +{% code title="clarity command console" %} +```clarity +(contract-call? .contract-0 increment) +``` +{% endcode %} +{% endstep %} + +{% step %} +#### Call our \`get-count\` function again + +To see if our count value was really incremented, let's call our read-only `get-count` function once again. + +{% code title="clarity command console" %} +```clarity +(contract-call? .contract-0 get-count) +``` +{% endcode %} + +The console should now returns a value of `u1` which is exactly what we'd expect. + +
        +{% endstep %} +{% endstepper %} + +Great! You just interacted with your first Clarity smart contract. Hopefully this gives you a good introduction to how the Clarity smart contract language looks and feels. + +*** + +### Read Access into Bitcoin + +Clarity smart contracts on the Stacks layer can also read Bitcoin state and can be triggered by standard Bitcoin transactions. This is because Stacks nodes also run Bitcoin nodes as part of consensus, and they read and index Bitcoin state. + +Reading Bitcoin state in Clarity is made by possible by the built-in function: `get-burn-block-info?` and the keyword `burn-block-height` . + +* `burn-block-height` : This keyword returns the current block height of the underlying burnchain: Bitcoin. Check out the example snippet below: + +```clarity +(> burn-block-height u1000) +;; returns true if the current height of the underlying burn blockchain has passed 1000 blocks. +``` + +* `get-burn-block-info?` : This function fetches block data of the burnchain: Bitcoin. Check out the example snippet below: + +```clarity +(get-burn-block-info? header-hash u677050) +;; Returns (some 0xe671...) +``` + +
        + +Verifying bitcoin transactions in Clarity + +One of the most popular Clarity contracts that leverages read access into Bitcoin is the `clarity-bitcoin-lib` contract, maintained by Friedger. This contract intakes data of a bitcoin transaction and will verify that it was indeed mined in a Bitcoin block. + +For more info: [https://github.com/friedger/clarity-bitcoin](https://github.com/friedger/clarity-bitcoin) + +
        + +*** + +### Flexible and secure modularization + +Many DAOs of the major Stacks apps implement a familiar contract design and architecture. This familiarity is inspired by the [ExecutorDAO](https://github.com/MarvinJanssen/executor-dao) framework, written by Marvin Janssen. This ExecutorDAO framework leverages the flexibility of having modularization in your smart contracts by compartmentalizing duties. + +The core tenets of the ExecutorDAO framework that make this possible are: + +1. Proposals are smart contracts. +2. The core executes, the extensions give form. +3. Ownership control happens via sending context. + +{% tabs %} +{% tab title="Main DAO contract" %} +The main DAO contract acts as the core contract where its sole purpose is to execute proposals and to keep a list of authorised extensions. + +{% code title="dao.clar" expandable="true" %} +```clarity +(use-trait proposal-trait .proposal-trait.proposal-trait) +(use-trait extension-trait .extension-trait.extension-trait) + +;; ... + +;; --- Authorisation check +(define-private (is-self-or-extension) + (ok (asserts! (or (is-eq tx-sender (as-contract tx-sender)) (is-extension contract-caller)) err-unauthorised)) +) + +;; ... + +;; --- Admin function to execute proposals +(define-public (execute (proposal ) (sender principal)) + (begin + (try! (is-self-or-extension)) + (asserts! (map-insert executed-proposals (contract-of proposal) block-height) err-already-executed) + (print {event: "execute", proposal: proposal}) + (as-contract (contract-call? proposal execute sender)) + ) +) +``` +{% endcode %} +{% endtab %} + +{% tab title="A proposal extension contract" %} +This proposal contract updates the whitelist of an example `.nft-escrow` contract that is owned by the main DAO contract. This proposal contract implements the `proposal-trait` and is passed into the main DAO contract's `execute` function for final approved execution. + +{% code title="proposal.clar" %} +```clarity +(impl-trait .proposal-trait.proposal-trait) + +(define-public (execute (sender principal)) + (contract-call? .nft-escrow set-whitelisted .some-nft true) +) +``` +{% endcode %} +{% endtab %} +{% endtabs %} + +*** + +### Testing Clarity Smart Contracts + +Once you get to writing more advanced smart contracts, properly testing them is paramount to protecting anyone who interacts with your contract. + +{% hint style="danger" %} +Smart contracts are immutable once deployed. Bugs are permanent. Test them thoroughly. +{% endhint %} + +* [Rendezvous Fuzz Testing](../rendezvous/overview.md): Use Rendezvous to hammer your contract with random inputs. It helps expose edge cases and vulnerabilities. +* [Unit Testing in Clarinet](/broken/pages/xKpkZWiNCO3dwHoA9AeB): Unit testing verifies that individual contract functions behave as expected. + +*** + +### Auditing Clarity Smart Contracts + +Auditors provide an independent, expert review of your smart contracts to identify vulnerabilities, logic flaws, edge cases, or design risks that might be missed during development. Here are a few Clarity smart contract auditors that are part of the Stacks community. + +* [Clarity Alliance](https://x.com/ClarAllianceSec) +* [SetDev](https://x.com/setdevbtc) + +*** + +### Additional Resources + +This brief overview should get your feet wet with Clarity. For deeper learning, we recommend: + +* [Clarity Book](https://book.clarity-lang.org/title-page.html) +* [Clarity Universe](https://clarity-lang.org/universe) +* [Clarity Playground](https://play.hiro.so/) +* [Clarity Camp](https://learn.stacks.org/course/clarity-camp) +* \[[StacksGov](https://github.com/stacksgov/sips/blob/main/sips/sip-002/sip-002-smart-contract-language.md)] SIP-002 The Clarity Smart Contract Language +* \[[Hiro Blog](https://www.hiro.so/blog/web3-programming-languages-clarity-vs-solidity)] Web3 Programming Languages: Clarity vs. Solidity +* \[[Stacks YT](https://youtu.be/hFqH1bJEvnw?si=yQADCvRNNjotuAga)] How Stacks' Language Clarity Enables Next Gen Smart Contracts +* \[[StacksDevs YT](https://www.youtube.com/watch?v=WZe1DgJ1w-E)] How Stacks’ Smart Contract Language Prevents Exploitation +* \[[Chainlink YT](https://youtu.be/OAVwd6SNJVU?si=UgfjmisBRbIYv27U)] Marvin Janssen: Clarity Smart Contracts for Stacks +* [100+ Days of Clarity video series by Setzeus](https://youtube.com/playlist?list=PLFHm9eE6H5uhNQ4cUXRE-4HkXF1ekS0ZG\&si=q0NmD-e9_QBomK3a) +* \[[waits.dev](https://waits.dev/writing/clarity-vs-solidity)] Clarity vs Solidity + +If you prefer jumping into Clarity's reference materials for definitions on all its types, functions, and keywords, head to [Clarity's Reference section](https://app.gitbook.com/s/GVj1Z9vMuEOMe7oH7Wnq/clarity/functions) of the docs. diff --git a/docs/build/get-started/create-a-token/README.md b/docs/build/get-started/create-a-token/README.md new file mode 100644 index 0000000000..c5f2922c5b --- /dev/null +++ b/docs/build/get-started/create-a-token/README.md @@ -0,0 +1,23 @@ +# Create a Token + +Many Stacks projects need tokens to establish membership or as an incentive for building a thriving Web3 community. When building a project, you need to not only think about whether your project needs a token (and if so, what type), but also what other Stacks tokens your app will need to interact with. + +Rather than needing to work with external libraries, Clarity has built-in functions that make working with fungible and non-fungible tokens a breeze. Below are the 3 different types of tokens available on Stacks. Click into any one of them to learn what they are and the different ways to have them launched for your project. + +### Fungible Tokens + +Fungible tokens on Stacks are digital assets that are interchangeable and identical in value, much like traditional currencies such as the dollar or bitcoin. They are typically used for utilities like payments, rewards, and participation rights within decentralized applications on the Stacks blockchain. + +[**Launch a fungible token**](fungible-tokens.md) + +### Non-Fungible Tokens + +Non-fungible tokens (NFTs) on Stacks are unique digital assets that cannot be exchanged on a one-to-one basis, as each token holds distinct information and value. They are often used for representing ownership of digital art, collectibles, and other unique items within decentralized applications on the Stacks network. NFTs provide a way to authenticate and trade the unique characteristics of digital goods securely. + +[**Launch a non-fungible token**](non-fungible-tokens.md) + +### Semi-Fungible Tokens + +Semi-fungible tokens (SFTs) are a type of digital asset that possess qualities of both fungible and non-fungible tokens. Initially, SFTs can be exchanged on a one-to-one basis like fungible tokens because they represent identical assets or goods, often in specific series or batches. However, once redeemed or utilized, they transform into non-fungible tokens, acquiring unique attributes or identification. This makes SFTs versatile for applications such as event tickets, vouchers, or gaming items where controlled fungibility is beneficial. + +[**Launch a semi-fungible token**](semi-fungible-tokens.md) diff --git a/docs/build/get-started/create-a-token/fungible-tokens.md b/docs/build/get-started/create-a-token/fungible-tokens.md new file mode 100644 index 0000000000..cc61c10812 --- /dev/null +++ b/docs/build/get-started/create-a-token/fungible-tokens.md @@ -0,0 +1,260 @@ +--- +description: A guide to help you create your own fungible tokens +--- + +# Fungible Tokens + +

        source: Hiro blog

        + +Creating a fungible token on Stacks can happen a few different ways — using no-code launchpads or writing your own Clarity smart contract. This guide helps you pick the best path for your goals and gives you the implementation details to ship confidently, whether you’re deploying with clicks or code. + +### Custom Development + +For developers who want full control over their token implementation, here’s how to create a custom SIP-010 token on Stacks using Clarity. But before you deploy the token contract, you must have your token contract conform to the SIP-010 trait standard. + +{% stepper %} +{% step %} +#### Define SIP-010 fungible token trait + +
        + +What is SIP-010? + +[SIP-010](https://github.com/stacksgov/sips/blob/main/sips/sip-010/sip-010-fungible-token-standard.md) is the standard for defining fungible tokens on Stacks. Defining a common interface (known in Clarity as a "trait") allows different smart contracts, apps, and wallets to interoperate with fungible token contracts in a reusable way. + +
        + +Below is an implementation of the SIP-010 trait standard for fungible tokens. You can use the existing minimal standard SIP-010 trait or extend it by adding in your own custom traits. But the requirements of the SIP-010 traits are necessary to have at the minimum. + +{% code title="SIP-010 trait implementation" expandable="true" %} +```clarity +(define-trait sip-010-trait + ( + ;; Transfer from the caller to a new principal + (transfer (uint principal principal (optional (buff 34))) (response bool uint)) + + ;; the human readable name of the token + (get-name () (response (string-ascii 32) uint)) + + ;; the ticker symbol, or empty if none + (get-symbol () (response (string-ascii 32) uint)) + + ;; the number of decimals used, e.g. 6 would mean 1_000_000 represents 1 token + (get-decimals () (response uint uint)) + + ;; the balance of the passed principal + (get-balance (principal) (response uint uint)) + + ;; the current total supply (which does not need to be a constant) + (get-total-supply () (response uint uint)) + + ;; an optional URI that represents metadata of this token + (get-token-uri () (response (optional (string-utf8 256)) uint)) + ) +) +``` +{% endcode %} + +All we are doing here is defining the function signatures for functions we'll need to implement in our token contract, which we can see a simple version of below. +{% endstep %} + +{% step %} +#### Implement SIP-010 trait in token contract + +Any token contract that wants to conform to the SIP-010 fungible token standard for Stacks needs to have this trait "implemented" in their token contract. See the below minimal token contract example of how this is done. + +{% code title="token-contract-clar" expandable="true" %} +```clarity +;; This contract implements the SIP-010 community-standard Fungible Token trait. +(impl-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait) + +;; Define the FT, with no maximum supply +(define-fungible-token clarity-coin) + +;; Define errors +(define-constant ERR_OWNER_ONLY (err u100)) +(define-constant ERR_NOT_TOKEN_OWNER (err u101)) + +;; Define constants for contract +(define-constant CONTRACT_OWNER tx-sender) +(define-constant TOKEN_NAME "Clarity Coin") +(define-constant TOKEN_SYMBOL "CC") +(define-constant TOKEN_DECIMALS u6) ;; 6 units displayed past decimal, e.g. 1.000_000 = 1 token + +(define-data-var token_uri (string-utf8 256) u"https://hiro.so") ;; utf-8 string with token metadata host + +;; SIP-010 function: Get the token balance of a specified principal +(define-read-only (get-balance (who principal)) + (ok (ft-get-balance clarity-coin who)) +) + +;; SIP-010 function: Returns the total supply of fungible token +(define-read-only (get-total-supply) + (ok (ft-get-supply clarity-coin)) +) + +;; SIP-010 function: Returns the human-readable token name +(define-read-only (get-name) + (ok TOKEN_NAME) +) + +;; SIP-010 function: Returns the symbol or "ticker" for this token +(define-read-only (get-symbol) + (ok TOKEN_SYMBOL) +) + +;; SIP-010 function: Returns number of decimals to display +(define-read-only (get-decimals) + (ok TOKEN_DECIMALS) +) + +;; SIP-010 function: Returns the URI containing token metadata +(define-read-only (get-token-uri) + (ok (some (var-get token_uri))) +) + +;; Properly updates token URI by emitting a SIP-019 token metadata update notification +(define-public (set-token-uri (value (string-utf8 256))) + (begin + (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_OWNER_ONLY) + (var-set token-uri value) + (ok (print { + notification: "token-metadata-update", + payload: { + contract-id: current-contract, + token-class: "ft" + } + }) + ) + ) +) + +;; Mint new tokens and send them to a recipient. +;; Only the contract deployer can perform this operation. +(define-public (mint (amount uint) (recipient principal)) + (begin + (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_OWNER_ONLY) + (ft-mint? clarity-coin amount recipient) + ) +) + +;; SIP-010 function: Transfers tokens to a recipient +;; Sender must be the same as the caller to prevent principals from transferring tokens they do not own. +(define-public (transfer + (amount uint) + (sender principal) + (recipient principal) + (memo (optional (buff 34))) +) + (begin + ;; #[filter(amount, recipient)] + (asserts! (or (is-eq tx-sender sender) (is-eq contract-caller sender)) ERR_NOT_TOKEN_OWNER) + (try! (ft-transfer? clarity-coin amount sender recipient)) + (match memo to-print (print to-print) 0x) + (ok true) + ) +) +``` +{% endcode %} + +This is the Clarity code we need in order to create an fungible token, with one additional function, `mint` that allows us to actually create a new fungible tokens. This `mint` function is not needed to adhere to the trait. + +The token contract example above is passing in an already deployed trait on mainnet into the `impl-trait` function. You can use this same deployed trait for your own token contract as well. + +{% hint style="success" %} +Deployed SIP-010 trait contracts you can directly implement in your custom token contract: + +* \[mainnet] [SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait](https://explorer.hiro.so/txid/SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard?chain=mainnet) +* \[testnet] [ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.sip-010-trait-ft-standard.sip-010-trait](https://explorer.hiro.so/txid/ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.sip-010-trait-ft-standard?chain=testnet) + +Reminder: when implementing these deployed traits in your contract, be sure to also add them as a contract requirement in Clarinet. +{% endhint %} +{% endstep %} +{% endstepper %} + +### No-code Platforms + +Use community built no-code platforms that can quickly help you deploy tokens. + +
        + +STX.City + +[STX.CITY](https://stx.city/) is a one-click platform for launching tokens on Stacks. The platform is a comprehensive toolkit for memecoin creators, enabling them to grow their communities through features like AMM listing support (such as on [Alex](https://alexgo.io/), [Velar](https://velar.com/), and [Stackswap](https://app.stackswap.org/)), airdrops, token donations, and burn mechanisms. + +Check out [this](https://www.hiro.so/blog/building-stx-city-as-a-solo-dev-on-stacks) blog post by the founder of STX.City for more information. + +
        + +### Best Practices + +Here are some things to consider when creating your token and after your token is launched. + +
        + +How to format the token metadata? + +Example token metadata taken from the sBTC token: + +```json +// https://ipfs.io/ipfs/bafkreibqnozdui4ntgoh3oo437lvhg7qrsccmbzhgumwwjf2smb3eegyqu + +{ + "sip": 16, + "name": "sBTC", + "description": "BTC is a 1:1 Bitcoin-backed asset on the Stacks Bitcoin L2 that will allow developers to leverage the security, network effects, and .5T in latent capital of the Bitcoin network.", + "image": "https://ipfs.io/ipfs/bafkreiffe46h5voimvulxm2s4ddszdm4uli4rwcvx34cgzz3xkfcc2hiwi", + "properties": { + "decimals": 8, + "external_url": "https://sbtc.tech" + } +} +``` + +Check out the [SIP-016](https://github.com/stacksgov/sips/blob/main/sips/sip-016/sip-016-token-metadata.md) standard for how you should define the schema of your metadata. + +
        + +
        + +How would I properly update my token metadata? + +If you plan on updating your token's metadata in the future, you should definitely implement a [SIP-019](https://github.com/stacksgov/sips/blob/main/sips/sip-019/sip-019-token-metadata-update-notifications.md) compliant token metadata update notification. Take a look at the example token contract above and you'll notice the `set-token-uri` function emits a SIP-019 compliant print event. + +```clarity +;; ... +(define-public (set-token-uri (value (string-utf8 256))) + (begin + (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_OWNER_ONLY) + (var-set token-uri value) + (ok (print { + notification: "token-metadata-update", + payload: { + contract-id: current-contract, + token-class: "ft" + } + }) + ) + ) +) +;; ... +``` + +Hiro’s [Token Metadata API](https://www.hiro.so/token-metadata-api) watches for that specific print event (specifically the notification of "token-metadata-update") on the network and auto-updates the API’s database to reflect a change in the existing token’s metadata. + +If your token contract did not implement this print event, you could use the helper contract below to invoke a function that'll emit the same print event notification. Just invoke the `ft-metadata-update-notify` function of this contract below: + +[SP1H6HY2ZPSFPZF6HBNADAYKQ2FJN75GHVV95YZQ.token-metadata-update-notify](https://explorer.hiro.so/txid/SP1H6HY2ZPSFPZF6HBNADAYKQ2FJN75GHVV95YZQ.token-metadata-update-notify?chain=mainnet) + +
        + +### Additional Resources + +* \[[dev.to](https://dev.to/kamalthedev/ethereum-vs-bitcoin-a-deep-dive-into-token-standards-erc-20-vs-sip-10-vs-brc20-vs-stx20-12na)] A Deep Dive into Token Standards: ERC-20 vs. SIP-10 vs. BRC20 vs. STX20 +* \[[StacksGov](https://github.com/stacksgov/sips/blob/main/sips/sip-010/sip-010-fungible-token-standard.md)] SIP-010 Standard Trait Definition for Fungible Tokens +* \[[StacksGov](https://github.com/stacksgov/sips/blob/main/sips/sip-016/sip-016-token-metadata.md)] SIP-016 Schema Definition for Metadata for Digital Assets +* \[[StacksGov](https://github.com/stacksgov/sips/blob/main/sips/sip-019/sip-019-token-metadata-update-notifications.md)] SIP-019 Notifications for Token Metadata Updates +* \[[contract](https://explorer.hiro.so/txid/SP1H6HY2ZPSFPZF6HBNADAYKQ2FJN75GHVV95YZQ.token-metadata-update-notify?chain=mainnet)] SP1H6HY2ZPSFPZF6HBNADAYKQ2FJN75GHVV95YZQ.token-metadata-update-notify +* \[[StacksDevs YT](https://youtu.be/v0_Mexz3KJ8?si=iGMyxQX2lSktOTWp)] Fungible Token Standard (SIP-10) Tutorial For Bitcoin L2 Stacks +* \[[LearnWeb3](https://learnweb3.io/lessons/sip-010-fungible-tokens-and-traits/)] SIP-010 Fungible Tokens & Traits +* \[[Medium @n.campos.rojas](https://medium.com/@n.campos.rojas/learn-how-to-create-fungible-tokens-on-stacks-versus-on-ethereum-a6dae4986863)] Learn how to create fungible tokens on Stacks (versus on Ethereum) diff --git a/docs/build/get-started/create-a-token/non-fungible-tokens.md b/docs/build/get-started/create-a-token/non-fungible-tokens.md new file mode 100644 index 0000000000..306b4535e9 --- /dev/null +++ b/docs/build/get-started/create-a-token/non-fungible-tokens.md @@ -0,0 +1,209 @@ +--- +description: A guide to help you create your own non-fungible tokens +--- + +# Non-Fungible Tokens + +

        source: Hiro blog

        + +Create an NFT with Stacks because it builds **on Bitcoin** — inheriting the security and permanence of the most durable chain via Proof-of-Transfer. Clarity smart contracts make logic easy to audit, reducing the guessing and attack surface common in NFT projects. Plus, Stacks NFTs tap into a Bitcoin-aligned community that values ownership, longevity, and real on-chain utility. + +### Custom Development + +For developers who want full control over their token implementation, here’s how to create a custom SIP-009 NFT on Stacks using Clarity. But before you deploy the NFT contract, you must have your NFT contract conform to the SIP-009 trait standard. + +{% stepper %} +{% step %} +#### Define SIP-009 non-fungible token trait + +
        + +What is SIP-009? + +[SIP-009](https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md) is the standard for defining fungible tokens on Stacks. Defining a common interface (known in Clarity as a "trait") allows different smart contracts, apps, and wallets to interoperate with non-fungible token contracts in a reusable way. Its primary purpose is to ensure that NFTs are composable and different tools know how to interact with them. + +
        + +Below is an implementation of the SIP-009 trait standard for non-fungible tokens. You can use the existing minimal standard SIP-009 trait or extend it by adding in your own custom traits. But the requirements of the SIP-009 traits are necessary to have at the minimum. + +{% code title="NFT trait standard" expandable="true" %} +```clarity +(define-trait nft-trait + ( + ;; Last token ID, limited to uint range + (get-last-token-id () (response uint uint)) + + ;; URI for metadata associated with the token + (get-token-uri (uint) (response (optional (string-ascii 256)) uint)) + + ;; Owner of a given token identifier + (get-owner (uint) (response (optional principal) uint)) + + ;; Transfer from the sender to a new principal + (transfer (uint principal principal) (response bool uint)) + ) +) +``` +{% endcode %} + +All we are doing here is defining the function signatures for functions we'll need to implement in our NFT contract, which we can see a simple version of below. +{% endstep %} + +{% step %} +#### Implement SIP-009 trait in NFT contract + +Any NFT contract that wants to conform to the SIP-009 non-fungible token standard for Stacks needs to have this trait "implemented" in their NFT contract. See the below minimal NFT contract example of how this is done. + +{% code title="non-fungible-token.clar" expandable="true" %} +```clarity +;; This contract implements the SIP-009 community-standard Non-Fungible Token trait +(impl-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait) + +;; Define the NFT's name +(define-non-fungible-token Your-NFT-Name uint) + +;; Keep track of the last minted token ID +(define-data-var last-token-id uint u0) + +;; Define constants +(define-constant CONTRACT_OWNER tx-sender) +(define-constant COLLECTION_LIMIT u1000) ;; Limit to series of 1000 + +(define-constant ERR_OWNER_ONLY (err u100)) +(define-constant ERR_NOT_TOKEN_OWNER (err u101)) +(define-constant ERR_SOLD_OUT (err u300)) + +(define-data-var base-uri (string-ascii 256) "https://your.api.com/path/to/collection/{id}") + +;; SIP-009 function: Get the last minted token ID. +(define-read-only (get-last-token-id) + (ok (var-get last-token-id)) +) + +;; SIP-009 function: Get link where token metadata is hosted +(define-read-only (get-base-uri (token-id uint)) + (ok (some (var-get base-uri))) +) + +;; SIP-009 function: Get the owner of a given token +(define-read-only (get-owner (token-id uint)) + (ok (nft-get-owner? Your-NFT-Name token-id)) +) + +;; SIP-019 compliant token metadata update notification +(define-public (set-base-uri (value (string-ascii 256))) + (begin + (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_OWNER_ONLY) + (var-set base-uri value) + (ok (print { + notification: "token-metadata-update", + payload: { + token-class: "nft", + contract-id: current-contract, + } + }) + ) + ) +) + +;; SIP-009 function: Transfer NFT token to another owner. +(define-public (transfer (token-id uint) (sender principal) (recipient principal)) + (begin + ;; #[filter(sender)] + (asserts! (is-eq tx-sender sender) ERR_NOT_TOKEN_OWNER) + (nft-transfer? Your-NFT-Name token-id sender recipient) + ) +) + +;; Mint a new NFT. +(define-public (mint (recipient principal)) + ;; Create the new token ID by incrementing the last minted ID. + (let ((token-id (+ (var-get last-token-id) u1))) + ;; Ensure the collection stays within the limit. + (asserts! (< (var-get last-token-id) COLLECTION_LIMIT) ERR_SOLD_OUT) + ;; Only the contract owner can mint. + (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_OWNER_ONLY) + ;; Mint the NFT and send it to the given recipient. + (try! (nft-mint? Your-NFT-Name token-id recipient)) + + ;; Update the last minted token ID. + (var-set last-token-id token-id) + ;; Return a success status and the newly minted NFT ID. + (ok token-id) + ) +) +``` +{% endcode %} + +This is the Clarity code we need in order to create an NFT, with one additional function, `mint` that allows us to actually create a new NFT. This `mint` function is not needed to adhere to the trait. + +The token contract example above is passing in an already deployed trait on mainnet into the `impl-trait` function. You can use this same deployed trait for your own NFT contract as well. + +{% hint style="success" %} +Deployed SIP-010 trait contracts you can directly implement in your custom token contract: + +* \[mainnet] SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait +* \[testnet] ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.nft-trait + +Reminder: when implementing these deployed traits in your contract, be sure to also add them as a contract requirement in Clarinet. +{% endhint %} +{% endstep %} +{% endstepper %} + +### Best Practices + +Here are some things to consider when creating your NFT and after your NFT is launched. + +
        + +How to format the NFT metadata? + +Check out the [SIP-016](https://github.com/stacksgov/sips/blob/main/sips/sip-016/sip-016-token-metadata.md) standard for how you should define the schema of your metadata. + +
        + +
        + +How would I properly update my NFT metadata? + +If you plan on updating your NFT's metadata in the future, you should definitely implement a function that emits a [SIP-019](https://github.com/stacksgov/sips/blob/main/sips/sip-019/sip-019-token-metadata-update-notifications.md) compliant token metadata update notification. Take a look at the example NFT contract above and you'll notice the `set-base-uri` function emits a SIP-019 compliant print event. + +
        ;; ...
        +(define-public (set-base-uri (value (string-ascii 256)))
        +    (begin
        +        (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_OWNER_ONLY)
        +        (var-set base-uri value)
        +        (ok (print {
        +              notification: "token-metadata-update",
        +              payload: {
        +                token-class: "nft",
        +                contract-id: current-contract,
        +              }
        +            })
        +        )
        +    )
        +)
        +;; ...
        +
        + +Hiro’s [Token Metadata API](https://www.hiro.so/token-metadata-api) watches for that specific print event (specifically the notification of "token-metadata-update") on the network and auto-updates the API’s database to reflect a change in the existing NFT’s metadata. + +If your NFT contract did not implement this print event, you could use the helper contract below to invoke a function that'll emit the same print event notification. Just invoke the `nft-metadata-update-notify` function of this contract below: + +[SP1H6HY2ZPSFPZF6HBNADAYKQ2FJN75GHVV95YZQ.token-metadata-update-notify](https://explorer.hiro.so/txid/SP1H6HY2ZPSFPZF6HBNADAYKQ2FJN75GHVV95YZQ.token-metadata-update-notify?chain=mainnet) + +
        + +### Additional Resources + +* \[[Stacks](https://www.stacks.co/explore/nfts)] Explore NFTs on Stacks +* \[[Clarity Book](https://book.clarity-lang.org/ch10-01-sip009-nft-standard.html)] SIP009: the NFT standard +* \[[StacksGov](https://github.com/stacksgov/sips/blob/main/sips/sip-010/sip-010-fungible-token-standard.md)] SIP-009 Standard Trait Definition for Non-Fungible Tokens +* \[[StacksGov](https://github.com/stacksgov/sips/blob/main/sips/sip-016/sip-016-token-metadata.md)] SIP-016 Schema Definition for Metadata for Digital Assets +* \[[StacksGov](https://github.com/stacksgov/sips/blob/main/sips/sip-019/sip-019-token-metadata-update-notifications.md)] SIP-019 Notifications for Token Metadata Updates +* \[[contract](https://explorer.hiro.so/txid/SP1H6HY2ZPSFPZF6HBNADAYKQ2FJN75GHVV95YZQ.token-metadata-update-notify?chain=mainnet)] SP1H6HY2ZPSFPZF6HBNADAYKQ2FJN75GHVV95YZQ.token-metadata-update-notify +* \[[Hiro YT](https://youtu.be/Hejsz-pivM4?si=SrOlgC9KK6YQvEy7)] How to Display NFTs in a Wallet Using the Token Metadata API +* \[[Hiro YT](https://youtu.be/xwbXNgSvMkk?si=Dl8KEL2KsmPy1kON)] A Beginner's Overview of the Megapont Ape NFT Clarity Smart Contract +* \[[Hiro YT](https://youtu.be/Ajuq6j2NXM8?si=Gj-Z5sxJ28FRyPmN)] Stacker Chat with Muneeb Ali: Diving Deeper into Bitcoin NFTs +* \[[Hiro Blog](https://www.hiro.so/blog/breaking-down-nft-code-snippets-in-clarity)] Breaking Down NFT Code Snippets in Clarity +* \[[Hiro Blog](https://www.hiro.so/blog/how-sigle-built-nft-gated-features-in-their-app)] How Sigle Built NFT-Gated Features in Their App diff --git a/docs/build/get-started/create-a-token/semi-fungible-tokens.md b/docs/build/get-started/create-a-token/semi-fungible-tokens.md new file mode 100644 index 0000000000..9d5673471c --- /dev/null +++ b/docs/build/get-started/create-a-token/semi-fungible-tokens.md @@ -0,0 +1,230 @@ +--- +description: A guide to help you create your own semi-fungible tokens +--- + +# Semi-Fungible Tokens + +Semi-fungible tokens (SFTs) are a hybrid token structure that embraces parts of both FTs (fungible tokens) and NFTs. SFTs are interchangeable (like FTs) and can be traded between users like cash—1 SFT has the same value as another SFT in the same collection. But each SFT also has a unique identifier (like NFTs). + +SFTs are also particularly well suited for Web3 gaming and applications that need to issue lots of different tokens because SFTs enable different asset classes to be managed by a single smart contract (which as a developer is easier to manage and as a user results in cheaper transaction fees). + +### Custom Development + +For developers who want full control over their SFT implementation, here’s how to create a custom SIP-013 SFT on Stacks using Clarity. But before you deploy the SFT contract, you must have your SFT contract conform to the SIP-013 trait standard. + +{% stepper %} +{% step %} +#### Define SIP-013 semi-fungible token trait + +
        + +What is SIP-013? + +[SIP-013](https://github.com/stacksgov/sips/blob/main/sips/sip-013/sip-013-semi-fungible-token-standard.md) is the standard for defining semi-fungible tokens on Stacks. Defining a common interface (known in Clarity as a "trait") allows different smart contracts, apps, and wallets to interoperate with semi-fungible token contracts in a reusable, standard way. + +
        + +Below is an implementation of the SIP-013 trait standard for semi-fungible tokens. You can use the existing minimal standard SIP-013 trait or extend it by adding in your own custom traits. But the requirements of the SIP-013 traits are necessary to have at the minimum. + +{% code title="SFT trait standard" expandable="true" %} +```clarity +(define-trait sip013-semi-fungible-token-trait + ( + ;; Get a token type balance of the passed principal. + (get-balance (uint principal) (response uint uint)) + + ;; Get the total SFT balance of the passed principal. + (get-overall-balance (principal) (response uint uint)) + + ;; Get the current total supply of a token type. + (get-total-supply (uint) (response uint uint)) + + ;; Get the overall SFT supply. + (get-overall-supply () (response uint uint)) + + ;; Get the number of decimal places of a token type. + (get-decimals (uint) (response uint uint)) + + ;; Get an optional token URI that represents metadata for a specific token. + (get-token-uri (uint) (response (optional (string-ascii 256)) uint)) + + ;; Transfer from one principal to another. + (transfer (uint uint principal principal) (response bool uint)) + + ;; Transfer from one principal to another with a memo. + (transfer-memo (uint uint principal principal (buff 34)) (response bool uint)) + ) +) + +``` +{% endcode %} + +All we are doing here is defining the function signatures for functions we'll need to implement in our SFT contract, which we can see a simple version of below. + +**\[optional] transfer-many specification**\ +SIP013 Semi-fungible tokens can also optionally implement the trait `sip013-transfer-many-trait` to offer a built-in "transfer-many" features for bulk token transfers. + +{% code title="SFT transfer many trait" %} +```clarity +(define-trait sip013-transfer-many-trait + ( + ;; Transfer many tokens at once. + (transfer-many ((list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal})) (response bool uint)) + + ;; Transfer many tokens at once with memos. + (transfer-many-memo ((list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)})) (response bool uint)) + ) +) +``` +{% endcode %} +{% endstep %} + +{% step %} +#### Implement SIP-013 trait in SFT contract + +Any SFT contract that wants to conform to the SIP-013 semi-fungible token standard for Stacks needs to have this trait "implemented" in their SFT contract. See the below minimal SFT contract example of how this is done. + +{% code title="semi-fungible-token.clar" expandable="true" %} +```clarity +(impl-trait 'SPDBEG5X8XD50SPM1JJH0E5CTXGDV5NJTKAKKR5V.sip013-semi-fungible-token-trait.sip013-semi-fungible-token-trait) +(impl-trait 'SPDBEG5X8XD50SPM1JJH0E5CTXGDV5NJTKAKKR5V.sip013-transfer-many-trait.sip013-transfer-many-trait) + +(define-fungible-token semi-fungible-token) +(define-non-fungible-token semi-fungible-token-id {token-id: uint, owner: principal}) +(define-map token-balances {token-id: uint, owner: principal} uint) +(define-map token-supplies uint uint) + +(define-constant contract-owner tx-sender) + +(define-constant err-owner-only (err u100)) +(define-constant err-insufficient-balance (err u1)) +(define-constant err-invalid-sender (err u4)) + +(define-private (set-balance (token-id uint) (balance uint) (owner principal)) + (map-set token-balances {token-id: token-id, owner: owner} balance) +) + +(define-private (get-balance-uint (token-id uint) (who principal)) + (default-to u0 (map-get? token-balances {token-id: token-id, owner: who})) +) + +(define-read-only (get-balance (token-id uint) (who principal)) + (ok (get-balance-uint token-id who)) +) + +(define-read-only (get-overall-balance (who principal)) + (ok (ft-get-balance semi-fungible-token who)) +) + +(define-read-only (get-total-supply (token-id uint)) + (ok (default-to u0 (map-get? token-supplies token-id))) +) + +(define-read-only (get-overall-supply) + (ok (ft-get-supply semi-fungible-token)) +) + +(define-read-only (get-decimals (token-id uint)) + (ok u0) +) + +(define-read-only (get-token-uri (token-id uint)) + (ok none) +) + +;; #[allow(unchecked_params)] +(define-public (transfer (token-id uint) (amount uint) (sender principal) (recipient principal)) + (let + ( + (sender-balance (get-balance-uint token-id sender)) + ) + (asserts! (or (is-eq sender tx-sender) (is-eq sender contract-caller)) err-invalid-sender) + (asserts! (<= amount sender-balance) err-insufficient-balance) + (try! (ft-transfer? semi-fungible-token amount sender recipient)) + (try! (tag-nft-token-id {token-id: token-id, owner: sender})) + (try! (tag-nft-token-id {token-id: token-id, owner: recipient})) + (set-balance token-id (- sender-balance amount) sender) + (set-balance token-id (+ (get-balance-uint token-id recipient) amount) recipient) + (print {type: "sft_transfer", token-id: token-id, amount: amount, sender: sender, recipient: recipient}) + (ok true) + ) +) + +(define-public (transfer-memo (token-id uint) (amount uint) (sender principal) (recipient principal) (memo (buff 34))) + (begin + (try! (transfer token-id amount sender recipient)) + (print memo) + (ok true) + ) +) + +(define-private (transfer-many-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal}) (previous-response (response bool uint))) + (match previous-response prev-ok (transfer (get token-id item) (get amount item) (get sender item) (get recipient item)) prev-err previous-response) +) + +(define-public (transfer-many (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal}))) + (fold transfer-many-iter transfers (ok true)) +) + +(define-private (transfer-many-memo-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)}) (previous-response (response bool uint))) + (match previous-response prev-ok (transfer-memo (get token-id item) (get amount item) (get sender item) (get recipient item) (get memo item)) prev-err previous-response) +) + +(define-public (transfer-many-memo (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)}))) + (fold transfer-many-memo-iter transfers (ok true)) +) + +(define-public (mint (token-id uint) (amount uint) (recipient principal)) + (begin + (asserts! (is-eq tx-sender contract-owner) err-owner-only) + (try! (ft-mint? semi-fungible-token amount recipient)) + (try! (tag-nft-token-id {token-id: token-id, owner: recipient})) + (set-balance token-id (+ (get-balance-uint token-id recipient) amount) recipient) + (map-set token-supplies token-id (+ (unwrap-panic (get-total-supply token-id)) amount)) + (print {type: "sft_mint", token-id: token-id, amount: amount, recipient: recipient}) + (ok true) + ) +) + +(define-private (tag-nft-token-id (nft-token-id {token-id: uint, owner: principal})) + (begin + (and + (is-some (nft-get-owner? semi-fungible-token-id nft-token-id)) + (try! (nft-burn? semi-fungible-token-id nft-token-id (get owner nft-token-id))) + ) + (nft-mint? semi-fungible-token-id nft-token-id (get owner nft-token-id)) + ) +) +``` +{% endcode %} + +This is the Clarity code we need in order to create a SFT, with an additional function, `mint` that allows us to actually create a new SFT. This `mint` function is not needed to adhere to the trait. + +The token contract example above is passing in already deployed traits on mainnet into the `impl-trait` function. You can use these same deployed traits for your own SFT contract as well. + +{% hint style="success" %} +Deployed SIP-013 trait contracts you can directly implement in your custom token contract: + +* \[mainnet] SPDBEG5X8XD50SPM1JJH0E5CTXGDV5NJTKAKKR5V.sip013-semi-fungible-token-trait +* \[mainnet] SPDBEG5X8XD50SPM1JJH0E5CTXGDV5NJTKAKKR5V.sip013-transfer-many-trait + +Reminder: when implementing these deployed traits in your contract, be sure to also add them as contract requirements in Clarinet. +{% endhint %} +{% endstep %} +{% endstepper %} + +### Best Practices + +
        + +How to deal with post-conditions on SFTs? + +Check out the SIP-013 standard for more info on dealing with post-conditions for SFTs. + +
        + +### Additional Resources + +* \[[StacksGov](https://github.com/stacksgov/sips/blob/main/sips/sip-013/sip-013-semi-fungible-token-standard.md)] SIP-013 Standard Trait Definition for Semi-Fungible Tokens +* \[[Hiro YT](https://youtu.be/a7c8aBiIkD4?si=k4cUS5DHrUsnm7Q9)] A Walkthrough of a SIP013 Implementation of SFTs on Stacks +* \[[DegenLab](https://docs.degenlab.io/gamefistacks/sfts/general-idea-and-base-sfts-static-deployments)] Example of SFT implementation with DegenLab diff --git a/docs/build/get-started/developer-quickstart.md b/docs/build/get-started/developer-quickstart.md new file mode 100644 index 0000000000..b75921d6a4 --- /dev/null +++ b/docs/build/get-started/developer-quickstart.md @@ -0,0 +1,853 @@ +--- +description: Your 0→1 guide for building a Clarity contract and app on Stacks. +--- + +# Developer Quickstart + +
        + +**Welcome to the Stacks Developer Quickstart Guide!**\ +This is your fast-track path for understanding what you'll need to become a Stacks developer. In this guide, you’ll build a real Clarity smart contract, wire up a functioning Stacks app, and pick up about 75% of the practical knowledge every Stacks builder needs. Whether you’re shipping your first project or leveling up your skills, this guide takes you from zero to deployed—quickly and confidently. + +### What you'll achieve + +By the end of this quickstart, you’ll have built an onchain app by: + +* Building a Clarity smart contract with Clarinet +* Utilize the 1:1 Bitcoin backed token, sBTC +* Deploying your smart contract to Stacks' testnet +* Interacting with your deployed contract from a frontend app + +{% hint style="success" %} +**Why Stacks?** + +Stacks is a fast, low-cost, builder-friendly layer 2 network on Bitcoin. It’s built on Bitcoin, inheriting Bitcoin’s battle-tested security. By jumping into this guide, you’re joining the Stacks community that’s bringing a global onchain economy to Bitcoin. +{% endhint %} + +### What You'll Build + +The app you'll build will be a message board contract. Users can add a new message to store on-chain for a fee of 1 satoshi in sBTC. Other functionality to read data from the contract will also be handled. Besides sBTC, there will be other things that'll be introduced to you such as post-conditions, Bitcoin read access, unit testing, wallet connectivity, BNS, Hiro, and more. Hopefully all this will give you a good flavor of what you can expect in the Stacks builder ecosystem. + +Let's start building on Bitcoin! :orange\_square: + +{% hint style="info" %} +**Prerequisites** + +* Basic familiarity with web development +* Basic familiarity with web3 concepts +* A modern web browser +* Node.js +* Visual Studio Code or any other popular IDE +{% endhint %} + +### Set Up Your Developer Environment + +{% stepper %} +{% step %} +**Install Clarinet** + +Clarinet is the popular CLI tool to build, test, and deploy smart contracts on the Stacks blockchain. + +Below are a few different ways to install Clarinet on your machine using your terminal. Refer to the dedicated [installation](../clarinet/overview.md) guide in the 'Learn Clarinet' section for more information. + +{% tabs %} +{% tab title="Homebrew" %} +```bash +brew install clarinet +``` +{% endtab %} + +{% tab title="Winget" %} +```bash +winget install clarinet +``` +{% endtab %} + +{% tab title="Source" %} +```bash +sudo apt install build-essential pkg-config libssl-dev +git clone https://github.com/stx-labs/clarinet +cd clarinet +cargo clarinet-install +``` +{% endtab %} + +{% tab title="Binary" %} +```bash +wget -nv https://github.com/stx-labs/clarinet/releases/latest/download/clarinet-linux-x64-glibc.tar.gz -O clarinet-linux-x64.tar.gz +tar -xf clarinet-linux-x64.tar.gz +chmod +x ./clarinet +mv ./clarinet /usr/local/bin +``` +{% endtab %} +{% endtabs %} +{% endstep %} + +{% step %} +**Install Clarity Extension** + +You'll also want to install the Clarity Extension for your code editor. The official one is '[Clarity - Stacks Labs](https://marketplace.visualstudio.com/items?itemName=StacksLabs.clarity-stacks)' which is maintained by [Stacks Labs](https://stackslabs.com/). + +
        + +What is Clarity? + +Clarity is Stacks' smart contract language, designed for safety and predictability. + +Clarity is inspired by LISP and uses a functional programming approach. Everything in Clarity is an expression wrapped in parentheses. This can be a bit overwhelming at first if you are used to languages like JavaScript or Solidity, but the learning curve is short and Clarity is a simple language to understand once you dive in and start using it. + +Check out the [Clarity Crash Course](clarity-crash-course.md) for a quick primer. + +
        + +

        The 'Clarity - Stacks Labs' extension as it appears in Visual Studio Code.

        +{% endstep %} + +{% step %} +**Install a Stacks wallet** + +There are many Stacks supported wallets in the market. For this guide, we'll be using the [Leather](https://leather.io/) wallet. Leather supports Stacks, Bitcoin, and other Bitcoin related meta-protocols. Download and install its browser extension so you can interact with your smart contract later on in this guide. Make sure to switch to the **Testnet** network in your wallet settings. Later on, we'll show you how to get testnet STX and sBTC tokens that you'll use for contract interaction. +{% endstep %} +{% endstepper %} + +### Create a Clarity smart contract + +{% stepper %} +{% step %} +**Create a new Clarinet project** + +Let's start by creating a new Clarinet project which will house our smart contract. The `clarinet new` command sets up everything you need for smart contract development, including a testing framework, deployment configurations, and a local development environment. + +{% code title="terminal" %} +``` +clarinet new my-stacks-contracts +``` +{% endcode %} + +A Clarinet project will be scaffolded with the below: + +{% code title="terminal" expandable="true" %} +``` +Created directory my-stacks-contracts +Created directory contracts +Created directory settings +Created directory tests +Created file Clarinet.toml +Created file settings/Mainnet.toml +Created file settings/Testnet.toml +Created file settings/Devnet.toml +Created directory .vscode +Created file .vscode/settings.json +Created file .vscode/tasks.json +Created file .gitignore +Created file .gitattributes +Created file package.json +Created file tsconfig.json +Created file vitest.config.ts + +---------------------------- +Hint: what's next? +Switch to the newly created directory with: + + $ cd my-stacks-contracts + +Once you are ready to write your contracts, run the following commands: + + $ clarinet contract new + Create new contract scaffolding, including test files. + + $ clarinet check + Check contract syntax for all files in ./contracts. +``` +{% endcode %} +{% endstep %} + +{% step %} +**Generate your contract** + +Now that we have our project structure, let's create a smart contract. Navigate into your project directory and use Clarinet's contract generator: + +```sh +$ cd my-stacks-contracts +$ clarinet contract new message-board +Created file contracts/message-board.clar +Created file tests/message-board.test.ts +Updated Clarinet.toml with contract message-board +``` + +Clarinet automatically creates both your contract file and a corresponding test file. +{% endstep %} +{% endstepper %} + +### Write your Clarity smart contract + +{% stepper %} +{% step %} +**Define constants** + +Open `contracts/message-board.clar` and remove its existing content. This is where we'll start writing our own Clarity smart contract. + +Let's first define some constants: + +* contract owner to establish control access +* custom error codes to handle errors in functions + +
        ;; Simple Message Board Contract
        +;; This contract allows users to read and post messages for a fee in sBTC.
        +
        +;; Define contract owner
        +(define-constant CONTRACT_OWNER tx-sender)
        +
        +;; Define error codes
        +(define-constant ERR_NOT_ENOUGH_SBTC (err u1004))
        +(define-constant ERR_NOT_CONTRACT_OWNER (err u1005))
        +(define-constant ERR_BLOCK_NOT_FOUND (err u1003))
        +
        + +You'll notice in the `CONTRACT_OWNER` constant that `tx-sender` is set in place as the value. When this contract is deployed, the Clarity VM will determine who the `tx-sender` is based on who deployed the contract. This allows the hardcoded `tx-sender` to always point to the principal that deployed the contract. +{% endstep %} + +{% step %} +**Define data storage** + +We'll then need to define some data storage: + +* A map to store key-value pairs of the message id and it's related metadata +* A data variable to count the total number of messages added + +
        ;; Define a map to store messages
        +;; Each message has an ID, content, author, and Bitcoin block height timestamp
        +(define-map messages
        +  uint
        +  {
        +    message: (string-utf8 280),
        +    author: principal,
        +    time: uint,
        +  }
        +)
        +
        +;; Counter for total messages
        +(define-data-var message-count uint u0)
        +
        +{% endstep %} + +{% step %} +**Define an add message function** + +Next up is our main function of the contract. This function allows users to add a new message to the contract for a fee of 1 satoshi in sBTC. Invoking this function will change the state of our contract and update the data storage pieces we setup before. + +
        ;; Public function to add a new message for 1 satoshi of sBTC
        +;; @format-ignore
        +(define-public (add-message (content (string-utf8 280)))
        +  (let ((id (+ (var-get message-count) u1)))
        +    (try! (restrict-assets? contract-caller 
        +      ((with-ft 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token "sbtc-token" u1))
        +      (unwrap!
        +        ;; Charge 1 satoshi of sBTC from the caller
        +        (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
        +          transfer u1 contract-caller current-contract none
        +        )
        +        ERR_NOT_ENOUGH_SBTC
        +      )
        +    ))
        +    ;; Store the message with current Bitcoin block height
        +    (map-set messages id {
        +      message: content,
        +      author: contract-caller,
        +      time: burn-block-height,
        +    })
        +    ;; Update message count
        +    (var-set message-count id)
        +    ;; Emit event for the new message
        +    (print {
        +      event: "[Stacks Dev Quickstart] New Message",
        +      message: content,
        +      id: id,
        +      author: contract-caller,
        +      time: burn-block-height,
        +    })
        +    ;; Return the message ID
        +    (ok id)
        +  )
        +)
        +
        + +There's quite a lot going on in this function above that covers in-contract post-conditions, calling the official sBTC token contract, reading Bitcoin state, emitting events, and etc. We'll break it down for you: + +
        + +Define public function and params + +```clarity +(define-public (add-message (content (string-utf8 280))) + ;; function body +) +``` + +By using the `define-public` function, we can literally create a public function where anyone can invoke. + +* `(add-message ... )` : the custom name of the public function +* `(content (string-utf8 280))` : the custom paramater name and type + +
        + +
        + +Create let variable binding for next message id + +```clarity +(let ((id (+ (var-get message-count) u1))) + ;; body expressions +) +``` + +Creates a "local" variable that can be used inside the function body only. This `id` variable will be used to represent the new message id being added. + +
        + +
        + +Transfer 1 satoshi of sBTC from user to the contract + +
        (try! (restrict-assets? contract-caller 
        +  ((with-ft 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token "sbtc-token" u1))
        +  (unwrap!
        +    ;; Charge 1 satoshi of sBTC from the caller
        +    (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
        +      transfer u1 contract-caller current-contract none
        +    )
        +    ERR_NOT_ENOUGH_SBTC
        +  )
        +))
        +
        + +This snippet calls the external .`sbtc-token` contract to transfer sBTC. + +The `restrict-assets?` acts as an in-contract post-condition to protect user and contract funds when calling external contracts to transfer assets. + +
        + +
        + +Store message data in mapping + +```clarity +(map-set messages id { + message: content, + author: contract-caller, + time: burn-block-height, +}) +``` + +The function `map-set` will allow the existing mapping of `messages` to add a new key-value pair consiting of the metadata of the new message. + +We'll be using the current Bitcoin block height (via `burn-block-height`) as a way to capture the time of when this new message was added. Through `burn-block-height` , Clarity allows us to have read access into the Bitcoin state at anytime. + +
        + +
        + +Update the message-count variable + +```clarity +(var-set message-count id) +``` + +Increments the existing data variable of `message-count` with the `let` id variable. + +
        + +
        + +Emit an event to the network + +```clarity +(print { + event: "[Stacks Dev Quickstart] New Message", + message: content, + id: id, + author: contract-caller, + time: burn-block-height, +}) +``` + +The `print` function will allow us to emit a custom event to the Stacks network. + +Emitting events on Stacks serves several critical purposes: + +1. **Transparency**: Events provide an on-chain record of actions and transactions, ensuring transparency. +2. **Notification**: They serve as a signal mechanism for users and external applications, notifying them of specific occurrences on Stacks. +3. **State Tracking**: Developers can use events to track changes in the state of smart contracts without querying the chain continuously. +4. **Efficient Data Handling**: By emitting events, webhook services, such as Hiro's [Chainhooks](https://docs.hiro.so/en/tools/chainhooks), can filter and handle relevant data efficiently, reducing the on-chain computation load. + +
        + +
        + +Return final response + +
        ;; Return the message ID
        +(ok id)
        +
        + +Public functions _must_ return a ResponseType (using either `ok` or `err`). In this case, we'll return a response type with an inner value of the new message id. + +
        +{% endstep %} + +{% step %} +**Add sBTC contract requirements** + +Since we're working with sBTC in our local developer environment, we'll need to make sure Clarinet can recognize this. Clarinet can automatically wire up the official sBTC contracts so you can build and test sBTC flows locally. + +In our case, all we'll need to do is add the [`.sbtc-deposit`](https://explorer.hiro.so/txid/SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-deposit?chain=mainnet) contract as a project requirement. + +{% code title="terminal" %} +``` +clarinet requirements add SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-deposit +``` +{% endcode %} + +You'll notice in the `add-message` public function, we're making an external contract call to the [`.sbtc-token`](https://explorer.hiro.so/txid/0xead2080826685a98886891cbd9b288d367ae19b357353c71fff4a3330da582c8?chain=mainnet) contract. This is the official sBTC token contract that contains the [SIP-010](https://github.com/stacksgov/sips/blob/main/sips/sip-010/sip-010-fungible-token-standard.md) standard `transfer` function that we are invoking. + +Check out the dedicated [sBTC integration](../clarinet/integrations/sbtc.md) page to learn more. +{% endstep %} + +{% step %} +**Allow contract owner to withdraw funds** + +In the beginning of our contract, we defined a constant to store the Stacks principal of the contract owner. Having a contract owner allows for specific access control of the contract that is entitled to the owner. Let's allow the owner to be able to withdraw the accumulated sBTC fees that were sent by anyone who created a new message in the contract. + +
        ;; Withdraw function for contract owner to withdraw accumulated sBTC
        +(define-public (withdraw-funds)
        +  (begin
        +    (asserts! (is-eq tx-sender CONTRACT_OWNER) (err u1005))
        +    (let ((balance (unwrap-panic (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
        +        get-balance current-contract
        +      ))))
        +      (if (> balance u0)
        +        (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
        +          transfer balance current-contract CONTRACT_OWNER none
        +        )
        +        (ok false)
        +      )
        +    )
        +  )
        +)
        +
        + +You'll notice in the highlighted line that the function performs an `asserts!` check to confirm that the `tx-sender` calling the contract is in fact the `CONTRACT_OWNER` . If it is in fact the owner of the contract, the function body proceeds with transferring the balance of sBTC to the owner or else it'll throw an error that we defined earlier. + +{% hint style="info" %} +The usage of `tx-sender` versus another Clarity keyword, `contract-caller` , is always a tricky concept because it determines who actually initiated the transaction versus who invoked the current function. Both of them can have certain implications on security based on the context of your code. Check out the dedicated [blog](https://www.setzeus.com/public-blog-post/clarity-carefully-tx-sender), written by community dev Setzeus, to learn when you should use either or. +{% endhint %} +{% endstep %} + +{% step %} +**Implement read only functions** + +We'll round out our contract with important read only functions that will return us needed data from the contract. + +
        ;; Read-only function to get a message by ID
        +(define-read-only (get-message (id uint))
        +  (map-get? messages id)
        +)
        +
        +;; Read-only function to get message author
        +(define-read-only (get-message-author (id uint))
        +  (get author (map-get? messages id))
        +)
        +
        +;; Read-only function to get message count at a specific Stacks block height
        +(define-read-only (get-message-count-at-block (block uint))
        +  (ok (at-block
        +    (unwrap! (get-stacks-block-info? id-header-hash block) ERR_BLOCK_NOT_FOUND)
        +    (var-get message-count)
        +  ))
        +)
        +
        + +You'll notice the usage of a `at-block` function in the highlighted line of code. The `at-block` function evaluates the inner expression _as if_ it were evaluated at the end of a specific Stacks block. +{% endstep %} + +{% step %} +**Test your contract** + +Now with the actual writing of your contract complete, we now need to test its functionality. There's a few different ways we can go about iterating and testing the functionality of your contract. + +* Contract interaction in the [Clarinet REPL](../clarinet/contract-interaction.md) +* Running your contract in a [local blockchain environment](../clarinet/local-blockchain-development.md) +* Fuzz testing with [Rendezvous](https://stacks-network.github.io/rendezvous/) +* Writing unit tests with the [Clarinet JS SDK](../clarinet/testing-with-clarinet-sdk.md) + +We'll go with unit testing for now. In your `tests` folder, open up the related `message-board.test.ts` file and let's use the unit test written below. + +
        import { Cl, ClarityType } from "@stacks/transactions";
        +import { describe, expect, it } from "vitest";
        +
        +const accounts = simnet.getAccounts();
        +const deployer = accounts.get("deployer")!;
        +const address1 = accounts.get("wallet_1")!;
        +
        +describe("example tests", () => {
        +  let content = "Hello Stacks Devs!"
        +
        +  it("allows user to add a new message", () => {
        +    let currentBurnBlockHeight = simnet.burnBlockHeight;
        +
        +    let confirmation = simnet.callPublicFn(
        +      "stacks-dev-quickstart-message-board",
        +      "add-message",
        +      [Cl.stringUtf8(content)],
        +      address1
        +    )
        +
        +    const messageCount = simnet.getDataVar("stacks-dev-quickstart-message-board", "message-count");
        +    
        +    expect(confirmation.result).toHaveClarityType(ClarityType.ResponseOk);
        +    expect(confirmation.result).toBeOk(messageCount);    
        +    expect(confirmation.events[1].data.value).toBeTuple({
        +      author: Cl.standardPrincipal(address1),
        +      event: Cl.stringAscii("[Stacks Dev Quickstart] New Message"),
        +      id: messageCount,
        +      message: Cl.stringUtf8(content),
        +      time: Cl.uint(currentBurnBlockHeight),
        +    });
        +  });
        +
        +  it("allows contract owner to withdraw funds", () => {
        +    simnet.callPublicFn(
        +      "stacks-dev-quickstart-message-board",
        +      "add-message",
        +      [Cl.stringUtf8(content)],
        +      address1
        +    )
        +    
        +    simnet.mineEmptyBurnBlocks(2);
        +
        +    let confirmation = simnet.callPublicFn(
        +      "stacks-dev-quickstart-message-board",
        +      "withdraw-funds",
        +      [],
        +      deployer
        +    )
        +    
        +    expect(confirmation.result).toBeOk(Cl.bool(true));
        +    expect(confirmation.events[0].event).toBe("ft_transfer_event")
        +    expect(confirmation.events[0].data).toMatchObject({
        +      amount: '1',
        +      asset_identifier: 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token::sbtc-token',
        +      recipient: deployer,
        +      sender: `${deployer}${".stacks-dev-quickstart-message-board"}`,
        +    })
        +  })
        +});
        +
        +
        + +You'll notice we have two `it` blocks setup to test out 2 different scenarios: + +1. Allows user to add a new message +2. Allows owner to withdraw sBTC funds + +Run the test via `npm run test` to confirm that the two scenarios are functioning as intended. + +{% code title="terminal" %} +```bash +$ npm run test + + ✓ tests/message-board.test.ts (2 tests) 46ms + ✓ message board tests (2) + ✓ allows user to add a new message 26ms + ✓ allows contract owner to withdraw funds 19ms + + Test Files 1 passed (1) + Tests 2 passed (2) + Start at 14:05:07 + Duration 886ms (transform 40ms, setup 42ms, collect 8ms, tests 46ms, environment 699ms, prepare 4ms) +``` +{% endcode %} + +Great! Now that your contract is working as intended, let's deploy the contract to testnet. +{% endstep %} +{% endstepper %} + +### Get testnet faucet tokens + +{% stepper %} +{% step %} +**Navigate to the Hiro Platform faucet** + +[Hiro](https://www.hiro.so/platform) is a platform to build and scale Bitcoin apps, including custom data streams, onchain alerts, API key management, and more. Create an account and navigate to the top tab of 'Faucet'. On the Faucet page, you can request testnet STX and/or sBTC. We'll be needing both so fund your Leather wallet account with both. + +
        + +Grab the testnet Stacks address from your Leather wallet and paste it in the recipient field. + +{% hint style="warning" %} +**Important**: Switch to the **Testnet** network in your wallet settings +{% endhint %} +{% endstep %} + +{% step %} +**Confirm testnet tokens in your wallet** + +Open up your Leather extension to confirm that you've received testnet STX and sBTC. You might need to enable the viewing of the sBTC token in your wallet under 'Manage tokens'. + +
        + +With both testnet STX and sBTC, you're ready to deploy your contract and interact with it from a front-end client. +{% endstep %} +{% endstepper %} + +### Deploy your Clarity smart contract + +{% stepper %} +{% step %} +**Generate testnet deployment plan** + +You'll first want to input a mnemonic seed phrase in the `settings/Testnet.toml` file and specify the account derivation path that you want to use for deploying the contract. The account should be the same one you used to request testnet STX to. This will be the account that actually deploys the contract and becomes the contract owner. + +
        [network]
        +name = "testnet"
        +stacks_node_rpc_address = "https://api.testnet.hiro.so"
        +deployment_fee_rate = 10
        +
        +[accounts.deployer]
        +mnemonic = "<YOUR TESTNET MNEMONIC>"
        +derivation = "m/44'/5757'/0'/0/0"
        +
        + +Then generate a deployment plan for the testnet network. Deployment plans are YAML files that describe how contracts are published or called. + +{% hint style="warning" %} +For more information on configuring deployment plans, check out the specific guide [here](../clarinet/contract-deployment.md). +{% endhint %} + +{% code title="terminal" %} +```bash +$ clarinet deployments generate --testnet --medium-cost +Analyzing contracts... +Calculating deployment costs... +Generating deployment plan +Created file deployments/default.testnet-plan.yaml +``` +{% endcode %} +{% endstep %} + +{% step %} +**Deploy contract to testnet** + +Once your deployment plan is generated and configured properly, go ahead and deploy the contract to testnet. + +{% code title="terminal" %} +```bash +clarinet deployments apply --testnet +``` +{% endcode %} + +If the contract was successfully deployed, you should see the below confirmation: + +``` +Broadcasting transactions to https://api.testnet.hiro.so +Publish ST11V9ZN6E6VG72SHMAVM9GDE30VD3VGW5Q1W9WX3.message-board Transaction confirmed +``` + +{% hint style="info" %} +A sample of the contract we just created above is already deployed to testnet [here](https://explorer.hiro.so/txid/ST11V9ZN6E6VG72SHMAVM9GDE30VD3VGW5Q1W9WX3.stacks-dev-quickstart-message-board?chain=testnet). Check out its contract page on the Stacks Explorer and directly interact with its functions. +{% endhint %} +{% endstep %} +{% endstepper %} + +### Use stacks.js on the frontend + +{% stepper %} +{% step %} +**Connect wallet** + +Using [stacks.js](../stacks.js/overview.md) packages on the frontend will allow our frontend app to authenticate wallets, call our contract functions, and interact with the Stacks network. + +We'll first want to connect and authenticate our Leather wallet extension with our frontend app. The stacks.js monorepo contains several underlying packages specific to different use cases. The package `@stacks/connect` is the main connectivity package used in Stacks. + +In the snippet below, you'll notice we have 3 functions setup to handle `connectWallet` , `disconnectWallet`, and for `getBns` . All 3 functions will be integral in how we want to display the 'Connect' and 'Disconnect' button in the UI. + +{% hint style="info" %} +Retrieving a wallet account's associated [BNS](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/network-fundamentals/bitcoin-name-system) is a staple of Stacks and for web3 identity. Check out [BNSv2](https://www.bnsv2.com/) for more information and for availably public API endpoints you could use. +{% endhint %} + +
        import { connect, disconnect } from '@stacks/connect'
        +import type { GetAddressesResult } from '@stacks/connect/dist/types/methods'
        +import { useState } from 'react'
        +
        +function App() {
        +  let [isConnected, setIsConnected] = useState<boolean>(false)
        +  let [walletInfo, setWalletInfo] = useState<any>(null)
        +  let [bns, setBns] = useState<string>('')
        +
        +  async function connectWallet() {
        +    let connectionResponse: GetAddressesResult = await connect()
        +    let bnsName = await getBns(connectionResponse.addresses[2].address)
        +
        +    setIsConnected(true)
        +    setWalletInfo(connectionResponse)
        +    setBns(bnsName)
        +  }
        +
        +  async function disconnectWallet() {
        +    disconnect();
        +  }
        +  
        +  async function getBns(stxAddress: string) {
        +    let response = await fetch(`https://api.bnsv2.com/testnet/names/address/${stxAddress}/valid`)
        +    let data = await response.json()
        +
        +    return data.names[0].full_name
        +  }
        +  
        +  return (
        +    <>
        +      <h3>Stacks Dev Quickstart Message Board</h3>
        +      {isConnected ? (
        +        <button onClick={disconnectWallet}>{
        +          bns ? bns : walletInfo.addresses[2].address
        +        }</button>
        +      ) : (
        +        <button onClick={connectWallet}>connect wallet</button>
        +      )}
        +    </>
        +  )
        +}
        +
        + +The `connect()` method comes with ability to configure how you want the wallet selector modal to appear for your app. You can decide which wallets to have only appear as an option or allow any wallet that follows the SIP-030 standard to appear as an available Stacks wallet. + +

        The Stacks Connect wallet selector modal

        +{% endstep %} + +{% step %} +**Call \`add-message\` public function** + +Next, we'll setup a `stx_callContract` to invoke the `add-message` public function of our contract. This function will accept a string content to be passed into our contract call. + +
        import { request } from '@stacks/connect'
        +import type { TransactionResult } from '@stacks/connect/dist/types/methods'
        +import { Cl, Pc } from '@stacks/transactions'
        +import { useState } from 'react'
        +
        +function App() {
        +  // ...
        +  let [content, setContent] = useState<string>('')
        +
        +  async function addMessage() {
        +    let postCond_1 = Pc.principal('ST11V9ZN6E6VG72SHMAVM9GDE30VD3VGW5Q1W9WX3')
        +      .willSendEq(1)
        +      .ft('ST1F7QA2MDF17S807EPA36TSS8AMEFY4KA9TVGWXT.sbtc-token', 'sbtc-token')
        +  
        +    let result: TransactionResult = await request('stx_callContract', {
        +      contract: 'ST11V9ZN6E6VG72SHMAVM9GDE30VD3VGW5Q1W9WX3.stacks-dev-quickstart-message-board',
        +      functionName: 'add-message',
        +      functionArgs: [Cl.stringUtf8(content)],
        +      network: 'testnet',
        +      postConditions: [postCond_1],
        +      postConditionMode: 'deny',
        +      sponsored: false
        +    })
        +  
        +    setContent('')
        +  }
        +
        +  return (
        +    <>
        +      // ...
        +      <span className='input-container'>
        +        <button onClick={addMessage}>add-message</button>
        +        <input type="text" onChange={e => setContent(e.target.value)}/>
        +      </span>
        +    </>
        +  )
        +}
        +
        + +You'll notice in the transaction data object that we pass into our string literal method of `stx_callContract`, that we're setting up post-conditions. [Post-Conditions](../post-conditions/overview.md) for the frontend are declared to protect user assets. The `Pc` helper from `@stacks/transactions` helps us to declare post-condition statements for any type of asset and equality operator. + +Invoking our `addMessage` function will prompt the user's connected wallet to prompt a transaction confirmation popup. This popup will display all of the relevant information of the transaction as well as the post-condition statements that we've declared. + +
        +{% endstep %} + +{% step %} +**Call read-only function** + +As how we've created a few read-only functions in our contract, we'll also want to call these from the frontend to retrieve certain contract data. + +Let's setup a `fetchCallReadOnlyFunction` to invoke our contract's `get-message-count-at-block` read-only function. For this, we'll fetch the current Stacks block height from the Hiro API endpoint and pass that returned value into our read-only function. + +
        // ...
        +import type { ClarityValue } from '@stacks/connect/dist/types/methods'
        +import { Cl, fetchCallReadOnlyFunction } from '@stacks/transactions'
        +
        +function App() {
        +  // ...  
        +  async function getMessageCountAtBlock() {
        +    let response = await fetch('https://api.testnet.hiro.so/v2/info', {
        +      headers: {
        +        "x-api-key": "<HIRO_API_KEY>"
        +      }
        +    })
        +    let data = await response.json()
        +    let stacksBlockHeight = data.stacks_tip_height
        +
        +    let result: ClarityValue = await fetchCallReadOnlyFunction({
        +      contractAddress: 'ST11V9ZN6E6VG72SHMAVM9GDE30VD3VGW5Q1W9WX3',
        +      contractName: 'stacks-dev-quickstart-message-board',
        +      functionName: 'get-message-count-at-block',
        +      functionArgs: [Cl.uint(stacksBlockHeight)],
        +      network: 'testnet',
        +      senderAddress: 'ST11V9ZN6E6VG72SHMAVM9GDE30VD3VGW5Q1W9WX3',
        +    })
        +  }
        +  
        +  // ...
        +
        + +{% hint style="info" %} +For the complete set of available API endpoints for the Stacks network, check out the [Hiro docs](https://docs.hiro.so/). But first create an API key from the [Hiro Platform](https://platform.hiro.so/) to determine your API rate plan. +{% endhint %} +{% endstep %} +{% endstepper %} + +And that's it, you've successfully created an sBTC powered Clarity smart contract which can be interacted with from a frontend app. There's obviously much more you can do to complete this but you've got some of the basics down pat now. Go ahead and finish creating the frontend functions to call on the other contract functions we have. + +*** + +### Further Improvements + +This is just the beginning. There are many ways we can improve upon this app. Here are some suggestions for you to extend the functionality of this app: + +* Deploy to mainnet and share your project with the community +* Use [Chainhooks](https://docs.hiro.so/en/tools/chainhooks) to index emitted events from the contract +* Integrate the [`sbtc`](../more-guides/sbtc/bridging-bitcoin/) library so users can directly bridge their BTC to sBTC in-app +* Utilize SIP-009 NFTs to uniquely identify each message for each author + +*** + +### Next Steps + +Now that you have the basics down, here are some ways to continue your Stacks development journey: + +**Learn More About Clarity** + +* [**Clarity Crash Course**](https://docs.stacks.co/docs/clarity-crash-course): Quick introduction to Clarity concepts +* [**Clarity Book**](https://book.clarity-lang.org/): Comprehensive guide to Clarity development +* [**Clarity Reference**](https://docs.stacks.co/docs/clarity): Complete documentation of Clarity functions + +**Development Tools** + +* [**Clarinet**](https://github.com/stx-labs/clarinet): Local development environment for Clarity +* [**Hiro Platform**](https://platform.hiro.so/): Hosted development environment +* [**Stacks Explorer**](https://explorer.stacks.co/): View transactions and contracts + +**Community Resources** + +* [**Stacks Discord**](https://discord.gg/stacks): Connect with other developers +* [**Stacks Forum**](https://forum.stacks.org/): Ask questions and share projects diff --git a/docs/build/get-started/introduction/why-build-with-stacks.md b/docs/build/get-started/introduction/why-build-with-stacks.md new file mode 100644 index 0000000000..2da229f98b --- /dev/null +++ b/docs/build/get-started/introduction/why-build-with-stacks.md @@ -0,0 +1,67 @@ +--- +description: Diving into the question of why we're all here +--- + +# Why Build with Stacks + +{% embed url="https://youtu.be/eqFFX4Sx9mY?si=XcH6Bo3nTQusW0Tp" %} + +### Why build with Stacks + +Build on Bitcoin with Stacks because builders don’t need a new base layer — they need a better app layer. Stacks anchors to Bitcoin finality while enabling clear, auditable contracts and expressive logic, making BTC useful beyond holding. With production-ready tooling and one of the most active Bitcoin L2 developer communities, you get momentum, collaboration, and Bitcoin-aligned product-market fit from day one. If you want Bitcoin capital with real applications, Stacks is the path of least resistance and greatest leverage. + +### What does it mean to build with Stacks + +Building with Stacks means creating applications and smart contracts that _**extend**_ the power of Bitcoin without altering Bitcoin itself. It’s about inheriting Bitcoin’s security, finality, and economic gravity, while gaining the expressiveness needed for decentralized apps, ownership-first systems, and programmable digital assets. + +Developers use easy, LISP-like contracts written in the Clarity language, interact with the chain using familiar tooling like Stacks.js, automate on-chain responsiveness with Chainhooks, enable Bitcoin programmability via sBTC, and tap into indexed on-chain data through reliable APIs. + +Ultimately, **building with Stacks is a mindset**: leverage Bitcoin as the base layer, but experiment boldly at the application layer—where users keep custody of their bitcoin via sBTC, transactions enforce intent, and decentralized systems become useful enough for everyday life. + +### Benefits of building with Stacks + +
        + +🌐 Global Community + +_**Diverse and Vibrant:**_ Stacks has a massive global builder community where collaboration happens at internet speed, not conference speed. You plug into shared momentum, talent, diversity, and distribution across a network anchored by Bitcoin. + +* \[[Hiro YT](https://youtu.be/jjSbIKRb8Z8?si=JRzdbiBWq9mLKVaz)] Meeting Rockstar Web3 Builders at the EasyA x Stacks Hackathon +* \[[StacksDevs](https://x.com/StacksDevs/status/1991506865394774371)] Photos from the Stacks Hacker House in Buenos Aires +* \[[Stacks YT](https://youtu.be/UB6pkG58wYo?si=ITiW7xhz8odfa59T)] How Stacks Took Over Miami + +
        + +
        + +🎨 Creator Tools + +_**The Bitcoin-Backed Creator Economy:**_ From artists to influencers, creators are seeking smarter ways to capture value. With Stacks, you can push the boundaries of monetization, powered by Bitcoin capital and programmable Clarity contracts. + +* \[[Hiro Blog](https://www.hiro.so/blog/what-is-bitcoin-culture-see-it-through-bitcoin-nfts)] What is “Bitcoin Culture?” See it through Bitcoin NFTs +* \[[Hiro Blog](https://www.hiro.so/blog/building-an-accelerator-for-african-creators-with-osinachi-africas-foremost-nft-artist)] Building an Accelerator for African Creators With Osinachi, Africa’s Foremost NFT Artist +* \[[Hiro Blog](https://www.hiro.so/blog/a-look-inside-gammas-create-portal-and-how-it-empowers-nft-creators)] A Look Inside Gamma’s Create Portal and How It Empowers NFT Creators + +
        + +
        + +🛠️ Developer Experience + +_**Built for Builders:**_ Stacks offers everything you need — tooling, infra, and hands-on support. Backed by one of the largest on-chain dev communities, it’s a place to collaborate, level up, and build the future together. + +* \[[Hiro YT](https://youtu.be/gPG6ZFGpYo0?si=hMxIxGJjuoggfD8Q)] Exploring Web3 Developer Tooling With the Clarinet Team +* \[[Bitcoin Builders YT](https://youtu.be/RCHbqfaUbHQ?si=j5BLw6lCIYAq52RI)] What is the Dev Experience for Bitcoin Layers? + +
        + +
        + +🏗️ Builder Programs + +_**Support at Every Step:**_ Stacks runs specialized builder programs that give you capital, feedback, and community validation, not just swag and tweets. You get structured support that compounds long after demo day. + +* \[[Stacks Ascent](https://stacks.org/ascent)] From Code to Company +* \[[Stacks Foundation](https://stacks.org/grants)] Grants & Bounty Programs + +
        diff --git a/docs/build/get-started/use-cases/README.md b/docs/build/get-started/use-cases/README.md new file mode 100644 index 0000000000..86b69b58bf --- /dev/null +++ b/docs/build/get-started/use-cases/README.md @@ -0,0 +1,12 @@ +--- +description: Exploring different use cases for building with Stacks, on Bitcoin. +--- + +# Use Cases + +
        + +Stacks extends Bitcoin's functionality, enabling a variety of decentralized applications. Here are some use cases for you to explore and incite new ideas. + +

        DeFi

        Enable smart contracts for lending, borrowing, and trading of digital assets.defi.md

        Art

        Facilitate the artistic creation and trading of digital art as NFTs.art.md

        Payments

        Enable fast, Bitcoin-settled transactions using assets like sBTC and STX, with developer tooling support and easy wallet integrations.payments.md

        Gaming

        Support on-chain economies, verifiable in-game assets as NFTs, and player rewards programs.gaming.md

        AI

        AI agents can hold sBTC, sign transactions, manage digital assets, and automate workflows while being bound by Clarity-level constraintsai.md
        + diff --git a/docs/build/get-started/use-cases/ai.md b/docs/build/get-started/use-cases/ai.md new file mode 100644 index 0000000000..c0304e5327 --- /dev/null +++ b/docs/build/get-started/use-cases/ai.md @@ -0,0 +1,75 @@ +--- +description: Use cases of AI on Stacks +--- + +# AI + +AI on **Stacks** enables verifiable, Bitcoin-secured AI applications. Developers can build decentralized AI marketplaces, create AI agents that safely manage sBTC under smart-contract rules, and generate verifiable inference receipts for trust and compliance. By combining Bitcoin security with Clarity and sBTC, Stacks provides a powerful foundation for trusted, permissionless, AI-powered apps. + +Here are some powerful examples of how **Stacks is unlocking on-chain AI experiences secured by Bitcoin:** + +### **x402-Stacks** + +x402 enables **automatic HTTP-level payments** for APIs, AI agents, and digital services using STX or sBTC tokens on Stacks. Pay only for what you use, right when you use it. No subscriptions, no API keys, no intermediaries. + +**Implementation highlight:**\ +The `x402-Stacks` library is a TypeScript library for implementing the x402 payment protocol on Stacks blockchain. This system supports multiple ways to handle paid API access: clients can pay automatically using an axios interceptor, or they can sign requests while a facilitator service reliably settles the payment on their behalf. Developers can also protect any Express.js endpoint with plug-and-play middleware. Pricing is fully flexible, supporting fixed, tiered, or dynamic fee models. + +{% code title="x402-stacks" expandable="true" %} +```typescript +import axios from 'axios'; +import { withPaymentInterceptor, privateKeyToAccount } from 'x402-stacks'; + +// Create account from private key +const account = privateKeyToAccount(process.env.PRIVATE_KEY!, 'testnet'); + +// Wrap axios with automatic payment handling +const api = withPaymentInterceptor( + axios.create({ baseURL: 'https://api.example.com' }), + account +); + +// Use normally - 402 payments are handled automatically! +const response = await api.get('/api/premium-data'); +console.log(response.data); +``` +{% endcode %} + +
        + +Check out more from x402-Stacks + +* \[[npm package](https://www.npmjs.com/package/x402-stacks)] x402-Stacks +* \[[Github](https://github.com/tony1908/x402Stacks)] Open-source repo for x402-Stacks +* \[[Twitter](https://x.com/toony1908/status/1996417973842858238)] Demo video of using x402-Stacks SDK + +
        + +*** + +### AIBTC + +The AI + BTC coordination network. AIBTC provides AI powered agents with Bitcoin and Stacks tooling. + +**Implementation highlight:**\ +The `aibtcdev-backend` bridges AI capabilities with Stacks blockchain technology to create intelligent DAO management experiences. The system provides real-time communication with AI agents that can autonomously interact with DAOs, create and evaluate proposals, execute trades, and manage blockchain accounts. + +{% code title="WebSocket Chat usage example" %} +```typescript +const ws = new WebSocket('ws://localhost:8000/chat/ws?token=your_token'); +ws.send(JSON.stringify({ + type: 'message', + thread_id: 'thread-uuid', + content: 'Hello, AI agent!' +})); +``` +{% endcode %} + +
        + +Check out more from AIBTC + +* \[[Official](https://aibtc.com/)] Official website of AIBTC +* \[[Github](https://github.com/aibtcdev)] Open-source repo for backend, frontend, and agent tooling support + +
        diff --git a/docs/build/get-started/use-cases/art.md b/docs/build/get-started/use-cases/art.md new file mode 100644 index 0000000000..7ae3a6176c --- /dev/null +++ b/docs/build/get-started/use-cases/art.md @@ -0,0 +1,122 @@ +--- +description: Use cases of art on Stacks +--- + +# Art + +Unlock expressive, Bitcoin-secured digital art with NFT assets — all backed by fast, low-cost execution on Stacks. The Stacks ecosystem brings programmability, provenance, and creative tooling to Bitcoin, giving artists and collectors the speed, flexibility, and cultural richness they’ve always wanted _without leaving Bitcoin_. Stacks expands what Bitcoin culture can be: native digital ownership, programmable collectibles, onchain storytelling, and communities that form directly around Bitcoin-secured art. + +Here are the creative use cases artists are exploring today: + +### **Megapont Ape Club** + +Megapont Ape Club is a pixel-art NFT collection built on Stacks. The “universe” behind Megapont is a fictional, stylized world — a retro-inspired pixel-art universe where mutated species evolved, with chimpanzees (apes) playing a central role. Beyond just apes, Megapont supports other NFT lines (e.g. Robot Factory, other “species / world” NFTs) and cross-chain / cross-platform assets. + +**Implementation highlight:**\ +The below code snippet is taken from the Megapont Ape's NFT contracts. This snippet shows two **internal minting functions** used. They are not public entrypoints—only callable from inside the contract. One handles _mintpass_-based presale minting, the other handles _public sale_ minting. + +* **Presale minting** has stricter access rules (mintpasses, whitelists, limits). +* **Public sale minting** is open to anyone once activated. +* The actual NFT lives in a separate contract (`.megapont-ape-club-nft`), keeping logic modular. + +{% code title=".megapont-ape-club-mint" fullWidth="false" expandable="true" %} +```clarity +;; ... + +;; Internal - Mint NFT using Mintpass mechanism +(define-private (mintpass-mint (new-owner principal)) + (let ((presale-balance (get-presale-balance new-owner))) + (asserts! (> presale-balance u0) ERR-NO-MINTPASS-REMAINING) + (map-set presale-count + new-owner + (- presale-balance u1)) + (contract-call? .megapont-ape-club-nft mint new-owner))) + +;; Internal - Mint public sale NFT +(define-private (public-mint (new-owner principal)) + (begin + (asserts! (var-get sale-active) ERR-SALE-NOT-ACTIVE) + (contract-call? .megapont-ape-club-nft mint new-owner))) + +;; ... +``` +{% endcode %} + +
        + +Check out more from Megapont + +* \[[Official](https://www.megapont.com/)] Official website of Megapont +* \[[Gamma](https://stacks.gamma.io/collections/megapont-ape-club)] NFT listing page on the Gamma marketplace +* \[[Hiro YT](https://youtu.be/mz1irJUpq0I?t=843)] Muneeb joining the Megapont Ape community +* \[[Hiro YT](https://youtu.be/xwbXNgSvMkk?si=jeHwWQ4oLlj9_ucr)] A Beginner's Overview of the Megapont Ape NFT Clarity Smart Contract +* \[[contract](https://explorer.hiro.so/txid/SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.megapont-ape-club-nft?chain=mainnet)] .megapont-ape-club-nft +* \[[contract](https://explorer.hiro.so/txid/SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.megapont-ape-club-mint?chain=mainnet\&tab=overview)] .megapont-ape-club-mint + +
        + +*** + +### Satoshibles + +Satoshibles is an early, pixel-art NFT collection that originally launched on Ethereum in 2021 and later bridged to Stacks, becoming one of the first established NFT communities to move onto the Bitcoin ecosystem. + +The project consists of 5000 algorithmically generated crypto collectible NFTs that have been hand illustrated. Each one unique, and each representing a fun, retro interpretation of “Satoshi-like” personas—quirky, expressive, and distinctly Bitcoin-themed. + +**Implementation highlights:**\ +This code snippet below enables a flexible, secure commission system by letting each NFT listing specify a commission contract, enforcing its use during purchase, and delegating fee payout logic to that external contract via the commission trait. By accepting a contract implementing `commission-trait`, it creates a _plug-and-play commission system_ where: + +* marketplaces decide their fee logic, +* creators can define custom royalty structures, +* different listings can use different commission rules. + +The marketplace contract enforces that the same commission contract passed at listing must also be used at purchase time. + +{% code title="SP6P4EJF0VG8V0RB3TQQKJBHDQKEF6NVRD1KZE3C.satoshibles" expandable="true" %} +```clarity +;; ... + +(use-trait commission-trait .commission-trait.commission) + +;; ... + +(define-public (list-in-ustx (id uint) (price uint) (comm )) + (let ((listing {price: price, commission: (contract-of comm)})) + (asserts! (is-sender-owner id) ERR-NOT-AUTHORIZED) + (map-set market id listing) + (print (merge listing {a: "list-in-ustx", id: id})) + (ok true))) + +(define-public (unlist-in-ustx (id uint)) + (begin + (asserts! (is-sender-owner id) ERR-NOT-AUTHORIZED) + (map-delete market id) + (print {action: "unlist-in-ustx", id: id}) + (ok true))) + +(define-public (buy-in-ustx (id uint) (comm )) + (let ((owner (unwrap! (nft-get-owner? Satoshibles id) ERR-NOT-FOUND)) + (listing (unwrap! (map-get? market id) ERR-LISTING)) + (price (get price listing))) + (asserts! (is-eq (contract-of comm) (get commission listing)) ERR-WRONG-COMMISSION) + (try! (stx-transfer? price tx-sender owner)) + (try! (contract-call? comm pay id price)) + (try! (trnsfr id owner tx-sender)) + (map-delete market id) + (print {action: "buy-in-ustx", id: id}) + (ok true))) + +;; ... +``` +{% endcode %} + +
        + +Check out more from Satoshibles + +* \[[Official](https://satoshibles.com/)] Official website of Satoshibles +* \[[contract](https://explorer.hiro.so/txid/SP6P4EJF0VG8V0RB3TQQKJBHDQKEF6NVRD1KZE3C.satoshibles?tab=overview)] SP6P4EJF0VG8V0RB3TQQKJBHDQKEF6NVRD1KZE3C.satoshibles +* \[[Gamma](https://gamma.io/stacks/collections/satoshibles/items)] Satoshibles' listing page on Gamma marketplace +* \[[YT](https://www.youtube.com/watch?v=gZw4EvV6qig)] BRIDGE from ETH to Stacks | Satoshibles + +
        diff --git a/docs/build/get-started/use-cases/defi.md b/docs/build/get-started/use-cases/defi.md new file mode 100644 index 0000000000..3516e98281 --- /dev/null +++ b/docs/build/get-started/use-cases/defi.md @@ -0,0 +1,122 @@ +--- +description: Use cases of DeFi on Stacks +--- + +# DeFi + +Stacks enables users to put their BTC to work in trust-minimized ways: borrowing against it, providing liquidity, earning yield, or participating in programmable financial products that settle on Bitcoin. This ecosystem gives builders the foundation to extend Bitcoin from a passive store of value into an active financial layer. + +Here are some powerful demonstrations of unlocking DeFi for Bitcoin: + +### Hermetica + +Hermetica is a DeFi protocol built on Stacks whose mission is to bring fully Bitcoin-native stablecoins and yield products to the Bitcoin ecosystem. Their flagship asset is USDh — a Bitcoin-backed, yield-bearing synthetic dollar. + +**Implementation highlight:**\ +The code snippet below highlights the **migration lock**, **manager assignment**, and **token deprecation logic** all in one tight block. It captures the “power unlock” of the contract without pulling in the entire file: + +* Safely upgrade USDh by freezing old token operations once migration starts. +* Assigns a migration manager to burn and migrate balances securely. +* Ensures consistent supply and a trust-minimized transition to the new token. + +{% code title="SPN5AKG35QZSK2M8GAMR4AFX45659RJHDW353HSG.usdh-token-v1" expandable="true" %} +```clarity +;; Prevents all token activity once migration has begun +(define-read-only (is-not-migrated) + (ok (asserts! (is-eq u0 (var-get migration-start-height)) ERR_DEPRECATED_TOKEN)) +) + +;; Begins the migration process and hands control to a migration manager contract +(define-public (start-migration (manager )) + (begin + (try! (is-not-migrated)) + (try! (contract-call? .hq-v1 check-is-owner contract-caller)) + (var-set migration-start-height burn-block-height) + (var-set migration-manager (some (contract-of manager))) + (contract-call? manager start-migration burn-block-height (ft-get-supply usdh)) + ) +) + +;; Allows the designated migration manager to burn a user’s balance and extract it for migration +(define-public (migrate-balance (who principal)) + (let ((balance (ft-get-balance usdh who))) + (asserts! (is-eq (var-get migration-manager) (some contract-caller)) ERR_NOT_MIGRATION_MANAGER) + (asserts! (> balance u0) (ok u0)) + (try! (ft-burn? usdh balance who)) + (ok balance) + ) +) + +``` +{% endcode %} + +
        + +Check out more from Hermetica + +* \[[Official](https://hermetica.fi/)] Official website of Hermetica +* \[[Stacks YT](https://youtu.be/Xb54LJrLicY?si=3wMTBwOskphdsn4S)] Earn up to 25% with Your BTC as Collateral with Jakob Schillinger +* \[[Hiro YT](https://www.youtube.com/watch?v=R6f4jR8S45M)] Lessons From Building Bitcoin DeFi +* \[[Hiro Blog](https://www.hiro.so/blog/how-hermetica-uses-chainhook-to-track-bitcoin-deposits)] How Hermetica Uses Chainhook to Track Bitcoin Deposits +* \[[contract](https://explorer.stacks.co/token/SPN5AKG35QZSK2M8GAMR4AFX45659RJHDW353HSG.usdh-token-v1?chain=mainnet)] SPN5AKG35QZSK2M8GAMR4AFX45659RJHDW353HSG.usdh-token-v1 + +
        + +*** + +### StackingDAO + +StackingDAO is the hub for liquid stacking on Stacks. One of their liquid stacking services **stSTXbtc**, is a liquid stacking token (LST) backed 1-to-1 with STX, and holders receive sBTC rewards daily that can be claimed at any moment. + +**Implementation highlight:**\ +This function **initiates a withdrawal of stSTXbtc**, locks the underlying STX for withdrawal, burns the derivative token, and mints an NFT that represents the user’s withdrawal claim. It effectively transforms a liquid derivative token into a time-locked withdrawal right. + +* Enforces protocol-level safety checks +* Determines the unlock Bitcoin (burn) height +* Links the withdrawal to an NFT “claim ticket” +* Coordinates state changes across multiple protocol contracts + +{% code title="SP4SZE494VC2YC5JYG7AYFQ44F5Q4PYV7DVMDPBG.stacking-dao-core-btc-v2" fullWidth="true" expandable="true" %} +```clarity +;; --snip-- + +(define-public (init-withdraw + (reserve ) + (direct-helpers ) + (ststxbtc-amount uint) +) + (let ( + (sender tx-sender) + (unlock-burn-height (unwrap-panic (contract-call? .stacking-dao-core-v4 get-withdraw-unlock-burn-height))) + + (nft-id (unwrap-panic (contract-call? .ststxbtc-withdraw-nft get-last-token-id))) + ) + (try! (contract-call? .dao check-is-enabled)) + (try! (contract-call? .dao check-is-protocol (contract-of reserve))) + (try! (contract-call? .dao check-is-protocol (contract-of direct-helpers))) + (asserts! (not (get-shutdown-init-withdraw)) (err ERR_SHUTDOWN)) + + (try! (contract-call? .data-core-v2 set-ststxbtc-withdrawals-by-nft nft-id ststxbtc-amount unlock-burn-height)) + + (try! (contract-call? direct-helpers subtract-direct-stacking tx-sender ststxbtc-amount)) + + ;; Burn stSTXbtc tokens + (try! (as-contract (contract-call? reserve lock-stx-for-withdrawal ststxbtc-amount))) + (try! (contract-call? .ststxbtc-token-v2 burn-for-protocol ststxbtc-amount sender)) + (try! (as-contract (contract-call? .ststxbtc-withdraw-nft mint-for-protocol sender))) + + (print { action: "init-withdraw", data: { stacker: tx-sender, nft-id: nft-id, ststxbtc-amount: ststxbtc-amount, unlock-burn-height: unlock-burn-height, block-height: block-height } }) + (ok nft-id) + ) +) +``` +{% endcode %} + +
        + +Check out more from StackingDAO + +* \[[Hiro YT](https://youtu.be/1hWYeqS5r-k?si=GRWag60DUmm08TG_)] Going Under the Hood of stSTXbtc with Philip De Smedt from StackingDAO +* \[[Hiro YT](https://youtu.be/ujpatSY9DhM?si=cr823veqbmcfYr7y)] A Simple Breakdown of the StackingDAO Clarity Contracts on Stacks + +
        diff --git a/docs/build/get-started/use-cases/gaming.md b/docs/build/get-started/use-cases/gaming.md new file mode 100644 index 0000000000..2673225ccc --- /dev/null +++ b/docs/build/get-started/use-cases/gaming.md @@ -0,0 +1,91 @@ +--- +description: Use cases of gaming on Stacks +--- + +# Gaming + +Gaming is one of the most powerful entry points for bringing millions of new users into the Bitcoin ecosystem, and Stacks unlocks this opportunity with onchain logic secured by Bitcoin itself. By enabling fast, low-cost transactions, expressive smart contracts, and asset ownership through NFTs and fungible tokens, Stacks gives game developers the tools to build richer in-game economies, verifiable digital ownership, and player-driven marketplaces—all anchored to Bitcoin’s security. This combination lets games move beyond simple collectibles and into fully programmable, decentralized worlds where players truly own their assets and developers can design deeper incentives, interoperable items, and sustainable onchain economies. + +Here are some powerful examples of how Stacks is unlocking on-chain gaming for Bitcoin: + +### Skullcoin + +Skullcoin is a Web3 gaming project building a new genre called Find2Earn — treasure hunt games powered by Encrypted NFTs and real on-chain rewards. An Encrypted NFT is a new type of digital asset with two layers of information: a public layer visible to everyone, and a private encrypted layer that can only be revealed by the owner of NFT. + +**Implementation highlight:**\ +Here's a high-level breakdown of Skullcoin's Encrypted NFTs: + +* **What happens on-chain**: standard SIP-009 NFTs + commitments (hashes / indexes / events) that define what’s “locked” and who has the right to unlock it; +* **What happens off-chain**: the actual encrypted payload (image / text / coords) lives in storage and is only served after the on-chain proof/conditions are satisfied. + +{% code title="skullcoin-competitive-seed-phase2.clar" %} +```clarity +;; --snip-- + +(define-private (is-sender-owner (id uint)) + (let ((owner (unwrap! (nft-get-owner? skullcoin_competitive_seed_p2 id) false))) + (or (is-eq tx-sender owner) (is-eq contract-caller owner)))) + +;; --snip-- +``` +{% endcode %} + +
        + +Check out more from Skullcoin + +* \[[Official](https://skullco.in/)] Official website of Skullcoin +* \[[Whitepaper](https://docs.skullco.in/)] Skullcoin whitepaper/docs +* \[[contracts](https://github.com/proofofgame/find_to_earn)] Github repo for Skullcoin's Find2Earn contracts + +
        + +*** + +### Cryptonauts + +Cryptonauts is a multiplayer GameFi Experience built on Unreal Engine 5 and powered by **Stacks**, a Bitcoin-anchored smart contract layer. Cryptonauts integrates Stacks wallet authentication, NFT verification, and on-chain asset logic directly into Unreal Engine, enabling features such as player skin ownership validation, Codex Component loadouts (NFT-based abilities), and signed game session data for Web3 interoperability. + +**Implementation highlight:**\ +Cryptonauts leverages BNS for player identification. + +* **Human-readable identities for players** — instead of using cryptographic addresses, BNS lets players show names like `cryptodude.btc`. This makes in-game identities more memorable, personality-driven, and social. +* **Persistent identity + on-chain history** — BNS allows binding off-chain state to names and linking with on-chain state. That means a player’s actions, assets, and progress can be tied to a stable identity — even if they change wallets. + +{% code title="https://api.bnsv2.com/names/address/SP3WAR3N1XRR139DXCGPR1ATPK2VN63PGRXTD537N/valid" expandable="true" %} +```json +{ + "total": 2, + "current_burn_block": 921664, + "limit": 50, + "offset": 0, + "names": [ + { + "full_name": "cryptodude.btc", + "name_string": "cryptodude", + "namespace_string": "btc", + "owner": "SP3WAR3N1XRR139DXCGPR1ATPK2VN63PGRXTD537N", + "registered_at": "28369", + "renewal_height": "1125897", + "stx_burn": "2000000", + "revoked": false + } + ] +} +``` +{% endcode %} + +
        + +Check out more from Cryptonauts + +* \[[docs](https://cryptonauts.gitbook.io/cryptonauts-docs)] Cryptonauts docs + +
        + +*** + +### Additional Resources + +* \[[Hiro Blog](https://www.hiro.so/blog/its-time-to-stake-your-claim-on-the-future-of-web3-gaming)] It’s Time to Stake Your Claim on the Future of Web3 Gaming +* \[[Hiro Blog](https://www.hiro.so/blog/what-are-gaming-nfts-and-how-can-they-drive-bitcoin-adoption)] What Are Gaming NFTs and How Can They Drive Bitcoin Adoption? diff --git a/docs/build/get-started/use-cases/payments.md b/docs/build/get-started/use-cases/payments.md new file mode 100644 index 0000000000..7c6da75286 --- /dev/null +++ b/docs/build/get-started/use-cases/payments.md @@ -0,0 +1,133 @@ +--- +description: Use cases of payments on Stacks +--- + +# Payments + +Enable fast, Bitcoin-settled transactions using assets like sBTC and STX, with developer tooling support and easy wallet integrations. Stacks brings the liquidity, speed, and logic of modern payments to Bitcoin — fast digital transfers, smarter payment rules, and the UX digital payments deserve. + +Here are the payment use cases developers are building with today: + +### sBTC Pay + +sBTC Pay provides a payment gateway with APIs and webhooks for developers to instantly integrate a simple sBTC payment system in their app. A complete _**"Stripe for sBTC"**_ payment gateway that enables businesses to easily accept Bitcoin payments via sBTC on Stacks blockchain. + +**Implementation highlight:**\ +sBTC Pay comes with a few integration methods developers can start using right away. The React component from its `@sbtc-gateway/react` library provides UI components for readily usable sBTC payments. + +{% code title="@sbtc-gateway/react" %} +```typescript +import { SBTCProvider, PaymentButton } from '@sbtc-gateway/react'; + +function App() { + return ( + + console.log('Success!', paymentIntent)} + /> + + ); +} +``` +{% endcode %} + +
        + +Check out more from sBTC Pay + +* [Official website](https://sbtcpay.org/) +* [Demo video](https://x.com/kai_builder/status/1962151430535700891) +* [Winner of the Stacks Builder Competition](https://x.com/kai_builder/status/1967806436387459388) +* [Github repo](https://github.com/STX-CITY/sbtc-pay) + +
        + +*** + +### Bolt Wallet + +Bolt wallet not only enables sBTC as transaction fees, but also boasts lightning bolt speed for payments on Stacks while guaranteeing transaction confirmation. This is made possible by Stacks' sponsor transaction feature and optimistic confirmations. + +**Implementation highlight:**\ +The code snippet below demonstrates how to transfer sBTC between Stacks wallets while paying the fee in sBTC instead of STX using Bolt Protocol's sponsorship feature. + +{% code title="https://github.com/ronoel/bolt-protocol/blob/main/cookbook/transfer-stacks-to-stacks.md" expandable="true" %} +```typescript +import { STACKS_TESTNET } from "@stacks/network"; +import { bytesToHex } from "@stacks/common"; +import { + Cl, + FungiblePostCondition, + makeContractCall, + PostConditionMode, + SignedContractCallOptions, +} from "@stacks/transactions"; + +async function transferStacksToStacks() { + // Amount and fee in satoshis + const amount = 100000000; // 1 sBTC + const fee = 10; // 10 satoshis minimum fee + + // Post condition to ensure exact amount+fee is spent + const ftPostCondition: FungiblePostCondition = { + type: 'ft-postcondition', + address: "", // Replace with actual sender address + condition: 'eq', + amount: amount + fee, + asset: 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token::sbtc-token' + }; + + // Transaction options + const txOptions: SignedContractCallOptions = { + sponsored: true, // Enable sponsorship + senderKey: "", // Replace with sender's private key + network: STACKS_TESTNET, + contractAddress: "SP3QZNX3CGT6V7PE1PBK17FCRK1TP1AT02ZHQCMVJ", + contractName: "boltproto-sbtc-v1", + functionName: "transfer-stacks-to-stacks", + functionArgs: [ + Cl.uint(amount), + Cl.principal(""), // Replace with recipient address + Cl.none(), // No memo + Cl.uint(fee) + ], + postConditionMode: PostConditionMode.Deny, + postConditions: [ftPostCondition], + }; + + // Create and serialize the transaction + const transaction = await makeContractCall(txOptions); + const serializedTx = bytesToHex(transaction.serializeBytes()); + + // Submit to Bolt Protocol API + const response = await fetch('https://boltproto.org/api/v1/transaction/sbtc-token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ serializedTx }) + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + console.log('Transaction submitted:', data.txid); + return data; +} +``` +{% endcode %} + +
        + +Check out more from Bolt wallet + +* [Official website for Bolt](https://boltproto.org/) +* [Hiro Dev N' Tell with Ronoel.btc showcasing Bolt wallet in action](https://youtu.be/iuNQ88VoRiU?si=xR7hf5N0VJQlz1rm) +* [Bolt Protocol](https://x.com/boltprotobtc) +* [Github repo](https://github.com/ronoel/bolt-protocol) + +
        diff --git a/docs/build/more-guides/c32check.md b/docs/build/more-guides/c32check.md new file mode 100644 index 0000000000..d9a6926725 --- /dev/null +++ b/docs/build/more-guides/c32check.md @@ -0,0 +1,115 @@ +--- +description: Generating and decoding addresses on the Stacks blockchain. +--- + +# c32check + +
        + +{% hint style="info" %} +For the c32check open-source repo: [https://github.com/stacks-network/c32check](https://github.com/stacks-network/c32check) +{% endhint %} + +The Stacks blockchain uses c32-encoded public key hashes as addresses. Specifically, a **c32check address** is a c32check-encoded ripemd160 hash. This library is meant for generating and decoding addresses on the Stacks blockchain. + +### How it works + +Each c32check string encodes a 1-byte version and a 4-byte checksum. When decoded as a hex string, the wire format looks like this: + +``` +0 1 n+1 n+5 +|------|-----------------------------|---------------| +version n-byte hex payload 4-byte hash +``` + +If `version` is the version byte (a 1-byte `number`) and `payload` is the raw bytes (e.g. as a `string`), then the `checksum` is calculated as follows: + +``` +checksum = sha256(sha256(version + payload)).substring(0,4) +``` + +In other words, the checksum is the first four bytes of the double-sha256 of the bytestring concatenation of the `version` and `payload`. This is similar to base58check encoding, for example. + +### Examples + +**Installation** + +{% code title="terminal" %} +```shellscript +npm install c32check +``` +{% endcode %} + +```typescript +> c32 = require('c32check') +{ c32encode: [Function: c32encode], + c32decode: [Function: c32decode], + c32checkEncode: [Function: c32checkEncode], + c32checkDecode: [Function: c32checkDecode], + c32address: [Function: c32address], + c32addressDecode: [Function: c32addressDecode], + versions: + { mainnet: { p2pkh: 22, p2sh: 20 }, + testnet: { p2pkh: 26, p2sh: 21 } }, + c32ToB58: [Function: c32ToB58], + b58ToC32: [Function: b58ToC32] } +``` + +#### c32encode, c32decode + +```typescript +> c32check.c32encode(Buffer.from('hello world').toString('hex')) +'38CNP6RVS0EXQQ4V34' +> c32check.c32decode('38CNP6RVS0EXQQ4V34') +'68656c6c6f20776f726c64' +> Buffer.from('68656c6c6f20776f726c64', 'hex').toString() +'hello world' +``` + +#### c32checkEncode, c32checkDecode + +```typescript +> version = 12 +> c32check.c32checkEncode(version, Buffer.from('hello world').toString('hex')) +'CD1JPRV3F41VPYWKCCGRMASC8' +> c32check.c32checkDecode('CD1JPRV3F41VPYWKCCGRMASC8') +[ 12, '68656c6c6f20776f726c64' ] +> Buffer.from('68656c6c6f20776f726c64', 'hex').toString() +'hello world' +``` + +#### c32address, c32addressDecode + +> **Note**: These methods only work on ripemd160 hashes + +```typescript +> hash160 = 'a46ff88886c2ef9762d970b4d2c63678835bd39d' +> version = c32check.versions.mainnet.p2pkh +22 +> c32check.c32address(version, hash160) +'SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7' +> c32check.c32addressDecode('SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7') +[ 22, 'a46ff88886c2ef9762d970b4d2c63678835bd39d' ] +``` + +#### c32ToB58, b58ToC32 + +Convert a Stacks address to its corresponding Bitcoin address, or vice versa. + +> **Note**: Common address versions are converted between c32check and base58check seamlessly, in order to accommodate Stacks addresses. + +```typescript +> b58addr = '16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg' +> c32check.b58ToC32(b58addr) +'SPWNYDJ3STG7XH7ERWXMV6MQ7Q6EATWVY5Q1QMP8' +> c32check.c32ToB58('SPWNYDJ3STG7XH7ERWXMV6MQ7Q6EATWVY5Q1QMP8') +'16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg' +``` + +```typescript +> b58addr = '3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r' +> c32check.b58ToC32(b58addr) +'SM1Y6EXF21RZ9739DFTEQKB1H044BMM0XVCM4A4NY' +> c32check.c32ToB58('SM1Y6EXF21RZ9739DFTEQKB1H044BMM0XVCM4A4NY') +'3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r' +``` diff --git a/docs/build/more-guides/community-tutorials.md b/docs/build/more-guides/community-tutorials.md new file mode 100644 index 0000000000..54448e5058 --- /dev/null +++ b/docs/build/more-guides/community-tutorials.md @@ -0,0 +1,27 @@ +# Community Tutorials + +

        source: EasyA x Stacks hackathon at Harvard University [2024]

        + +These tutorials have been created by various developers of the Stacks community. Have a tutorial to suggest? View the [contribution guide](http://localhost:3000/docs/contribute/docs) to submit a PR with a new tutorial added. + +### Written Tutorials + +* [Create a server-side rendered Stacks app with Remix](https://micro-stacks.dev/guides/with-remix) +* [Build a Stacks app with Next.js](https://micro-stacks.dev/guides/with-nextjs) +* [Creating a Voting Contract](https://www.clearness.dev/01-voting-clarity-smart-contract/01-getting-started) +* [Building an NFT with Stacks and Clarity](https://blog.developerdao.com/building-an-nft-with-stacks-and-clarity) +* [Minting NFTs with QuickNode](https://www.quicknode.com/guides/web3-sdks/how-to-mint-nfts-on-the-stacks-blockchain) +* [Order Book Contract Walkthrough](https://byzantion.hiro.so/) +* [Build a DEX on Stacks](https://www.pointer.gg/tutorials/build-a-dex-with-stacks/56abb3a4-05c1-4608-b096-f82189e9f759) +* [NFT Tutorial](https://docs.hiro.so/tutorials/clarity-nft) +* [Billboard Tutorial](https://docs.hiro.so/tutorials/clarity-billboard) +* [Integrating NFTs Into a Game](https://gamefi-stacks.gitbook.io/gamefistacks/tutorials/integrate-nfts-into-game) +* [Building on Stacks](https://github.com/amoweolubusayo/stacks-clarinet-tutorial) + +### Video Tutorials + +* [Web3 for Bitcoin](https://www.crowdcast.io/e/web3-for-bitcoin/) + +### Other Resources + +There are also a great amount of both tutorials and developer tools in the [Awesome Stacks repo](https://github.com/friedger/awesome-stacks-chain#clarity-resources). diff --git a/docs/build/more-guides/onboarding/README.md b/docs/build/more-guides/onboarding/README.md new file mode 100644 index 0000000000..6c36225277 --- /dev/null +++ b/docs/build/more-guides/onboarding/README.md @@ -0,0 +1,14 @@ +--- +description: Guides to assist the onboarding experience of your app +--- + +# Onboarding + +
        + +A smooth onboarding process is crucial for applications because it directly affects user retention and engagement. Here are a few reasons why it is important: + +* **User Retention**: A seamless onboarding experience helps in retaining new users by reducing friction and making it easier for them to get started with the app. +* **First Impressions**: The onboarding process shapes the first impressions of the application. A positive experience can lead to increased trust and credibility. + +By prioritizing a well-designed onboarding process, developers can enhance user experience and promote wider adoption of Bitcoin/Stacks applications. diff --git a/docs/build/more-guides/onboarding/signing-with-turnkey.md b/docs/build/more-guides/onboarding/signing-with-turnkey.md new file mode 100644 index 0000000000..1d6ca61b3c --- /dev/null +++ b/docs/build/more-guides/onboarding/signing-with-turnkey.md @@ -0,0 +1,181 @@ +--- +description: Leverage Stacks.js with Turnkey's embedded wallet solutions +--- + +# Signing with Turnkey + +[Turnkey’s embedded wallet](https://docs.turnkey.com/embedded-wallets/overview) makes STX & sBTC transactions seamless, secure, and user-friendly, letting your users experience Bitcoin-native apps without friction. Using external embedded wallet/signing solutions with Stacks comes down to properly passing in a hashed transaction payload to a provided signing method. We'll show you how to derive that hashed transaction payload in this guide. + +{% hint style="info" %} +Currently, Turnkey does not natively support Stacks, but can be simply integrated together using the example below. Work is in progress for native Stacks support in Turnkey. +{% endhint %} + +### Address derivation + +Turnkey supports Stacks address derivation with `ADDRESS_FORMAT_COMPRESSED` and `ADDRESS_FORMAT_UNCOMPRESSED` address formats. Stacks addresses are derived from the secp256k1 curve, which Turnkey fully supports. + +{% hint style="info" %} +Use a secp256k1 generated public key to derive Stacks addresses and sign Stacks transactions. In most cases with Turnkey, using an Ethereum account's public key would satisfy this. +{% endhint %} + +```typescript +import { publicKeyToAddress } from "@stacks/transactions" + +const stxAddress = publicKeyToAddress(matchingAccount?.publicKey!) + +// STX address: SP1Z6MYME47PW04D1J15K368XZE02VWQ2A5SRC4HV +// publicKey: 03169b8f8bbad2cc6435893c5f255cd5201d272befa8556c82136bf9b36aa0d778 +``` + +### Transaction construction and signing + +A sample script that demonstrates how to sign a Stacks transaction with Turnkey. Stacks uses the secp256k1 cryptographic curve for transaction signing, but there some specific data formatting that takes place for the signing process. + +Simplified step-by-step process of what this example script is showing: + +1. Generate `sigHash` from unsigned transaction +2. Generate `preSignSigHash` +3. ECDSA sign `preSignSigHash` with a Turnkey private key +4. Concatenate outputted raw signature (from step 3) components in the order of V + R + S +5. The resulting signature of step 4 will be the `nextSig` +6. Reassign `spendingCondition.signature` with the value of `nextSig` + +**Note:** The hashFunction `HASH_FUNCTION_NO_OP` should be set this way because the payload has already been hashed. + +{% hint style="info" %} +Signing Stacks transactions works with either Turnkey's Server or React SDKs +{% endhint %} + +```typescript +import { Turnkey as TurnkeyServerSDK } from "@turnkey/sdk-server"; +import { + broadcastTransaction, + createMessageSignature, + makeUnsignedSTXTokenTransfer, + sigHashPreSign, + SingleSigSpendingCondition, + TransactionSigner, + publicKeyToAddress, + type StacksTransactionWire, +} from "@stacks/transactions"; +import * as path from "path"; +import * as dotenv from "dotenv"; + +// Load environment variables from `.env.local` +dotenv.config({ path: path.resolve(process.cwd(), ".env.local") }); + +// Define the Turnkey API client +const client = new TurnkeyServerSDK({ + apiBaseUrl: process.env.TURNKEY_BASE_URL!, + apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY!, + apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY!, + defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID!, +}); + +// Construct an unsigned Stacks transaction +const constructStacksTx = async (pubKey: string) => { + const recipient = process.env.STACKS_RECIPIENT_ADDRESS!; + + let transaction = await makeUnsignedSTXTokenTransfer({ + recipient, + amount: 300, + publicKey: pubKey, + numSignatures: 1, + network: "mainnet", + }); + + return { stacksTransaction: transaction, sighash: transaction.signBegin() }; +}; + +// The resulting preSignSigHash is what ultimately gets signed by a private key using ECDSA over the secp256k1 curve. +const generatePreSignSigHash = ( + transaction: StacksTransactionWire, + sighash: string +) => { + let preSignSigHash = sigHashPreSign( + sigHash, + transaction.auth.authType, + transaction.auth.spendingCondition.fee, + transaction.auth.spendingCondition.nonce, + ); + + return preSignSigHash; +}; + +const signStacksTx = async () => { + try { + // Grab the public key associated with the user's Turnkey Ethereum account as a proxy + // Ethereum's public key generation and signing curve matches that of Stacks + // Use `publicKeyToAddress` to convert public key to Stacks address for display purposes + const stacksPublicKey = process.env.TURNKEY_SIGNER_PUBLIC_KEY!; + + let { stacksTransaction, stacksTxSigner } = await constructStacksTx( + stacksPublicKey!, + ); + let preSignSigHash = generatePreSignSigHash( + stacksTransaction, + stacksTxSigner, + ); + + const payload = `0x${preSignSigHash}`; + + const signature = await client?.apiClient().signRawPayload({ + payload, + // either passing in pubkey or account address + signWith: stacksPublicKey, + encoding: "PAYLOAD_ENCODING_HEXADECIMAL", + hashFunction: "HASH_FUNCTION_NO_OP", + }); + + // r and s values returned are in hex format, may need to padStart r and s values + // v should be "00" for Stacks but the returned "01" also works + const nextSig = `${signature!.v}${signature!.r.padStart(64, "0")}${signature!.s.padStart(64, "0")}`; + + // Reassign signature field in transaction with `nextSig` + let spendingCondition = stacksTransaction.auth.spendingCondition as SingleSigSpendingCondition; + spendingCondition.signature = createMessageSignature(nextSig); + + return stacksTransaction; + } catch (err) { + console.error("Signing failed:", err); + return undefined; + } +}; + +const handleBroadcastTx = async () => { + let tx = await signStacksTx(); + + let result = await broadcastTransaction({ + transaction: tx!, + network: "mainnet", + }); + + console.log("Broadcast Result:", result); +}; + +(async () => { + await handleBroadcastTx(); +})(); +``` + +### **Components of a ECDSA signature** + +* **r (32 bytes)**: The x-coordinate of a point on the elliptic curve, derived during the signing process. +* **s (32 bytes)**: A scalar derived from the message hash, private key, and the nonce k. +* **v (1 byte)**: Indicates which of the two possible public keys was used to generate the signature. + +### **Why Use Turnkey Embedded Wallets for Stacks Apps** + +* **Seamless onboarding** – Users can start interacting with your app immediately, without installing separate wallets or extensions. +* **Simplified authentication** – Turnkey handles secure key management and signing, including modern standards like passkeys. +* **Improved UX** – Embedded wallets reduce friction in transactions, making dApps feel more like mainstream apps. +* **Multi-chain-ready** – Easily support STX, sBTC and other blockchain assets without building your own wallet infrastructure. + +*** + +### Additional Resources + +* \[[Hiro Blog](https://www.hiro.so/blog/dissecting-a-transaction-signature-on-stacks)] Dissecting a Transaction Signature on Stacks +* \[[Twitter](https://x.com/ECBSJ/status/1976286764018077933)] Stacks DevRel Office Hours with Turnkey's Michael Lewellen +* \[[Twitter](https://x.com/kai_builder/status/1977059253379834059)] sBTC.Cool - Example project on Stacks integrating Turnkey +* \[[Twitter](https://x.com/turnkeyhq/status/1946239286686241228)] Turnkey supporting transaction signing for Stacks diff --git a/docs/build/more-guides/price-oracles/README.md b/docs/build/more-guides/price-oracles/README.md new file mode 100644 index 0000000000..c80a5be6d0 --- /dev/null +++ b/docs/build/more-guides/price-oracles/README.md @@ -0,0 +1,46 @@ +--- +description: Leverage real-time market price data in your Clarity smart contract +--- + +# Price Oracles + +
        + +Smart contracts written in **Clarity** run in a deterministic sandbox: they can read data in the Stacks and Bitcoin chainstate, but _nothing else_. Whenever your dApp needs the latest **BTC/USD**, **STX/BTC**, or any other market price, you’ll rely on an **oracle** to bring that data on‑chain in a verifiable way. + +This page explains why price‑feed oracles matter on Stacks and links to the specific oracle provider docs with instructions on how to integrate them. + +*** + +## Why you need a price‑feed oracle + +For DeFi smart contracts, it’s crucial to leverage trusted sources for asset pricing, which has profound implications for investor returns and trading strategies. + +Here are some possible scenarios where you might need an oracle. + +| On‑chain need | Typical Stacks use case | What the oracle supplies | +| ------------------------------------ | ---------------------------------------------- | ----------------------------------- | +| **Liquidations & collateral ratios** | Lending / borrowing protocols, margin trading | Signed price updated every N blocks | +| **Stablecoin peg maintenance** | BTC‑backed or exogenous‑collateral stablecoins | Reference BTC/USD (or other) price | +| **AMM curve calculations** | DEXs that tune fees or rebalance pools | Time‑weighted average price (TWAP) | +| **Derivatives settlement** | Options, futures, or perpetual swaps | Final settlement price at expiry | + +{% hint style="info" %} +Rule of thumb: if your contract’s math depends on a real‑time market price, you need a price‑feed oracle. +{% endhint %} + +## Oracle Providers for Stacks + +Here are the currently available oracle providers that Stacks builders commonly use for price data. + +### **Pyth** + +Pyth is a pull-based oracle. Stacks Labs currently maintains the Pyth bridge. + +[Learn how to use Pyth.](pyth.md) + +### **DIA** + +DIA is another oracle provider used by Stacks builders. See DIA's [guide](https://nexus.diadata.org/how-to-guides/fetch-price-data/chain-specific-guide/stacks) for how to use DIA oracles with Stacks. Check out the video tutorial to learn more on how DIA works for Clarity smart contracts: + +{% embed url="https://youtu.be/bhWQxHGpv2s?si=dWlBAEAuYtoQj2sC" %} diff --git a/docs/build/more-guides/price-oracles/dia.md b/docs/build/more-guides/price-oracles/dia.md new file mode 100644 index 0000000000..31cda65e57 --- /dev/null +++ b/docs/build/more-guides/price-oracles/dia.md @@ -0,0 +1,75 @@ +# Using DIA with Stacks + +DIA's oracle protocol utilizes a push price model. As the developer, you would only need to fetch prices directly from DIA's Clarity contracts. + +Price data can be fetched in Clarity by making an external call to the DIA `.dia-oracle` contract’s `get-value` read-only function with the desired asset pair. + +### DIA's Clarity Contracts + +{% hint style="info" %} +For the full list of supported asset feeds for Stacks with DIA, check out their docs [here](https://www.diadata.org/docs/nexus/how-to-guides/fetch-price-data/chain-specific-guide/stacks). +{% endhint %} + +
        ChainAddress
        MainnetSP1G48FZ4Y7JY8G2Z0N51QTCYGBQ6F4J43J77BQC0.dia-oracle
        TestnetST1S5ZGRZV5K4S9205RWPRTX9RGS9JV40KQMR4G1J.dia-oracle
        + +### Example + +Below is an example where we are fetching the sBTC price and determining if a user has the required minimum balance to join a whitelist. + +It does this by: + +* Reading the **current sBTC/USD price** from DIA’s on-chain oracle +* Reading the caller’s **sBTC balance** +* Calculating the USD value of that balance +* Whitelisting the user if they meet a minimum threshold + +In short: **prove you hold enough sBTC (by USD value) to be eligible for a whitelist.** + +{% hint style="warning" %} +All price feeds from DIA's Clarity contracts all have an implicit decimal place of 8 unless specified otherwise. +{% endhint %} + +
        (define-constant MIN-SBTC-BALANCE u100)
        +(define-constant ERR_READING_SBTC_BALANCE (err u7001))
        +(define-constant ERR_NOT_ENOUGH_SBTC (err u7002))
        +(define-constant ERR_NOT_OWNER (err u7003))
        +(define-constant SBTC-PRICE-EXPO 8)
        +
        +(define-map whitelist
        +  principal
        +  bool
        +)
        +
        +(define-public (check-eligibility)
        +  (let (
        +      (sbtc-price-data (unwrap-panic (contract-call? 'SP1G48FZ4Y7JY8G2Z0N51QTCYGBQ6F4J43J77BQC0.dia-oracle
        +        get-value "sBTC/USD"
        +      )))
        +      (sbtc-usd-price (to-int (get value sbtc-price-data)))
        +      (price-denomination (pow 10 SBTC-PRICE-EXPO))
        +      (adjusted-price (to-uint (/ sbtc-usd-price price-denomination)))
        +      (user-sbtc-balance (unwrap!
        +        (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
        +          get-balance-available tx-sender
        +        )
        +        ERR_READING_SBTC_BALANCE
        +      ))
        +    )
        +    (if (> (/ (* user-sbtc-balance adjusted-price) (to-uint price-denomination))
        +        MIN-SBTC-BALANCE
        +      )
        +      (ok (map-set whitelist tx-sender true))
        +      ERR_NOT_ENOUGH_SBTC
        +    )
        +  )
        +)
        +
        +
        + +*** + +### Additional Resources + +* \[[Hiro YT](https://youtu.be/bhWQxHGpv2s?si=RsKaCe169Vu6zw_e)] How to use DIA's price data featuring Khawla, Product Manager @ DIA +* \[[DIA Docs](https://www.diadata.org/docs/nexus/how-to-guides/fetch-price-data/chain-specific-guide/stacks)] The Stacks guide in DIA's docs +* \[[Twitter](https://x.com/DIAdata_org/status/1945476086110032147)] DIA x Stacks Oracle Grants diff --git a/docs/build/more-guides/price-oracles/pyth.md b/docs/build/more-guides/price-oracles/pyth.md new file mode 100644 index 0000000000..d4169d3f4b --- /dev/null +++ b/docs/build/more-guides/price-oracles/pyth.md @@ -0,0 +1,484 @@ +# Using Pyth with Stacks + +{% hint style="success" %} +For the latest releases and versions of the Stacks-Pyth contracts, check out the open-source repo [here](https://github.com/stx-labs/stacks-pyth-bridge). +{% endhint %} + +The contract logic, that we’ll use for this example, will mint an NFT in exchange for $100 of sBTC. Our Clarity contract will read the price of BTC/USD from the Pyth integration contract to calculate the amount of sBTC required to mint the NFT. + +Each Pyth Network price feed is referred to via a unique ID. The full list of price feeds is listed on the pyth.network website. To use a price feed on-chain, look up its ID, then store the feed ID in your program for price feed queries. Each price feed has its own unique id: + +**Available Pyth price feeds for Stacks:** + +* **BTC**: [0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43](https://www.pyth.network/price-feeds/crypto-btc-usd) +* **STX:** [0xec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c17](https://www.pyth.network/price-feeds/crypto-stx-usd) +* **USDC:** [0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a](https://www.pyth.network/price-feeds/crypto-usdc-usd) +* **ETH**: [0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace](https://www.pyth.network/price-feeds/crypto-eth-usd) + +{% hint style="info" %} +To request more supported price feeds, open an issue in the Pyth maintained repo for Stacks [here](https://github.com/Trust-Machines/stacks-pyth-bridge). +{% endhint %} + +Pyth Network uses a pull price update model that is slightly different from other oracles you may be more familiar with. Most oracles today use a push model, where the oracle runs an off-chain process that continuously sends transactions to update an on-chain price. In contrast, Pyth Network does not operate an off-chain process that pushes prices on-chain. Instead, it delegates this work to Pyth Network users. + +
        + +The maintained Pyth integration contract for Stacks is called [.pyth-oracle-v4](https://explorer.stacks.co/txid/SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-oracle-v4?chain=mainnet). This contract serves as the main entry point for updating and getting price feed data. The Pyth protocol integration is available as a Beta on both testnet and mainnet networks, to help developers test, give feedback, and ensure the reliability and stability of the integration. + +#### File setup + +Below are how the contracts and mainfest files are setup in this example Clarinet project. We'll be using Clarinet's mainnet simulation for this example, hence why we are adding mainnet contracts for Pyth in our files. + +{% tabs %} +{% tab title="main.clar" %} +{% code expandable="true" %} +```clarity +(define-constant CONTRACT_OWNER tx-sender) +(define-constant COST-OF-BENJAMIN-NFT u100) + +(define-constant ERR_READING_SBTC_BALANCE (err u7001)) +(define-constant ERR_NOT_ENOUGH_SBTC (err u7002)) +(define-constant ERR_NOT_OWNER (err u7003)) + +(define-public (join-the-benjamin-club (price-feed-bytes (buff 8192))) + (let ( + ;; Update & verify VAA for BTC price feed + (update-status (try! (contract-call? 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-oracle-v4 + verify-and-update-price-feeds price-feed-bytes { + pyth-storage-contract: 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-storage-v4, + pyth-decoder-contract: 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-pnau-decoder-v3, + wormhole-core-contract: 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.wormhole-core-v4, + }))) + ;; Get fresh BTC price + (price-data (try! (contract-call? 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-oracle-v4 + get-price + ;; The official BTC price feed id. + 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43 + 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-storage-v4 + ))) + ;; Adjust price and get user sBTC balance + ;; Price feeds represent numbers in a fixed-point format. The expo property tells us + ;; at what certain position is the decimal point implicity fixed. + (price-denomination (pow 10 (* (get expo price-data) -1))) + ;; We'll adjust the price to its normal decimal representation. + (adjusted-price (to-uint (/ (get price price-data) price-denomination))) + ;; Get the user's current sBTC balance. + (user-sbtc-balance (unwrap! + (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token + get-balance-available tx-sender + ) + ERR_READING_SBTC_BALANCE + )) + ) + ;; Determine if the user has at least $100 worth of sBTC to join the Benjamin Club and mint NFT + (if (> (/ (* user-sbtc-balance adjusted-price) (to-uint price-denomination)) + COST-OF-BENJAMIN-NFT + ) + (let ((hundred-dollars-in-sbtc (/ (* COST-OF-BENJAMIN-NFT (to-uint price-denomination)) adjusted-price))) + (try! (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token + transfer hundred-dollars-in-sbtc tx-sender current-contract + none + )) + (contract-call? .nft mint tx-sender) + ) + ERR_NOT_ENOUGH_SBTC + ) + ) +) +``` +{% endcode %} +{% endtab %} + +{% tab title="nft.clar" %} +{% code expandable="true" %} +```clarity +;; This contract implements the SIP-009 community-standard Non-Fungible Token trait +(impl-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait) + +;; Define the NFT's name +(define-non-fungible-token benjamin uint) + +;; Keep track of the last minted token ID +(define-data-var last-token-id uint u0) + +;; Define constants +(define-constant CONTRACT_OWNER tx-sender) +(define-constant COLLECTION_LIMIT u100) + +(define-constant ERR_OWNER_ONLY (err u100)) +(define-constant ERR_NOT_TOKEN_OWNER (err u101)) +(define-constant ERR_SOLD_OUT (err u300)) + +(define-data-var base-uri (string-ascii 80) "") + +;; SIP-009 function: Get the last minted token ID. +(define-read-only (get-last-token-id) + (ok (var-get last-token-id)) +) + +;; SIP-009 function: Get link where token metadata is hosted +(define-read-only (get-token-uri (token-id uint)) + (ok (some (var-get base-uri))) +) + +;; SIP-009 function: Get the owner of a given token +(define-read-only (get-owner (token-id uint)) + (ok (nft-get-owner? benjamin token-id)) +) + +;; SIP-009 function: Transfer NFT token to another owner. +(define-public (transfer + (token-id uint) + (sender principal) + (recipient principal) + ) + (begin + ;; #[filter(sender)] + (asserts! (is-eq tx-sender sender) ERR_NOT_TOKEN_OWNER) + (nft-transfer? benjamin token-id sender recipient) + ) +) + +;; Mint a new NFT. +(define-public (mint (recipient principal)) + ;; Create the new token ID by incrementing the last minted ID. + (let ((token-id (+ (var-get last-token-id) u1))) + ;; Ensure the collection stays within the limit. + (asserts! (< (var-get last-token-id) COLLECTION_LIMIT) ERR_SOLD_OUT) + ;; Only the contract owner can mint. + (asserts! (is-eq contract-caller .main) ERR_OWNER_ONLY) + ;; Mint the NFT and send it to the given recipient. + (try! (nft-mint? benjamin token-id recipient)) + ;; Update the last minted token ID. + (var-set last-token-id token-id) + ;; Return a success status and the newly minted NFT ID. + (ok token-id) + ) +) + +``` +{% endcode %} +{% endtab %} + +{% tab title="Clarinet.toml" %} +{% code expandable="true" %} +```toml +[project] +name = 'clarity-contracts' +description = '' +authors = [] +telemetry = false +cache_dir = './.cache' + +[[project.requirements]] +contract_id = 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait' + +[[project.requirements]] +contract_id = 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-oracle-v4' + +[[project.requirements]] +contract_id = 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.wormhole-core-v4' + +[[project.requirements]] +contract_id = 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-pnau-decoder-v3' + +[[project.requirements]] +contract_id = 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-storage-v4' + +[[project.requirements]] +contract_id = 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token' + +[contracts.main] +path = 'contracts/main.clar' +clarity_version = 4 +epoch = 'latest' + +[contracts.nft] +path = 'contracts/nft.clar' +clarity_version = 4 +epoch = 'latest' + +[repl.analysis] +passes = ['check_checker'] + +[repl.analysis.lints] + +[repl.analysis.check_checker] +strict = false +trusted_sender = false +trusted_caller = false +callee_filter = false + +[repl.remote_data] +enabled = true +api_url = 'https://api.hiro.so' +use_mainnet_wallets = true +``` +{% endcode %} +{% endtab %} +{% endtabs %} + +### Contract Walkthrough + +The walkthrough below will use a example contract that will mint an NFT in exchange for $100 of sBTC. Our Clarity contract will read the price of BTC/USD from the Pyth integration contract to calculate the amount of sBTC required to mint the NFT. The action of minting an NFT if one has at least $100 worth of sBTC will be deemed as _"joining the benjamin club"_. Benjamin is in reference to Benjamin Franklin being the face of a one hundred dollar bill, get it? + +{% stepper %} +{% step %} +#### Verify and update the BTC price feed + +We'll open up our function by accepting a `(price-feed-bytes (buff 8192))` parameter. This `price-feed-bytes` is in reference to a VAA message payload. Later in this guide we'll show you how to fetch this payload on the front end. + +You'll notice in the Clarity snippet below we open up `let` bindings of our function to: + +1. Verify and update the BTC price feed with its latest VAA message (more on how to pull the VAA later in this guide). This is a means of participating in the pull price update model. +2. Getting a fresh instance of the updated price data for BTC. + +{% hint style="info" %} +The `pyth-oracle-contract`, `pyth-storage-contract`, `pyth-decoder-contract`, and `wormhole-core-contract` do not need to be hardcorded in your contract. It's recommended to pass these contracts into the outer function as arguments since these contracts are susceptible to future upgrades. +{% endhint %} + +{% code title="main.clar" expandable="true" %} +```clarity +;; --snip-- + +(define-public (join-the-benjamin-club (price-feed-bytes (buff 8192))) + (let ( + ;; Update & verify VAA for BTC price feed + (update-status (try! (contract-call? 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-oracle-v4 + verify-and-update-price-feeds price-feed-bytes { + pyth-storage-contract: 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-storage-v4, + pyth-decoder-contract: 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-pnau-decoder-v3, + wormhole-core-contract: 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.wormhole-core-v4, + }))) + ;; Get fresh BTC price + (price-data (try! (contract-call? 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-oracle-v4 + get-price + ;; The official BTC price feed id. + 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43 + 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-storage-v4 + ))) + +;; --snip--- +``` +{% endcode %} +{% endstep %} + +{% step %} +#### Handling \`get-price\` data + +After updating and verifying the price feed in question, and then getting the updated price feed data, we'll need to handle the price feed data and its properties. + +The price feed data returned from invoking the `get-price` function of the `.pyth-oracle-v4` contract looks like the below: + +``` +{ + price-identifier: 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43, + price: 10603557773590, + conf: u3776653890, + ema-price: 10602069900000, + ema-conf: u4062895700, + expo: -8, + publish-time: u1750425711, + prev-publish-time: u1750425710 +} +``` +{% endstep %} + +{% step %} +#### Adjust returned price value and determine mint eligibility + +With the latest price feed data returned, we can adjust the price based on the `expo` property. Price feeds represent numbers in a fixed-point format. Fixed-point numeric representation is a way of storing numbers with fractional parts using integers, where the decimal point is implicitly fixed at a certain position. So in the above returned price feed data, the returned price of `10603557773590` and given expo of `-8` should be formatted as `106035`. The same exponent is used for both the price and confidence interval. + +We can then determine the USD amount of sBTC the user owns and decide if it is enough to mint a `benjamin-nft` for $100 worth of sBTC. Benjamin is in reference to Benjamin Franklin being the face of a one hundred dollar bill, get it? + +{% code title="main.clar" fullWidth="false" expandable="true" %} +```clarity +;; --snip-- + + ;; Price feeds represent numbers in a fixed-point format. The expo property tells us + ;; at what certain position is the decimal point implicity fixed. + (price-denomination (pow 10 (* (get expo price-data) -1))) + ;; We'll adjust the price to its normal decimal representation. + (adjusted-price (to-uint (/ (get price price-data) price-denomination))) + ;; Get the user's current sBTC balance. + (user-sbtc-balance (unwrap! + (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token + get-balance-available tx-sender + ) + ERR_READING_SBTC_BALANCE + )) + ) + ;; Determine if the user has at least $100 worth of sBTC to join the Benjamin Club and mint NFT + (if (> (/ (* user-sbtc-balance adjusted-price) (to-uint price-denomination)) + COST-OF-BENJAMIN-NFT + ) + (let ((hundred-dollars-in-sbtc (/ (* COST-OF-BENJAMIN-NFT (to-uint price-denomination)) adjusted-price))) + (try! (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token + transfer hundred-dollars-in-sbtc tx-sender current-contract + none + )) + (contract-call? .nft mint tx-sender) + ) + ERR_NOT_ENOUGH_SBTC + ) + ) +) +``` +{% endcode %} +{% endstep %} +{% endstepper %} + +### Interact with contract in console using mxs + +Since we are using Pyth and sBTC's mainnet contract addresses in our contract, we'll use Clarinet's mainnet simulation feature to interact with the contract in simnet. + +{% hint style="warning" %} +Be sure you've enabled mainnet simulation in your manifest file. See the Clarinet.toml file above. +{% endhint %} + +{% stepper %} +{% step %} +#### Set \`tx\_sender\` to a mainnet address + +In the Clarinet console run: `::set_tx_sender ` + +You'll want to grab a `` that most likely has over $100 of sBTC. This is so we can have a simulation of being able to mint the NFT. Then switch the `tx_sender` context so that we can have _that_ mainnet address simulate calling our contract. +{% endstep %} + +{% step %} +#### Call \`join-the-benjamin-club\` with a VAA payload + +In the Clarinet console run: `(contract-call? 'SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R.main join-the-benjamin-club )` + +Pass in a valid VAA payload into the `join-the-benjamin-club` function. If everything passes, you should see the events emitted from the contract along with an `(ok u1)` response. + +
        + +Where to get a sample VAA payload? + +You can use this sample VAA payload to test with. This VAA payload is outdated but should be testable in simnet/console. + +``` +0x504e41550100000003b801000000040d0064f9d65f754bc6426b50d45047fc50c905196202e888b73182a8177579ec838f652d710879a9a0c3026bd9e44b32509b9c94e86328d89235c5221e4cd20303ca000265aa2173fe44b28c3942c984ee953e9e2a06a686656c8bd36fbf396d7b4bfe9338bd6017a484b60ca1c80a75458f55ff9c9dd3e5edfa45f6243c13d77f414d4000045529833b17cfc6b011de0b72709c785a86d0ce9281a8c21a250f55d3fbfd79b47b89f110c63acdf8e0fa251d245b6e87ccbf5747dab2ba6eaf22e27f16fc97580006f673a3f384580e0bc7c5a246fb77a37f83ded9fe1368f3c15cdbad2475f2378d0daae7d3201e23ba45c04606dedea0327bd095f7b45d9dd84600b8e877af0c810008a4018e5793e30ac8439aef1b3ce284b670e95ab5ce76090f4dac860a1267177355102ddc46d33f4c53c253b30cb945ce63c710773fa40ae846192c6d1e335921000aea43bd88cc9891274d2b7f493b362c7799b2bf1e40630181fecce951118c987345da8f194ac0a44a2f956dce99b61f2254fe6b2409382d8e887e8fa07e6894aa000b9639228e880e97da54c07a9b762849a232a6f4358d9c82ec2e9a57c55cf0d89767e9e690586e52c752f31d541b1d68f7a86f3d7dffbbd6229fae2aa9b7647815010ca826b369905ff52daa0204b2ce3cedde1eee11d167e0cbdad3350362210840523db4c95c4669bd59b5e3dcd84d8bed673d0452d2229832f9af0904f070721fd0010d1b6af9c169ab48b47d6726d35f030eddabf9e6e04058097730bca037284a114a439b7ceb984013e8bc4873182dfd893d52c91a558a4bafe32f6d9a692765fc27010ef1c798f6db4a3c6387797dff6c06779b45b03bc1fde7a1eb86942bbb8ab3d5fc70c317d51dd71cbac39b8bd038bcf72552e4bc048f4e0731b9643090eb8bb1b6000fca5d6ac6f0e6569e1072566a56075dae7db43d8a008e939422863890c64e76b81544ae0ebaaacb473241c75cf5e2a8a1fd5c1cd5ee99970113e4d4bd0a86d9c70010c66616689026b43cbcd0012c7e87c9de75b7b8f60d2c765ec298ade7862d64ce63ef95670553bc120b08b52e6c5893a0509287ad5b38cd44a9113ddce5dc4c12001177acdc2be20bef459c68d319fd121fea8c2fbd1aad4ee8ebe36a5750214c39510b60e89b4c247e6e354f64722a49eb40ba2ba164cb9e6191e14b81155f16e6a4006935e74900000000001ae101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71000000000a6abf2a014155575600000000000f78660e00002710c82bc426063e681025b2a2462b81da02fbdc9ab001005500e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b4300000852e1a4948000000000ac606b00fffffff8000000006935e749000000006935e7480000084e36eef02000000000a4da13a40d0d91f61daad89890a2c5da1abd4009915ccb2042e737bd6a369235eadc65f71867b56b312545c86c2cd2cd18971f74e0ef4e849a2b295b12dab11d5650dc565595a445ea019e1258a1397dc974e42ef8315937dc38ca22e8327aa6c21decb109199d18fec07feded692d0a8b1660748d3402dcd9e84229dd201c5da161c653ae21e63acdbf9ee603deec2668923bc0435272b1fc3851957c2a7a4867704a39d4e0c43b378456f0695c944f2bc3ba3ea356d4d3318a0b8c666d8df3a74fa3cea21e74470dc680878ff8dac909832fea3e4dc56c1fa5287cd472db6db3eb54c232d4faf9080009f4fa433b6b465ced1e2826dc510e7cf81c7261bf377e1847c0f08d7280f8 +``` + +
        + +
        + +Run unit tests + +You can also test your contract using mxs in unit tests. See the below unit test setup that shows how you can fetch the latest VAA for your contract call. + +{% code title="main.test.ts" expandable="true" %} +```typescript +import { describe, expect, it } from "vitest" +import { Cl, } from "@stacks/transactions" +import { HermesClient } from "@pythnetwork/hermes-client"; + +const accounts = simnet.getAccounts() +const deployer = accounts.get("deployer") + +async function handleFetchLatestVaa() { + const connection = new HermesClient("https://hermes.pyth.network", {}); + + const priceIds = [ + "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43", + ]; + + const priceUpdates = await connection.getLatestPriceUpdates(priceIds); + console.log(priceUpdates); + + let latestVaaHex = `0x${priceUpdates.binary.data[0]}`; + + return latestVaaHex; +} + +describe("pyth unit tests", () => { + it("ensures joining of Benjaming Club", async () => { + let VAA = await handleFetchLatestVaa() + + let confirmation = simnet.callPublicFn( + `${deployer}.main`, + "join-the-benjamin-club", + [Cl.bufferFromHex(VAA)], + "" + ) + + expect(confirmation.result).toBeOk(Cl.uint(1)) + + // an extended timeout value is set below due to long processing of Pyth functions + }, 12000) +}) +``` +{% endcode %} + +
        +{% endstep %} +{% endstepper %} + +### Frontend Walkthrough + +Wormhole is a decentralized attestation engine that leverages its network of guardians to trustlessly bridge information between the chains it supports. Wormhole has a simple, elegant, and pragmatic design that has enabled it to be the first real solution to ship to market and has received wide recognition and support from its member chains. + +Hermes is a web service that listens to the Wormhole Network for signed and attested price updates, and serves them via a convenient web [API](https://hermes.pyth.network/docs/#/rest/latest_price_updates). It provides Pyth's latest price update data format that is more cost-effective to verify and use on-chain. Hermes allows users to easily query for recent price updates via a REST API, or subscribe to a websocket for streaming updates. The Pyth Network also provides a Javascript [SDK](https://github.com/pyth-network/pyth-crosschain/tree/main/price_service/client/js) to connect to an instance of Hermes for fetching price updates. + +In your front-end application code, you can install and use the methods brought by Pyth Network's `hermes-client` Javascript SDK to fetch the latest price update, known as a VAA (Verified Action Approvals) message. + +```typescript +import { HermesClient } from "@pythnetwork/hermes-client"; +import { Cl, Pc } from "@stacks/transactions" +import { request } from "@stacks/connect" + +// --snip-- + +async function handleFetchLatestVaa() { + const connection = new HermesClient("https://hermes.pyth.network", {}); + + const priceIds = [ + "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43", + ]; + + const priceUpdates = await connection.getLatestPriceUpdates(priceIds); + let latestVaaHex = `0x${priceUpdates.binary.data[0]}`; + + return latestVaaHex; +} + +// --snip-- +``` + +The binary data returned from the Pyth SDK will already be in hexadecimal format. We'll then take this hexadecimal VAA message and pass it into our Clarity function as an argument. + +Using Stacks Connect of the stacks.js monorepo, we'll open up a `stx_callContract` request and invoke our public function while passing in the `latestVaaHex` as the function argument. + +```typescript +// --snip-- + +let latestVaaHex = await handleFetchLatestVaa(); + +let postCond1 = Pc.principal("") + .willSendLte(1) + .ustx(); + +const response = await request("stx_callContract", { + contract: `SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R.benjamin-club`, + functionName: "join-the-benjamin-club", + functionArgs: [Cl.bufferFromHex(latestVaaHex)], + network: "mainnet", + postConditions: [postCond1], + postConditionMode: "allow", +}); +``` + +If you noticed, we set a post-condition statement of our user transferring less than or equal to 1 uSTX, which is 0.000001 STX. This is because the `verify-and-update-price-feeds` of the `.pyth-oracle-v4` contract applies a fee for this. + +Setting a separate post-condition statement on the actual sbtc token transfer in our example will also be needed. Beforehand, you could invoke the `decode-price-feeds` function with the `latestVaaHex` to simply have the contained price data decoded and returned. From there you could pre-determine the estimated amount of sbtc tokens to be transferred and set in a separate post-condition. + +*** + +### Additional Resources + +* \[[Hiro YT](https://youtu.be/eybqQVRh_hw?si=uwlpiZq36Ad7bBb0)] How To Pull Real-Time Price Data in Your Clarity Smart Contracts (Using Pyth) +* \[[Pyth Docs](https://docs.pyth.network/price-feeds/core/use-real-time-data/pull-integration/stacks)] How to Use Real-Time Data in Stacks Applications +* \[[Hiro Blog](https://www.hiro.so/blog/new-oracle-alert-pyth-integration-with-stacks)] New Oracle Alert: Pyth Integration With Stacks diff --git a/docs/build/more-guides/sbtc/README.md b/docs/build/more-guides/sbtc/README.md new file mode 100644 index 0000000000..10bf8e5309 --- /dev/null +++ b/docs/build/more-guides/sbtc/README.md @@ -0,0 +1,11 @@ +--- +description: Integrate sBTC in your app +--- + +# sBTC + +
        + +Integrating **sBTC** lets apps use real Bitcoin in smart contracts, unlocking programmable BTC without needing wrapped or custodial assets. It enables fast, low-fee interactions anchored to Bitcoin security while still benefiting from expressive on-chain logic on Stacks. For developers, sBTC dramatically expands what’s possible with Bitcoin—powering lending, trading, gaming, and new user experiences backed by the world’s hardest money. + +The guides in this section provide step-by-step instructions for interacting with sBTC, including developer guides on how to interact with sBTC and how to bridge BTC into sBTC. diff --git a/docs/build/more-guides/sbtc/bridging-bitcoin/README.md b/docs/build/more-guides/sbtc/bridging-bitcoin/README.md new file mode 100644 index 0000000000..5e62cd5af3 --- /dev/null +++ b/docs/build/more-guides/sbtc/bridging-bitcoin/README.md @@ -0,0 +1,26 @@ +--- +description: >- + A Javascript/Typescript package for integrating your own peg-in/out bridging + flow. +--- + +# How to Use the sBTC JS Library for Bridging + +Currently, the official [sBTC Bridge app](https://sbtc.stacks.co/) provides users the interface for pegging BTC into sBTC, and vice versa. Building your own sBTC bridging app would consist of working with the construction of bitcoin P2TR transactions, handling user UTXOs, broadcasting transactions, notifying the sBTC Signers' of incoming transactions, and etc. You could check out the complexity of that in the sBTC Bridge app's open-source repo [here](https://github.com/stacks-sbtc/sbtc-bridge). But thankfully there is a library for all of that. + +The [`sbtc`](https://www.npmjs.com/package/sbtc) npm library was built to abstract away the complexities of the bridging process of sBTC. + +{% hint style="info" %} +Check out the `sbtc` reference [section](https://app.gitbook.com/s/GVj1Z9vMuEOMe7oH7Wnq/stacks.js/sbtc) for definitions, configurations, and more examples. +{% endhint %} + +#### **Architecture** + +* **Bitcoin:** The original funds are sourced from Bitcoin. A depositor sends these funds to a group of signers, which manage a (rotating) multisignature address formatted for sBTC transactions. +* **sBTC API (Emily):** This API is responsible for tracking deposits and notifying the signers about pending deposits. +* **Stacks:** The network where sBTC is minted. Once the deposit is confirmed, the signers mint the corresponding amount of sBTC to the depositor's specified address on the Stacks network. + +#### This guide will provide a walkthrough of using the `sbtc` library for: + +* **Depositing**: pegging BTC into sBTC +* **Withdrawing**: pegging sBTC into BTC diff --git a/docs/build/more-guides/sbtc/bridging-bitcoin/btc-to-sbtc.md b/docs/build/more-guides/sbtc/bridging-bitcoin/btc-to-sbtc.md new file mode 100644 index 0000000000..d99c8ca957 --- /dev/null +++ b/docs/build/more-guides/sbtc/bridging-bitcoin/btc-to-sbtc.md @@ -0,0 +1,291 @@ +# Depositing: Pegging BTC into sBTC + +This guides shows how you can integrate the deposit (peg-in) flow from your front-end app to allow users to peg BTC into sBTC on the Stacks network. For more information about sBTC and an explainer of its architecture, check out the general sBTC section [here](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/sbtc) in the Learn category. + +### Breakdown of the deposit (peg-in) flow + +* **Create Deposit (Bitcoin) Transaction:** + * Structure a Bitcoin transaction to send funds to the group of signers. + * Use a specialized format that includes: + * Deposit Script: Identifies which _Stacks address_ the sBTC will be minted to and what the _maximum fee_ (in satoshis) the signers may take in exchange for minting. + * Reclaim Script: Allows the sender to reclaim their funds if the transaction is not processed by the signers. +* **Sign and Broadcast the Transaction:** + * Sign the transaction with the sender's private key. + * Broadcast the transaction to the Bitcoin network (Bitcoin Regtest for Stacks Testnet). +* **Notify the sBTC API (Emily):** + * Inform the API about the transaction by submitting its details. This step ensures that the signers are aware of the deposit and can track it. +* **Processing by Signers:** (_no action required_) + * The signers retrieve and verify the deposit transaction from the Bitcoin blockchain. + * Once verified, the signers mint the equivalent amount of sBTC on the Stacks network. +* **Receive sBTC (Stacks):** (_no action required_) + * The minted sBTC is sent to the depositor's designated Stacks address, completing the deposit process. + * sBTC is SIP-010 compatible and will show up in Stacks wallets and explorers. + +In this guide you'll touch on some of the steps above but its much simpler than you'd expect. Using the `sbtc` and `@stacks/connect` libraries, putting together the peg-in process from BTC into sBTC will simply involve the following steps: + +1. Building the sBTC deposit address +2. Invoking the user's wallet to sign and broadcast the bitcoin transaction +3. Notifying the sBTC signers +4. Confirm user's sBTC balance + +{% hint style="info" %} +This guide assumes you have a front-end bootstrapped with the Stacks Connect library for wallet interactions. Head to the guides for Stacks Connect before continuing with this guide. +{% endhint %} + +{% stepper %} +{% step %} +#### Building the sBTC deposit address + +You're not directly sending bitcoin to the public sBTC Signers' [bitcoin address](https://mempool.space/address/bc1prcs82tvrz70jk8u79uekwdfjhd0qhs2mva6e526arycu7fu25zsqhyztuy), but rather sending to a custom P2TR address where both the user and sBTC Signers have control over. This custom P2TR address is special because it contains tapscripts that specify which parties are able to unlock the UTXOs via a script path spend. + +The construction of these tapscripts is what ultimately generates the custom P2TR address that the user will be sending their UTXOs to. Constructing tapscripts, or bitcoin scripts in general, are complex and tricky. The `sbtc` library provides useful methods for abstracting away the complexities of working with taproot related functions. + +```typescript +import { buildSbtcDepositAddress, MAINNET, SbtcApiClientMainnet } from 'sbtc'; + +// Initialize the sBTC API client based on network +const client = new SbtcApiClientMainnet(); + +// Build the deposit address with metadata +const deposit = buildSbtcDepositAddress({ + stacksAddress: 'SP14ZYP25NW67XZQWMCDQCGH9S178JT78QJYE6K37', // the address to send/mint the sBTC to + signersPublicKey: await client.fetchSignersPublicKey(), // the aggregated public key of the signers + reclaimLockTime: 700, // default value is 950 + reclaimPublicKey: btcPubKey.value, // public key for reclaiming failed deposits + network: MAINNET, + maxSignerFee: 4000 // optional property, default value is 80,000 sats +}); + +// `deposit.address` is the deposit address (send funds here, aka the deposit address as an output) +``` + +{% hint style="info" %} +The `maxSignerFee` refers to the fee in the bitcoin transaction sweeping funds into, or out of, the consolidated UTXO locked exclusively by sBTC Signers' aggregate address. Depending on network congestion, specify a custom fee your users would be willing to spend. The default value will be 80,000 sats. The user's responsibility of the actual fee spent is actually deducted from the amount of sBTC that will be minted. +{% endhint %} + +The `buildSbtcDepositAddress` will return with a schema of: + +``` +deposit { + depositScript: string; + reclaimScript: string; + trOut: P2TROut; + address: string; // the custom P2TR address to deposit bitcoin to +} +``` +{% endstep %} + +{% step %} +#### Sign and broadcast the bitcoin transaction + +Invoke the user's Stacks wallet to sign and broadcast the deposit bitcoin transaction. + +The string literal `sendTransfer` method will invoke the user's wallet to construct a bitcoin transaction. Be certain that the user's Stacks-supported wallet also supports `sendTransfer` as it is part of [WBIP005](https://wbips.netlify.app/wbips/WBIP005) for default Bitcoin methods. + +```typescript +import { request } from '@stacks/connect'; + +const result = await request('sendTransfer', { + recipients: [ + { + address: deposit.address, + amount: 100_000, + }, + ], +}) +``` +{% endstep %} + +{% step %} +#### Notify the sBTC Signers + +Immediately after the deposit bitcoin transaction is broadcasted, fetch the transaction hex and notify the sBTC Signers via the Emily API. + +{% hint style="warning" %} +You'll want to wait until the transaction hits the bitcoin mempool before fetching the transaction hex. Usually this is within seconds. +{% endhint %} + +```typescript +// below should be delayed until txid appears in the bitcoin mempool +const transaction = await client.fetchTxHex(result.txid); + +// 3. NOTIFY THE SIGNERS +let response = await client.notifySbtc({ transaction, ...deposit }); +console.log('Notify response:', response); +``` + +The `notifySbtc` method will return with a schema of: + +{% code expandable="true" %} +``` +export type SbtcApiNotifyResponse = { + bitcoinTxid: string; + bitcoinTxOutputIndex: number; + recipient: string; + amount: number; + lastUpdateHeight: number; + lastUpdateBlockHash: string; + status: string; + statusMessage: string; + parameters: { + maxFee: number; + lockTime: number; + }; + reclaimScript: string; + depositScript: string; +}; +``` +{% endcode %} +{% endstep %} + +{% step %} +#### Confirm user's sBTC balance + +The user should expect to receive their newly minted sBTC in about 20 minutes, or within 1 to 2 confirmations on the Bitcoin chain. Poll the user's specified `stacksAddress` to check if they've received sBTC and display that to the user on the front-end. + +The API client comes with a `fetchSbtcBalance` method that can help with this: + +```typescript +const balance = await client.fetchSbtcBalance('SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159'); +// 1000000n (in micro-sBTC) +``` + +You could also fetch the deposit data from the API client. This will return data pertaining to the 3 total transactions that make up the entire deposit (peg-in) flow: + +1. \[Bitcoin] Initial bitcoin deposit by the user ([example](https://mempool.space/tx/174bff280dff56d5c1d86d341ddee213d248f375c2552d2d333a59d82a59a35c)) +2. \[Bitcoin] Sweep bitcoin transaction by the Signers ([example](https://mempool.space/tx/15029c5cabbd759ae31f58d8a08b50f9e0d9181c128fa61c3bea9c48e8ac8ea7)) +3. \[Stacks] Mint sBTC by the Signers ([example](https://explorer.hiro.so/txid/0xe26c60bce407147c6f538805776039b1ed2710b903255a5726d46f4bbe97fc75?chain=mainnet)) + +{% code expandable="true" %} +```typescript +let depositInfo = await client.fetchDeposit(txid) // txid of initial bitcoin deposit + +// example depositInfo result below +{ + "nextToken": null, + "deposits": [ + { + "bitcoinTxid": "174bff280dff56d5c1d86d341ddee213d248f375c2552d2d333a59d82a59a35c", + "bitcoinTxOutputIndex": 0, + "recipient": "0516a3e1feb8787ea10053bcf8761534112f8057e2af", + "amount": 300000, + "lastUpdateHeight": 4512460, + "lastUpdateBlockHash": "297a564dcbc5a327ceac5674e194bde3218f68d55f40616fd748874e0fcdf838", + "status": "confirmed", + "statusMessage": "Included in block 297a564dcbc5a327ceac5674e194bde3218f68d55f40616fd748874e0fcdf838", + "parameters": { + "maxFee": 80000, + "lockTime": 950 + }, + "reclaimScript": "02b603b275203b992a2735a7af6da02cbdfcb723fe5e669765505903db59270b852d0f2d6fe3ac", + "depositScript": "1e00000000000138800516a3e1feb8787ea10053bcf8761534112f8057e2af7520d8c4344861fc7590fd812c24884a3bfd9374d8ba865a787ff53c9060020aa967ac", + "fulfillment": { + "BitcoinTxid": "15029c5cabbd759ae31f58d8a08b50f9e0d9181c128fa61c3bea9c48e8ac8ea7", + "BitcoinTxIndex": 0, + "StacksTxid": "e26c60bce407147c6f538805776039b1ed2710b903255a5726d46f4bbe97fc75", + "BitcoinBlockHash": "00000000000000000001aab771f33914381ad92b3f5cfedf729172f2afd9c65c", + "BitcoinBlockHeight": 921715, + "BtcFee": 235 + } + } + ] +} +``` +{% endcode %} +{% endstep %} +{% endstepper %} + +And that's all to it. You've successfully allowed your app to handle incoming BTC to be pegged into sBTC onto the Stacks network. + +*** + +### \[Additional Insights] + +### What scripts make up the custom P2TR bitcoin address? + +As mentioned above, you're not directly sending bitcoin to the public sBTC Signers' [bitcoin address](https://mempool.space/address/bc1prcs82tvrz70jk8u79uekwdfjhd0qhs2mva6e526arycu7fu25zsqhyztuy), but rather sending to a custom P2TR address where both the user and sBTC Signers have control over. Besides the default key path spend, this custom P2TR address also contains 2 sets of scripts: + +
        + +#### Deposit script + +The deposit script is used by the sBTC Signers to "sweep" the deposited UTXO into their aggregate bitcoin address used to hold the entire balance of deposited bitcoin. So in a way they are consolidating all UTXOs into one single UTXO. + +The construction of the deposit script: + +{% code expandable="true" %} +```typescript +export function buildSbtcDepositScript(opts: { + maxSignerFee: number; + stacksAddress: string; + signersPublicKey: string; +}) { + const maxSignerFeeBytes = P.U64BE.encode(BigInt(opts.maxSignerFee)); + const recipientBytes = stacksAddressBytes(opts.stacksAddress); + const signersPublicKeyBytes = hexToBytes(opts.signersPublicKey); + + if (signersPublicKeyBytes.length !== 32) { + throw new Error('Signers public key must be 32 bytes (schnorr)'); + } + + return btc.Script.encode([ + P.utils.concatBytes(maxSignerFeeBytes, recipientBytes), + 'DROP', + signersPublicKeyBytes, + 'CHECKSIG', + ]); +} +``` +{% endcode %} + +#### Reclaim script + +The reclaim script allows the initial depositor to reclaim their UTXOs. + +The construction of the reclaim script: + +{% code expandable="true" %} +```typescript +export function buildSbtcReclaimScript(opts: { + reclaimLockTime: number; + reclaimPublicKey: string; +}) { + const reclaimLockTime = + opts.reclaimLockTime <= 16 + ? opts.reclaimLockTime // number if can be encoded as a OP_ + : btc.ScriptNum().encode(BigInt(opts.reclaimLockTime)); + const publicKeyBytes = hexToBytes(opts.reclaimPublicKey); + + if (publicKeyBytes.length !== 32) throw new Error('Public key must be 32 bytes (schnorr)'); + + return btc.Script.encode([ + reclaimLockTime, + 'CHECKSEQUENCEVERIFY', + 'DROP', + publicKeyBytes, + 'CHECKSIG', + ]); +} +``` +{% endcode %} + +Behind the scenes, these two script construction methods are being abstracted away by `buildSbtcDepositAddress` which you've implemented on the front-end. + +### How are fees dealt with? + +**During deposits** + +The `maxSignerFee` refers to the fee in the bitcoin transaction sweeping funds into the consolidated UTXO locked exclusively by sBTC Signers' aggregate address. Depending on network congestion, specify a custom fee your users would be willing to spend. The default value will be 80,000 sats. The user's responsibility of the actual fee spent (for the sweep transaction) is actually deducted from the amount of sBTC that will be minted. + +**During withdrawals** + +The fees specified in `max-fee` of the function `initiate-withdrawal-request` of the [`.sbtc-withdrawal`](https://explorer.hiro.so/txid/SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-withdrawal?chain=mainnet) contract are referring to the fees paid of the bitcoin withdrawal transaction. + +#### How to estimate how much in fees one should spend? + +If you want to estimate how much one would expect to be charged in fees, you'd have to estimate the size of the transaction (vbytes) and the current network's fee rate. Below are some estimations you could use as a benchmark: + +**For deposits**: \~250 vbytes times the prevailing sats per vbyte fee rate\ +**For withdrawals**: \~170 vbtytes times the prevailing sats per vbyte rate + +And although many deposits and withdrawals can be combined, these values should be the maximum that a user will be charged regardless of how many other deposits or withdrawals are being serviced in a single transaction by the Signers. Meaning when more than one user's request is included in a sweep transaction on the L1, the users share the fees in proportion to their deposit or withdrawal request's actual weight on the L1. diff --git a/docs/build/more-guides/sbtc/bridging-bitcoin/sbtc-to-btc.md b/docs/build/more-guides/sbtc/bridging-bitcoin/sbtc-to-btc.md new file mode 100644 index 0000000000..badc946dc1 --- /dev/null +++ b/docs/build/more-guides/sbtc/bridging-bitcoin/sbtc-to-btc.md @@ -0,0 +1,208 @@ +# Withdrawing: Pegging sBTC into BTC + +This guides shows how you can integrate the withdrawal (peg-out) flow from your front-end app to allow users to peg sBTC back into BTC on the Bitcoin network. For more information about sBTC and an explainer of its architecture, check out the general sBTC section [here](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/sbtc) in the Learn category. + +### Breakdown of the withdrawal (peg-in) flow + +* **Validate and deconstruct bitcoin address** + * Validate user's inputted bitcoin address, to be used to receive BTC, is a valid bitcoin address. + * Deconstruct the bitcoin address to identify its version type and hashbytes. +* **Construct a Stacks contract call:** + * Determine amount to withdraw and a reasonable max fee. + * Construct a contract call to invoke `initiate-withdrawal-request` of `.sbtc-withdrawal` contract. + * Broadcast the transaction to the Stacks network. +* **Processing by Signers:** (_no action required_) + * The signers retrieve and verify the withdrawal transaction from the Stacks network. + * Once verified, the signers burn the sBTC and sends the equivalent amount of BTC back to the user. +* **Receive BTC (Bitcoin):** (_no action required_) + * The returned BTC is sent to the depositor's designated bitcoin address, completing the withdrawal process. + +{% hint style="info" %} +At the moment, the `sbtc` library does not have any direct helper methods, but as you'll see in the guide, it's relatively straightforward to do it without any abstraction methods. +{% endhint %} + +In this guide you'll touch on some of the steps above but its much less complex than the deposit flow. Putting together the peg-out process from sBTC into BTC will simply involve the following steps: + +1. Validating the withdrawal bitcoin address +2. Contract call to `initiate-withdrawal-request` +3. Confirm BTC withdrawal + +{% hint style="info" %} +This guide assumes you have a front-end bootstrapped with the Stacks Connect library for wallet interactions. Head to the guides for Stacks Connect before continuing with this guide. +{% endhint %} + +{% stepper %} +{% step %} +#### Validating the withdrawal bitcoin address + +Validating a Bitcoin address before using it in code is essential to prevent errors and potential financial losses from incorrect or malicious addresses. It ensures compliance with types like P2PKH, P2SH, or P2TR, and adherence to network protocols. Without proper validation, there's a higher risk of failed transactions and security vulnerabilities, jeopardizing user funds and application reliability. + +After validating, you'll want to deconstruct the bitcoin address to identify its version type and hashbytes. The version type is pertaining to the bitcoin address type and how they map to its corresponding Clarity value. + +We'll use the `getAddressInfo` method from the [`bitcoin-address-validation`](https://github.com/ruigomeseu/bitcoin-address-validation) library to help us deconstruct the receiving bitcoin address. + +
        import { AddressType, getAddressInfo } from "bitcoin-address-validation";
        +import * as bitcoin from "bitcoinjs-lib";
        +
        +function deconstructBtcAdd(address: string) {
        +    const typeMapping = {
        +    [AddressType.p2pkh]: "0x00",
        +    [AddressType.p2sh]: "0x01",
        +    [AddressType.p2wpkh]: "0x04",
        +    [AddressType.p2wsh]: "0x05",
        +    [AddressType.p2tr]: "0x06",
        +  };
        +
        +  const addressInfo = getAddressInfo(address);
        +  
        +  const { bech32 } = addressInfo;
        +  let hashbytes: Uint8Array;
        +  if (bech32) {
        +    hashbytes = bitcoin.address.fromBech32(address).data;
        +  } else {
        +    hashbytes = bitcoin.address.fromBase58Check(address).hash;
        +  }
        +
        +  const type = typeMapping[addressInfo.type];
        +  if (!type) {
        +    throw new Error(`Unsupported address type: ${addressInfo.type}`);
        +  }
        +  
        +  return {
        +    type,
        +    hashbytes,
        +  };
        +}
        +
        + +In the next step, the returned `type` and `hashbytes` are to be passed in as a tuple param of a contract call. The constraints and meaning of the `type` and `hashbytes` param are summarized as: + +``` +;; version == 0x00 and (len hashbytes) == 20 => P2PKH +;; version == 0x01 and (len hashbytes) == 20 => P2SH +;; version == 0x02 and (len hashbytes) == 20 => P2SH-P2WPKH +;; version == 0x03 and (len hashbytes) == 20 => P2SH-P2WSH +;; version == 0x04 and (len hashbytes) == 20 => P2WPKH +;; version == 0x05 and (len hashbytes) == 32 => P2WSH +;; version == 0x06 and (len hashbytes) == 32 => P2TR +``` +{% endstep %} + +{% step %} +#### Contract call to `initiate-withdrawal-request` + +The function `initiate-withdrawal-request` of the [`.sbtc-withdrawal`](https://explorer.hiro.so/txid/SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-withdrawal?chain=mainnet) contract locks up `amount` + `max-fee` from the sender's account. When the withdrawal request is accepted, the signers will then send BTC `amount` of sats to the recipient and spend the fee `amount` to bitcoin miners (where fee less than or equal to max-fee). If actual fee is less than `max-fee`, then the difference will be minted back to the user when `accept-withdrawal-request` is invoked by the Signers. + +The network used, for the bitcoin address, is inherited from the network of the underlying transaction itself (basically, if on Stacks mainnet the Signers will send to mainnet Bitcoin addresses and similarly on Stacks testnet, to Bitcoin testnet addresses). + +
        import { request } from '@stacks/connect';
        +import { Cl, Pc } from '@stacks/transactions';
        +
        +let { type, hashbytes } = deconstructBtcAdd(<btcAddress>)
        +
        +let amount = 100000
        +let recipient = {
        +  version: Cl.bufferFromHex(type),
        +  hashbytes: Cl.buffer(hashbytes)
        +}
        +let maxFee = 3000
        +
        +let postCond_1 = Pc.principal(<stxAddress>)
        +  .willSendEq(amount + maxFee)
        +  .ft('SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token', 'sbtc-token')
        +
        +let result = await request('stx_callContract', {
        +  contract: 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-withdrawal',
        +  functionName: 'initiate-withdrawal-request',
        +  functionArgs: [Cl.uint(amount), Cl.tuple(recipient), Cl.uint(maxFee)],
        +  postConditions: [postCond_1],
        +  postConditionMode: 'deny',
        +  network: "mainnet",
        +})
        +
        +console.log(result)
        +// {
        +//    "transaction": "0000000001040049ff5845af0c7efefca31b764229c84e8968e8bc000000000000009f0000000000000bb800015772b7ea414879826e90da34f9030ad6f8b6fb3f7c07d766906d7d9f9a65752147961e3de57d8fcaa1dfd6d92e0d085af3491efdfe2ec78d9a403c258f5c092e0301000000000214f6decc7cfff2a413bd7cd4f53c25ad7fd1899acc0f736274632d7769746864726177616c1b696e6974696174652d7769746864726177616c2d726571756573740000000301000000000000000000000000000186a00c0000000209686173686279746573020000001457873a539bfb7c071f8cd91805068d546a4941950776657273696f6e0200000001010100000000000000000000000000000bb8",
        +//    "txid": "cd73c7c3023d4f271981d85cb1c29446a5513dcfc182963ee2d1cfee06b9a4ad"
        +// }
        +
        +{% endstep %} + +{% step %} +#### Confirm BTC withdrawal + +The Stacks contract call transaction will be confirmed by the Stacks network within seconds. But the actual BTC withdrawal confirmation is longer than the deposit confirmation. Usually around 6 Bitcoin block confirmations are needed. + +After confirming the Stacks transaction, confirm that the user has received the withdrawn BTC back into their designated bitcoin address by polling the Emily API endpoint below: + +``` +https://sbtc-emily.com/withdrawal/sender/ +``` + +This will return the status of all withdrawals from the specified sender: + +
        {
        +  "nextToken": null,
        +  "withdrawals": [
        +    {
        +      "requestId": 748,
        +      "stacksBlockHash": "e19bea7a651136ed5a156e69d5952e86a3792f78df2bb20c8c5ab2009fd5617e",
        +      "stacksBlockHeight": 4461632,
        +      "recipient": "0020a6abc068c9783dea16451549cebb174ee82618f9999b53334b2397e02c8a106f",
        +      "sender": "SP14ZYP25NW67XZQWMCDQCGH9S178JT78QJYE6K37",
        +      "amount": 110000,
        +      "lastUpdateHeight": 4462339,
        +      "lastUpdateBlockHash": "32b2834eb507ec9484ea1bbce97cb2dc241c8ab18bd443181aa5b0c178546bed",
        +      "status": "confirmed",
        +      "txid": "a355cd64374446e1d0de7096a7c1583bb4564fb6a997650bd9af26605805bfa0"
        +    },
        +  ]
        +}
        +
        + +So in total there are 2 on-chain transactions that make up the entire withdrawal (peg-out) flow: + +1. \[Stacks] Initial sBTC withdrawal request by the user ([example](https://explorer.hiro.so/txid/0x4f4000a0ca61ea10e31bc7950672f57612880b6de3a61463bb98e29ca6bb6491?chain=mainnet)) +2. \[Bitcoin] Returned BTC transaction by the Signers ([example](https://mempool.space/tx/4ec2396bebb79d11be6dae3ae133cb0501d05af4ff368425fe19740d050b74dc)) +{% endstep %} +{% endstepper %} + +And that's all to it. You've successfully allowed your app to handle incoming sBTC to be pegged out back into BTC. + +*** + +### \[Additional Insights] + +### What are the different bitcoin address types? + +Bitcoin addresses come in several types, each serving specific purposes and providing different functionalities. Each address type has evolved to enhance security, scalability, and functionality of Bitcoin transactions in response to the network's growing needs. + +Check out the dedicated Hiro blog post to learn more about the why and how different bitcoin addresses are constructed: + +{% embed url="https://www.hiro.so/blog/understanding-the-differences-between-bitcoin-address-formats-when-developing-your-app" %} + +### Why does the withdrawal (peg-out) take longer to provide a bitcoin txid from the Emily API? + +The current flow right now goes like this: + +1. The user creates a withdrawal request via a contract call on Stacks. In this example, let's say the withdrawal transaction is confirmed in a Stacks block anchored to a Bitcoin block at height N. +2. The Signers and Emily get the event from the contract call above. Emily marks the withdrawal as pending. +3. The Signers wait until that Bitcoin block is final enough, which is at Bitcoin block N+6. When that Bitcoin block arrives they create and broadcast a sweep transaction fulfilling the withdrawal request. Then the Signers tell Emily that they have accepted the withdrawal request. +4. Usually the sweep transaction is included in the next block, so it's confirmed at block N+7. +5. The Signers issue the contract call finalizing the withdrawal on Stacks, and Emily finds out about the transaction fulfilling the withdrawal. + +Here are some useful notes about the above process:\ +When the Signers tell Emily that the withdrawal has been accepted, they don't tell her about the bitcoin transaction that it's accepted in. This is intentional, because the final transaction fulfilling the withdrawal is not known until it is confirmed. It could also be the case that the Signers attempt to fulfill the withdrawal request but end up never fulfilling it. As in, the Signers could create a transaction fulfilling the withdrawal request, where they broadcast it to the Bitcoin network, but that transaction is never confirmed and never will be. Moreover, this situation is not too unlikely; it can happen when fees spike relative to the user's max fee. The current approach sidesteps all of that UX complexity and prudently informs Emily about the transaction ID after it is known to be confirmed. Moreover, some wallets can tell you if there is a payment made out to you by just examining the Bitcoin mempool. + +[^1]: ``` + type AddressInfo = { + bech32: boolean; + network: Network; + address: string; + type: AddressType; + } + ``` + +[^2]: type: string + +[^3]: type: Uint8Array diff --git a/docs/build/more-guides/sbtc/sbtc-builder-quickstart.md b/docs/build/more-guides/sbtc/sbtc-builder-quickstart.md new file mode 100644 index 0000000000..ea94fb42d3 --- /dev/null +++ b/docs/build/more-guides/sbtc/sbtc-builder-quickstart.md @@ -0,0 +1,158 @@ +# sBTC Builder Quickstart + +

        source: Hiro

        + +Get up and running with sBTC in 30 minutes or less. This guide covers the essentials for working with sBTC as a SIP-010 token in your smart contracts. + +### What is sBTC? + +sBTC is Bitcoin on Stacks. It's a SIP-010 fungible token that maintains a 1:1 peg with Bitcoin, enabling you to use Bitcoin in smart contracts and DeFi applications on the Stacks blockchain. + +Key points: + +* **1:1 Bitcoin peg**: 1 sBTC always equals 1 BTC +* **SIP-010 token**: Works like any other fungible token on Stacks +* **Programmable**: Use Bitcoin in smart contracts, DeFi protocols, and dApps + +### Quick Setup + +#### Prerequisites + +In order to get the most from this quickstart, you should familiarize yourself with Clarity, Clarinet, Stacks.js, and the Hiro Platform. These are the fundamental building blocks of building Stacks applications. + +* [Stacks Developer Quickstart](https://app.gitbook.com/o/hoh4mQXTl8NvI3cETroY/s/Zz9BLmTU9oydDpL3qiUh/) - For a quick holistic introduction to the Stacks development process, tools, and fundamentals +* [Clarity Crash Course](../../get-started/clarity-crash-course.md) - For a quick introduction to Clarity +* [Clarinet Docs](https://docs.hiro.so/tools/clarinet) +* [Stacks.js Docs](https://docs.hiro.so/reference/stacks.js) + +Choose your preferred development environment: + +#### Hiro Platform (Recommended) + +The fastest way to start building with sBTC is using the Hiro Platform's hosted devnet. The Platform integrates with your GitHub account. You can either import an existing project from GitHub or start with a Platform template and have it synced with your GitHub account. + +After you create the project in the Platform, you can clone it locally and work with the Platform's cloud devnet by connecting your API key as described in the template's README files. This will allow you to work on your code locally but let Platform handle the complexities of actually running the devnet. + +{% stepper %} +{% step %} +#### Create an account + +Create an account at:\ +https://platform.hiro.so +{% endstep %} + +{% step %} +#### Create or import a project + +* Select a template or import your own project from GitHub. There are several templates available to use as a starting point. Some have only smart contracts and some are full-stack dapp templates. Starting with one of these ensures you are building with best practices. +* Navigate to your project dashboard +{% endstep %} + +{% step %} +#### Start devnet + +* Click the "Devnet" tab +* Click "Start Devnet" +* Wait for status to show "Active" +{% endstep %} + +{% step %} +#### Connect your wallet + +* Your devnet wallets are automatically funded with STX and sBTC +* Use the provided wallet addresses within the templates +* The platform templates are automatically connected to Devnet, and there are instructions in the READMEs of the templates for how to connect your frontend to your Devnet instance +{% endstep %} +{% endstepper %} + +#### Local with Clarinet + +If you would prefer to have your devnet running locally instead of in the Platform cloud, you can run one yourself. + +{% stepper %} +{% step %} +#### Install Clarinet (version 3.x) + +```bash +brew install clarinet +``` +{% endstep %} + +{% step %} +#### Create a new project + +```bash +clarinet new my-sbtc-project +cd my-sbtc-project +``` +{% endstep %} + +{% step %} +#### Add sBTC requirements + +```bash +clarinet requirements add SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-deposit +``` + +This automatically includes the sBTC token contract in your Clarinet context so you can reference it within your contracts. +{% endstep %} + +{% step %} +#### Start devnet + +```bash +clarinet devnet start +``` + +With either of these options, your Devnet wallets are automatically funded with sBTC. You just need to include the sBTC contract in your contract requirements as shown above. +{% endstep %} +{% endstepper %} + +### Working with sBTC in Smart Contracts + +sBTC follows the SIP-010 standard, making it easy to integrate into your contracts. + +The primary function you'll be using is the `transfer` function. That's because sBTC exists as a 1:1 Bitcoin peg via a SIP-010 token. Minting is handled by the protocol, the main function of writing smart contracts that use sBTC is to move it around, which means using the `transfer` function. + +Here's a very basic example of how to transfer sBTC within your contract. + +#### Basic Transfer Example + +Create a new contract that accepts sBTC payments. You can do this within the Clarinet project folder with `clarinet contract new sbtc-payment`. + +{% code title="contracts/sbtc-payment.clar" %} +```clarity +;; contracts/sbtc-payment.clar + +;; Define the sBTC token contract reference +(define-constant sbtc-token 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token) + +;; Error codes +(define-constant err-insufficient-balance (err u100)) +(define-constant err-transfer-failed (err u101)) + +;; Accept sBTC payment +(define-public (pay-with-sbtc (amount uint) (recipient principal)) + (contract-call? sbtc-token transfer + amount + tx-sender + recipient + none)) +``` +{% endcode %} + +You can test out this contract by either using the UI within the Platform to call the functions directly if you have devnet running or by opening the console with `clarinet console`. + +Once you do that you'll see that your devnet accounts have automatically been funded with sBTC. + +
        + +Once you are ready to deploy to testnet, you can do so by editing your deployment plan as outlined in [this guide](https://docs.hiro.so/tools/clarinet/sbtc-integration). + +### Conclusion + +You can build pretty much anything you want using this simple foundation, as all of the complexity of sBTC is handled behind the scenes by the protocol. + +What's needed now is for builders to take this foundation and build interesting, useful things with it. sBTC unlocks a lot of additional functionality for Bitcoin that previously would have only been possible with either custodied solutions or slow, complex solutions with poor UX. + +If you are interested, you can read more about how sBTC works in the [sBTC Concept Guide](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/sbtc). diff --git a/docs/build/more-guides/verify-bitcoin-transactions-clarity/README.md b/docs/build/more-guides/verify-bitcoin-transactions-clarity/README.md new file mode 100644 index 0000000000..2d4669c729 --- /dev/null +++ b/docs/build/more-guides/verify-bitcoin-transactions-clarity/README.md @@ -0,0 +1,589 @@ +--- +description: Trustlessly validate Bitcoin data on-chain. +--- + +# Verifying Bitcoin Transactions in Clarity + +
        + +{% hint style="info" %} +The current version of the `clarity-bitcoin-lib` is version 7. Click [here](https://explorer.hiro.so/txid/0xe433b35e95acfd24595e601860b4240cfaacd689ae7e7938a80c5505f186516b) for the deployed mainnet contract. +{% endhint %} + +## Intro + +One of the unique features of the Stack chain and the Clarity language is that it allows for using read-only functions to trustlessly validate on-chain Bitcoin activity from Clarity smart contracts. This enables developers to build applications that **react to real Bitcoin activity** — such as deposits, transfers, and proofs of inclusion — without relying on off-chain indexers or oracles. In this guide, you’ll learn how to verify Bitcoin transactions in Clarity and safely bridge Bitcoin state into on-chain logic. + +> _Knowledge of Bitcoin state: has knowledge of the full Bitcoin state; it can trustlessly read_\ +> _Bitcoin transactions and state changes and execute smart contracts triggered by Bitcoin_\ +> _transactions. The Bitcoin read functionality helps to keep the decentralized peg state_\ +> _consistent with BTC locked on Bitcoin L1, amongst other things. **- Stacks Whitepaper**_ + +### tl;dr + +* We'll be using a popular smart contract `clarity-bitcoin-lib` to verify bitcoin transactions on-chain in Clarity. +* Allows contracts to enforce logic based on Bitcoin events—deposits, withdrawals, lockups, or multisig activity. +* Powers designs like wrapped assets, and deposits that mint or unlock value only after provable Bitcoin transactions. + +### Steps + +* Fetch bitcoin transaction metadata: block height and header, transaction hex, and the transaction's merkle proof of inclusion. +* Pass in transaction metadata as arguments to `clarity-bitcoin-lib` contract's `was-tx-mined-compact` read-only function. +* Returns `(ok )` if the proof checks out and the transaction is indeed mined in the specified Bitcoin block. + +### Key Tools To Use + +* [Clarity Bitcoin library](https://github.com/friedger/clarity-bitcoin): A Clarity library for parsing Bitcoin transactions and verifying Merkle proofs. +* [mempool.space APIs](https://mempool.space/docs/api/rest): Bitcoin explorer APIs used to fetch bitcoin transaction metadata. +* [Bitcoin Transaction Proof](https://github.com/kenrogers/bitcoin-tx-proof) \[optional]: A TypeScript library for generating Bitcoin transaction proofs, including witness data and merkle proofs. +* [Clarity Bitcoin Client](https://github.com/BigMarketDao/clarity-bitcoin-client) \[optional]: Clarity Bitcoin Client is an open-source TypeScript library for interacting with the `clarity-bitcoin-lib` contract on Stacks. + +*** + +## Complete Code + +If you want to jump straight to the full implementation, the complete working code used in this guide is shown below. + +{% tabs %} +{% tab title="Main" %} +The below consists of the main client side code of preparing the bitcoin transaction metadata and passing them into a contract call to the `clarity-bitcoin-lib` contract. + +{% code expandable="true" %} +```typescript +import { + fetchCallReadOnlyFunction, + bufferCV, + uintCV, + tupleCV, + listCV, + cvToString, +} from "@stacks/transactions" +import { hexToBytes } from "@stacks/common" +import { getTxHex, getTxMerkleProof, getBlkHeader, removeWitnessData } from "./fetch-bitcoin-data.js" + +// Data of read-only function of below clarity-bitcoin-lib contract: +const contractAddress = "SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9" +const contractName = "clarity-bitcoin-lib-v7" +const functionName = "was-tx-mined-compact" + +// Fetching btc tx metadata. You can replace this with any bitcoin transaction id. +let txid = "fb88309b4041f76bea0c196633c768dca82bb0dd424cbfe19be38569007f92d9" + +// Fetching and returning non-witness tx hex +let fullTxHex = await getTxHex(txid) +let txHex = removeWitnessData(fullTxHex) +let { block_height, merkle, pos } = await getTxMerkleProof(txid) +let { blockHeader } = await getBlkHeader(block_height) + +let txIndex = pos +let hashes = merkle.map(hash => bufferCV(hexToBytes(hash).reverse())) +let treeDepth = merkle.length + +// Preparing arguments for clarity function call +let functionArgs = [ + // (height) + uintCV(block_height), + // (tx) + bufferCV(Buffer.from(txHex, "hex")), + // (header) + bufferCV(Buffer.from(blockHeader, "hex")), + // (proof) + tupleCV({ + "tx-index": uintCV(txIndex), + hashes: listCV(hashes), + "tree-depth": uintCV(treeDepth) + }) +] + +// Calling clarity read-only function +let result = await fetchCallReadOnlyFunction({ + contractAddress, + contractName, + functionName, + functionArgs, + network: 'mainnet', + // this could be any principal address + senderAddress: 'SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R' +}) + +// Logging results +console.log("Full tx hex: ", fullTxHex) +console.log("Non-witness tx hex: ", txHex) +console.log("Block height: ", block_height) +console.log("Block header: ", blockHeader) +console.log("Merkle proof hashes: ", merkle) +console.log("Transaction index in block: ", txIndex) +console.log("Merkle tree depth: ", treeDepth) +console.log("Was the transaction mined (from clarity-bitcoin)? ") +console.log(cvToString(result)) +``` +{% endcode %} +{% endtab %} + +{% tab title="Helpers" %} +Helper functions to fetch the bitcoin transaction metadata from an explorer's API. + +{% code title="fetch-bitcoin-data.js" expandable="true" %} +```typescript +import mempoolJS from "@mempool/mempool.js" +import { Transaction } from "bitcoinjs-lib" + +// https://www.npmjs.com/package/@mempool/mempool.js +const { + bitcoin: { transactions, blocks } +} = mempoolJS({ + hostname: "mempool.space" +}) + +export const getTxHex = async (txid: string) => { + // Using mempool.space's GET /api/tx/:txid/hex + const txHex = await transactions.getTxHex({ txid }) + + return txHex +} + +export const getTxMerkleProof = async (txid: string) => { + // Using mempool.space's GET /api/tx/:txid/merkle-proof + const { block_height, merkle, pos } = await transactions.getTxMerkleProof({ txid }) + + return { block_height, merkle, pos } +} + +export const getBlkHeader = async (height: number) => { + // Using mempool.space's GET /api/block-height/:height + let blockHash = await blocks.getBlockHeight({ height }) + + // Using mempool.space's GET /api/block/:hash/header + const blockHeader = await blocks.getBlockHeader({ hash: blockHash }) + + return { blockHash, blockHeader } +} + +// Function to remove witness data from a Bitcoin transaction hex +// `was-tx-mined-compact` requires non-witness transaction format +export const removeWitnessData = (txHex: string) => { + const tx = Transaction.fromHex(txHex) + + if (!tx.hasWitnesses()) { + return txHex + } + + // Create a new empty transaction + const newTx = new Transaction() + + // Copy version from original transaction + newTx.version = tx.version + + // Copy inputs from original transaction + tx.ins.forEach(input => { + newTx.addInput(input.hash, input.index, input.sequence, input.script) + }) + + // Copy outputs from original transaction + tx.outs.forEach(output => { + newTx.addOutput(output.script, output.value) + }) + + // Copy locktime from original transaction + newTx.locktime = tx.locktime + + return newTx.toHex() +} +``` +{% endcode %} +{% endtab %} + +{% tab title="Clarity Contract Preview" %} +This is a snippet of the contract function we invoke to verify a bitcoin transaction. + +{% code title="clarity-bitcoin-lib.clar" expandable="true" %} +```clarity +;; --snip-- + +(define-read-only (was-tx-mined-compact + (height uint) + (tx (buff 4096)) + (header (buff 80)) + (proof { + tx-index: uint, + hashes: (list 14 (buff 32)), + tree-depth: uint, + }) + ) + (let ((block (unwrap! (parse-block-header header) (err ERR-BAD-HEADER)))) + (was-tx-mined-internal height tx header (get merkle-root block) proof) + ) +) + +;; --snip-- +``` +{% endcode %} +{% endtab %} +{% endtabs %} + +*** + +## Walkthrough + +{% stepper %} +{% step %} +### Fetch bitcoin transaction metadata + +Using mempool.space's APIs and with a bitcoin txid, fetch the required bitcoin transaction metadata needed for Clarity. + +* Fetches the transaction hex +* Fetches the transaction merkle proof +* Fetches the block header +* Removes witness data from transaction hex + +{% code title="fetch-bitcoin-data.ts" expandable="true" %} +```typescript +import mempoolJS from "@mempool/mempool.js" +import { Transaction } from "bitcoinjs-lib" + +// https://www.npmjs.com/package/@mempool/mempool.js +const { + bitcoin: { transactions, blocks } +} = mempoolJS({ + hostname: "mempool.space" +}) + +export const getTxHex = async (txid: string) => { + // Using mempool.space's GET /api/tx/:txid/hex + const txHex = await transactions.getTxHex({ txid }) + + return txHex +} + +export const getTxMerkleProof = async (txid: string) => { + // Using mempool.space's GET /api/tx/:txid/merkle-proof + const { block_height, merkle, pos } = await transactions.getTxMerkleProof({ txid }) + + return { block_height, merkle, pos } +} + +export const getBlkHeader = async (height: number) => { + // Using mempool.space's GET /api/block-height/:height + let blockHash = await blocks.getBlockHeight({ height }) + + // Using mempool.space's GET /api/block/:hash/header + const blockHeader = await blocks.getBlockHeader({ hash: blockHash }) + + return { blockHash, blockHeader } +} + +// Function to remove witness data from a Bitcoin transaction hex +// `was-tx-mined-compact` requires non-witness transaction format +export const removeWitnessData = (txHex: string) => { + const tx = Transaction.fromHex(txHex) + + if (!tx.hasWitnesses()) { + return txHex + } + + // Create a new empty transaction + const newTx = new Transaction() + + // Copy version from original transaction + newTx.version = tx.version + + // Copy inputs from original transaction + tx.ins.forEach(input => { + newTx.addInput(input.hash, input.index, input.sequence, input.script) + }) + + // Copy outputs from original transaction + tx.outs.forEach(output => { + newTx.addOutput(output.script, output.value) + }) + + // Copy locktime from original transaction + newTx.locktime = tx.locktime + + return newTx.toHex() +} +``` +{% endcode %} + +
        + +Are there libraries to help with fetching of the bitcoin transaction metadata? + +There sure are. Here are two community-built libraries that can abstract away some of the complexities with gathering the bitcoin transaction metadata. + +* [**Bitcoin Transaction Proof**](https://github.com/kenrogers/bitcoin-tx-proof) - A TypeScript library for generating Bitcoin transaction proofs, including witness data and merkle proofs. +* [**Clarity Bitcoin Client**](https://github.com/BigMarketDao/clarity-bitcoin-client) - A TypeScript library for interacting with the clarity-bitcoin-lib contract on Stacks. + +
        + +
        + +Don't have a bitcoin transaction id? + +Learn how to create and broadcast a bitcoin transaction on the frontend [here](creating-btc-tx.md). + +
        +{% endstep %} + +{% step %} +### Prepare metadata as Clarity arguments + +Let's circle back to the `was-tx-mined-compact` function of the `clarity-bitcoin-lib` contract for a second and analyze the order/type of parameters. + +You can see that it intakes the parameters with a certain typing and order: + +* `(height uint)` the block height you are looking to verify the transaction within +* `(tx (buff 1024))` the raw transaction hex of the transaction you are looking to verify +* `(header (buff 80))` the block header of the block +* `(proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint})` a merkle proof formatted as a Clarity tuple + +In short, the `was-tx-mined-compact` function takes the block height, the transaction hex, the block header, and a merkle proof, and determines that: + +* the block header corresponds to the block that was mined at the given Bitcoin height +* the transaction's merkle proof links it to the block header's merkle root. + +{% hint style="info" %} +The current version of the `clarity-bitcoin-lib` is version 7. Click [here](https://explorer.hiro.so/txid/0xe433b35e95acfd24595e601860b4240cfaacd689ae7e7938a80c5505f186516b) for the deployed mainnet contract. +{% endhint %} + +{% code title="clarity-bitcoin-lib.clar" %} +```clarity +;; --snip-- + +(define-read-only (was-tx-mined-compact + (height uint) + (tx (buff 4096)) + (header (buff 80)) + (proof { + tx-index: uint, + hashes: (list 14 (buff 32)), + tree-depth: uint, + }) + ) + (let ((block (unwrap! (parse-block-header header) (err ERR-BAD-HEADER)))) + (was-tx-mined-internal height tx header (get merkle-root block) proof) + ) +) + +;; --snip-- +``` +{% endcode %} + +So after you've fetched the bitcoin tx metadata and have removed the witness data from the original transaction hex, let's go ahead and prepare the metadata into it's Clarity parameter values. + +{% code expandable="true" %} +```typescript +import { + fetchCallReadOnlyFunction, + bufferCV, + uintCV, + tupleCV, + listCV, + cvToString, +} from "@stacks/transactions" +import { hexToBytes } from "@stacks/common" +import { getTxHex, getTxMerkleProof, getBlkHeader, removeWitnessData } from "./fetch-bitcoin-data.js" + +// Data of read-only function of below clarity-bitcoin-lib contract: +const contractAddress = "SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9" +const contractName = "clarity-bitcoin-lib-v7" +const functionName = "was-tx-mined-compact" + +// Fetching btc tx metadata. You can replace this with any bitcoin transaction id. +let txid = "fb88309b4041f76bea0c196633c768dca82bb0dd424cbfe19be38569007f92d9" + +// Fetching and returning non-witness tx hex +let fullTxHex = await getTxHex(txid) +let txHex = removeWitnessData(fullTxHex) +let { block_height, merkle, pos } = await getTxMerkleProof(txid) +let { blockHeader } = await getBlkHeader(block_height) + +let txIndex = pos +let hashes = merkle.map(hash => bufferCV(hexToBytes(hash).reverse())) +let treeDepth = merkle.length + +// Preparing arguments for clarity function call +let functionArgs = [ + // (height) + uintCV(block_height), + // (tx) + bufferCV(Buffer.from(txHex, "hex")), + // (header) + bufferCV(Buffer.from(blockHeader, "hex")), + // (proof) + tupleCV({ + "tx-index": uintCV(txIndex), + hashes: listCV(hashes), + "tree-depth": uintCV(treeDepth) + }) +] +``` +{% endcode %} + +
        + +What is the purpose of the merkle proof? + +A Merkle proof is a mathematically efficient and compact manner to prove that a transaction is included in a block in the Bitcoin blockchain. + +#### How transactions are combined into the Merkle root + +Transactions in a block are hashed and paired, then the hashes of the pairs are hashed and paired, and so on until a single hash remains — this is called the Merkle root. + +#### Merkle root in the block header + +The Merkle root is included in the block header. By providing the hashes that lead from a transaction's hash up to the Merkle root, along with the block header, one can prove that the transaction is included in that block. + +#### Merkle proof (Merkle path) + +The hashes that connect a transaction to the Merkle root are called the Merkle proof or Merkle path. By providing the Merkle proof along with the transaction hash and block header, anyone can verify that the transaction is part of that block. + +#### Efficient decentralized verification + +This allows for efficient decentralized verification of transactions without having to download the entire blockchain. One only needs the transaction hash, Merkle proof, and block header to verify. + +
        + +
        + +Why are we removing the witness data from the original tx hex? + +The `clarity-bitcoin-lib` contract's `was-tx-mined-compact` function only accepts a non-witness transaction hex. Usually only legacy bitcoin transactions are deemed as non-witness transactions. The contract also has a dedicated function for bitcoin transactions with witness data called `was-segwit-tx-mined-compact` . + +But you could still verify any transaction type in `was-tx-mined-compact` by simply removing the witness data from the original tx hex. + +
        +{% endstep %} + +{% step %} +### Invoke \`was-tx-mined-compact\` function + +Construct a read-only function call, pass in the contract call options, and await the results. If the bitcoin transaction in question is indeed mined in an existing Bitcoin block, the contract will return a response that looks like: + +`(ok 0xfb88309b4041f76bea0c196633c768dca82bb0dd424cbfe19be38569007f92d9)` + +You'll see your exact bitcoin txid returned wrapped in an `ok` response. + +{% code expandable="true" %} +```typescript +// --snip-- + +// Calling clarity read-only function +let result = await fetchCallReadOnlyFunction({ + contractAddress, + contractName, + functionName, + functionArgs, + network: 'mainnet', + // this could be any principal address + senderAddress: 'SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R' +}) + +// Logging results +console.log("Full tx hex: ", fullTxHex) +console.log("Non-witness tx hex: ", txHex) +console.log("Block height: ", block_height) +console.log("Block header: ", blockHeader) +console.log("Merkle proof hashes: ", merkle) +console.log("Transaction index in block: ", txIndex) +console.log("Merkle tree depth: ", treeDepth) +console.log("Was the transaction mined (from clarity-bitcoin)? ") +console.log(cvToString(result)) +``` +{% endcode %} +{% endstep %} +{% endstepper %} + +That's the end-to-end flow: fetch the bitcoin transaction metadata and its merkle proof, prepare and assemble the data for the contract's read-only function call, and finally call the Clarity contract function that verifies inclusion in a Bitcoin block. + +*** + +## Example Usage + +Here are some example projects/contracts that would leverage the `clarity-bitcoin-lib` contract in their own code. + +#### Square Runes + +A Clarity implementation for parsing Bitcoin Runes protocol data. Check out the project repo [here](https://github.com/Rapha-btc/square-runes). + +
        ;; --snip--
        +
        +;; Main entry point: Process a Runes deposit with full merkle proof verification
        +(define-public (process-deposit
        +    (height uint)
        +    (wtx {
        +      version: (buff 4),
        +      ins: (list 50 { outpoint: { hash: (buff 32), index: (buff 4) }, scriptSig: (buff 1376), sequence: (buff 4) }),
        +      outs: (list 50 { value: (buff 8), scriptPubKey: (buff 1376) }),
        +      locktime: (buff 4),
        +    })
        +    (witness-data (buff 1650))
        +    (header (buff 80))
        +    (tx-index uint)
        +    (tree-depth uint)
        +    (wproof (list 14 (buff 32)))
        +    (witness-merkle-root (buff 32))
        +    (witness-reserved-value (buff 32))
        +    (ctx (buff 4096))
        +    (cproof (list 14 (buff 32)))
        +    (mom-token <sr-trait>)
        +  )
        +  (let (
        +      ;; Build full tx buffer
        +      (tx-buff (contract-call?
        +        'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.bitcoin-helper-wtx-v2
        +        concat-wtx wtx witness-data
        +      ))
        +    )
        +    ;; Verify tx was mined on Bitcoin
        +    (match (contract-call?
        +      'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.clarity-bitcoin-lib-v7
        +      was-segwit-tx-mined-compact height tx-buff header tx-index tree-depth
        +      wproof witness-merkle-root witness-reserved-value ctx cproof
        +    )
        +      btc-tx-id (process-verified-deposit btc-tx-id tx-buff height mom-token)
        +      error (err (* error u1000)) ;; ERR-TX-NOT-MINED
        +    )
        +  )
        +)
        +
        +;; --snip--
        +
        + +#### Bitcoin Transaction Enabled NFT + +This contract example implements a basic NFT collection that is mintable only upon a user's bitcoin transaction. You can find this template available in the [Hiro Platform](https://platform.hiro.so/). + +
        ;; --snip--
        +
        +;; Mint a new NFT if a specific bitcoin transaction has been mined.
        +(define-public (mint (recipient principal) (height uint) (tx (buff 1024)) (header (buff 80)) (proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint}))
        +    (let
        +        (
        +            ;; Create the new token ID by incrementing the last minted ID.
        +            (token-id (+ (var-get last-token-id) u1))
        +            ;; Calls external contract function to confirm mined status on the supplied bitcoin transaction data. Will return (ok txid)
        +            (tx-was-mined (contract-call? 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.clarity-bitcoin-lib-v7 was-tx-mined-compact height tx header proof))
        +        )
        +        ;; Only the contract owner can mint.
        +        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        +        ;; Confirms if supplied bitcoin transaction data has been mined or not.
        +        (asserts! (is-ok tx-was-mined) err-tx-not-mined)
        +        ;; Mint the NFT and send it to the given recipient.
        +        (try! (nft-mint? Your-NFT-Name token-id recipient))
        +        ;; Update the last minted token ID.
        +        (var-set last-token-id token-id)
        +        ;; Return a success status and the newly minted NFT ID.
        +        (ok token-id)
        +    )
        +)
        +
        +;; --snip--
        +
        + +*** + +## Additional Resources + +* \[[Hiro YT](https://youtu.be/mPba9vJEDlc?si=wTCqweRWWFP-jyCJ)] Bitcoin Enabled NFT - Stacks NFTs Minted By Bitcoin Transactions +* \[[Hiro Blog](https://www.hiro.so/blog/how-to-use-bitcoin-data-in-clarity-unit-tests)] How to Use Bitcoin Data in Clarity Unit Tests diff --git a/docs/build/more-guides/verify-bitcoin-transactions-clarity/creating-btc-tx.md b/docs/build/more-guides/verify-bitcoin-transactions-clarity/creating-btc-tx.md new file mode 100644 index 0000000000..dd91f86d07 --- /dev/null +++ b/docs/build/more-guides/verify-bitcoin-transactions-clarity/creating-btc-tx.md @@ -0,0 +1,38 @@ +# Creating a Bitcoin Transaction + +For usage with the `clarity-bitcoin-lib` contract or if you just want to learn how to invoke a bitcoin transaction from your wallet on the front end, check out this guide. + +Using Stacks Connect and with a Stacks-supported wallet, you can initiate a simple Bitcoin transaction from a frontend app in a few lines of code. With this Bitcoin transaction, you can then use to verify it's inclusion in a Bitcoin block through Clarity. + +{% hint style="info" %} +Check out the [Stacks Connect](/broken/pages/JLRpUuDHPMxXaInZ9Vll) guides for more info on setup and wallet connection. +{% endhint %} + +{% stepper %} +{% step %} +#### Initiate a Bitcoin transaction + +Use the `request("sendTransfer", ...)` method to initiate a bitcoin transaction. Provide the recipient address and the amount in satoshis. + +```typescript +import { request } from '@stacks/connect'; + +const result = await request('sendTransfer', { + recipients: [ + { + address: "", + amount: 100_000, + }, + ], + }) + +let txid = result.txid; +``` +{% endstep %} + +{% step %} +#### Cache the \`txid\` + +As you'll see in the next section, in order to verify a transaction was mined in Clarity, you'll use the returned `txid` to fetch its transaction metadata. The transaction metadata can be fetched from a Bitcoin explorer or from some custom helper libraries built by the community. So it's important to cache or store the `txid` for your app. +{% endstep %} +{% endstepper %} diff --git a/docs/build/more-guides/verify-bitcoin-transactions-clarity/parsing-a-bitcoin-transaction.md b/docs/build/more-guides/verify-bitcoin-transactions-clarity/parsing-a-bitcoin-transaction.md new file mode 100644 index 0000000000..3c0311b120 --- /dev/null +++ b/docs/build/more-guides/verify-bitcoin-transactions-clarity/parsing-a-bitcoin-transaction.md @@ -0,0 +1,166 @@ +# Parsing a Bitcoin Transaction + +### Intro + +While we can verify that a transaction was mined using the `clarity-bitcoin-lib` contract library, we can also parse a Bitcoin transaction using Clarity directly. + +If you aren't familiar with how Bitcoin transactions are encoded in raw form, take a quick look at that. The tl;dr is that all of the data from a Bitcoin transaction is encoded in hexadecimal form in a string of bytes; we can slice out pieces of that hex value to pull out all of our transaction data components. + +The process to do this is relatively complex, but the `clarity-bitcoin-lib` provides a function called `parse-tx` or `parse-wtx` (for witness transactions) that makes this simple. All we need to do is pass in a raw transaction hex and we get back the data of the transaction, including inputs and outputs. + +{% hint style="info" %} +The current version of the `clarity-bitcoin-lib` is version 7. Click [here](https://explorer.hiro.so/txid/0xe433b35e95acfd24595e601860b4240cfaacd689ae7e7938a80c5505f186516b) for the deployed mainnet contract. +{% endhint %} + +The `parse-tx` function of the `clarity-bitcoin-lib` contract looks like this: + +{% code title="clarity-bitcoin-lib" %} +```clarity +;; --snip-- + +(define-read-only (parse-tx (tx (buff 4096))) + (let ( + (ctx { + txbuff: tx, + index: u0, + }) + (parsed-version (try! (read-uint32 ctx))) + (parsed-txins (try! (read-txins (get ctx parsed-version)))) + (parsed-txouts (try! (read-txouts (get ctx parsed-txins)))) + (parsed-locktime (try! (read-uint32 (get ctx parsed-txouts)))) + ) + ;; check if it is a non-segwit transaction? + ;; at least check what happens + (asserts! (is-eq (len tx) (get index (get ctx parsed-locktime))) + (err ERR-LEFTOVER-DATA) + ) + (ok { + version: (get uint32 parsed-version), + ins: (get txins parsed-txins), + outs: (get txouts parsed-txouts), + locktime: (get uint32 parsed-locktime), + }) + ) +) +``` +{% endcode %} + +Where `txRaw` is a string containing the raw transaction hex. Passing that buffer into `parse-tx` returns the parsed transaction object with version, inputs (ins), outputs (outs), and locktime. You can then extract whatever fields you need from that returned data. + +*** + +### Steps + +{% stepper %} +{% step %} +#### Get the raw transaction hex + +Obtain the raw transaction hex from a Bitcoin explorer API. This is a single hex string representing the entire transaction. + +Example raw bitcoin transaction hex: + +`0200000000010196277c04c986c1ad78c909287fd12dba2924324699a0232e0533f46a6a3916bb0100000000ffffffff026400000000000000160014274ae586ad2035efb4c25049c155f98310d7e106ca16440000000000160014599bcef6387256c6b019030c421b4a4d382fe2600247304402204d94a1e4047ca38a450177ccb6f88585ca147f1939df343d8ac5d962c5f35bb302206f7fa42c21c47ebccdc460393d35c5dfd3b6f0a26cf10fac23d3e6fab71835c20121020cb972a66e3fb1cdcc9efcad060b4457ebec534942700d4af1c0d82a33aa13f100000000` + +{% hint style="info" %} +You can paste this into a raw transaction decoder like this one to inspect the decoded fields: [https://live.blockcypher.com/btc/decodetx/](https://live.blockcypher.com/btc/decodetx/) +{% endhint %} +{% endstep %} + +{% step %} +#### Pass the transaction hex into Clarity-Bitcoin's parser + +Convert the hex string to a Clarity buffer and pass it to the `parse-tx` function (via your stack.js read-only function call). In stacks.js: + +If using stacks.js, pass the raw hex to your Clarity function as a Clarity typed buffer: + +
        import {
        +  fetchCallReadOnlyFunction,
        +  bufferCV,
        +} from "@stacks/transactions"
        +import { hexToBytes } from "@stacks/common"
        +
        +let txRaw = '<raw-transaction-hex>'
        +
        +// Data of read-only function of below clarity-bitcoin-lib contract:
        +const contractAddress = "SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9"
        +const contractName = "clarity-bitcoin-lib-v7"
        +const functionName = "parse-tx"
        +
        +// Calling clarity read-only function
        +let result = await fetchCallReadOnlyFunction({
        +  contractAddress,
        +  contractName,
        +  functionName,
        +  functionArgs: [bufferCV(Buffer.from(txRaw, "hex"))],
        +  network: 'mainnet',
        +  // this could be any principal address
        +  senderAddress: 'SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R'
        +})
        +
        +{% endstep %} + +{% step %} +#### Examine and use the parsed result + +`parse-tx` returns an object containing: + +* version +* ins (transaction inputs) +* outs (transaction outputs) +* locktime + +Extract whatever fields you need from that returned object. +{% endstep %} +{% endstepper %} + +*** + +### Example Usage + +#### Square Runes + +A Clarity implementation for parsing Bitcoin Runes protocol data, allowing Stacks smart contracts to understand and react to Runes transactions on the Bitcoin chain. + +Check out the project repo [here](https://github.com/Rapha-btc/square-runes). + +
        ;; --snip--
        +
        +;; ============================================
        +;; PARSING HELPERS (using runes-decoder)
        +;; ============================================
        +
        +;; Parse OP_RETURN from tx buffer using the runes-decoder library
        +(define-read-only (parse-deposit-opreturn (tx-buff (buff 4096)))
        +  (let (
        +      (output0 (unwrap! (get-output-at-index tx-buff u0) (err u500)))
        +      (script (get scriptPubKey output0))
        +    )
        +    ;; decode-any-runestone is more generic - handles Tag 0, 11, 22
        +    (contract-call? .runes-decoder decode-any-runestone script)
        +  )
        +)
        +
        +;; Get output at specific index from parsed tx
        +;; is this different for legacy Rafa?
        +(define-read-only (get-output-at-index (tx (buff 4096)) (index uint))
        +  (let (
        +      (parsed-tx (contract-call?
        +        'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.clarity-bitcoin-lib-v7
        +        parse-wtx tx false
        +      ))
        +    )
        +    (match parsed-tx
        +      result (let (
        +          (outs (get outs result))
        +          (out (unwrap! (element-at? outs index) (err u502)))
        +        )
        +        (ok {
        +          scriptPubKey: (get scriptPubKey out),
        +          value: (get value out)
        +        })
        +      )
        +      error (err u503) ;; missing
        +    )
        +  )
        +)
        +
        diff --git a/docs/build/post-conditions/implementation.md b/docs/build/post-conditions/implementation.md new file mode 100644 index 0000000000..6c157826dc --- /dev/null +++ b/docs/build/post-conditions/implementation.md @@ -0,0 +1,228 @@ +--- +description: Learn how to add post-conditions to protect your Stacks transactions. +--- + +# Implementing Post-Conditions + +Post-conditions are a powerful security feature in Stacks that protect users from unexpected transaction outcomes. This tutorial will walk you through implementing post-conditions in your applications to ensure transactions behave exactly as users expect. + +## What you'll learn + +* Construct post-conditions using the `Pc` helper API +* Add post-conditions to different transaction types +* Configure post-condition modes for transaction security +* Implement post-conditions for STX, fungible tokens, and NFTs +* Handle semi-fungible tokens (SFTs) with post-conditions + +## Prerequisites + +* Basic understanding of Stacks transactions +* Stacks.js library installed (`npm install @stacks/transactions`) +* A development environment set up for Stacks + +## Constructing post-conditions + +The Pc helper in Stacks.js provides a fluent, BDD-inspired API for constructing post-conditions. Start with `Pc.principal()` to specify which address will be verified, then chain methods to define the condition. + +```ts +import { Pc } from '@stacks/transactions'; + +// Basic structure of a post-condition +const postCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendEq(1000) + .ustx(); +``` + +The Pc helper uses method chaining for intuitive condition building. Your IDE will provide auto-completion for available methods at each step. + +## Available transfer methods + +Post-conditions support different comparison operators and asset types. Choose the appropriate method based on your security requirements. + +### STX and fungible token methods + +```ts +// Exact amount +Pc.principal(address).willSendEq(1000).ustx(); + +// Greater than or equal +Pc.principal(address).willSendGte(500).ustx(); + +// Less than +Pc.principal(address).willSendLt(2000).ustx(); +``` + +Comparison methods available: + +* `.willSendEq(amount)` - Exactly equal to amount +* `.willSendGte(amount)` - Greater than or equal to amount +* `.willSendGt(amount)` - Greater than amount +* `.willSendLte(amount)` - Less than or equal to amount +* `.willSendLt(amount)` - Less than amount + +### Asset type methods + +```ts +// STX transfers +.ustx() + +// Fungible token transfers +.ft(contractAddress, tokenName) + +// NFT transfers +.nft(assetIdentifier, tokenId) +``` + +### NFT-specific methods + +```ts +// Ensure NFT is sent +Pc.principal(address).willSendAsset().nft(...); + +// Ensure NFT is NOT sent +Pc.principal(address).willNotSendAsset().nft(...); +``` + +## Setting the post-condition mode + +The post-condition mode determines how the Stacks blockchain handles asset transfers not explicitly covered by your post-conditions. This is a critical security setting. + +```ts +import { PostConditionMode, makeContractCall } from '@stacks/transactions'; + +const tx = await makeContractCall({ + // ... other transaction properties + postConditionMode: PostConditionMode.Deny, // Recommended default + postConditions: [ + // your post-conditions here + ], +}); +``` + +Mode options: + +* PostConditionMode.Deny (default): Transaction fails if any unspecified transfers occur +* PostConditionMode.Allow: Transaction allows transfers beyond specified post-conditions + +## Common implementation patterns + +### STX transfer post-conditions + +Protect STX transfers by specifying exact amounts or ranges. + +```ts +import { Pc, makeSTXTokenTransfer } from '@stacks/transactions'; + +// Exact amount post-condition +const exactAmountCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendEq(1000) + .ustx(); + +// Use in a transaction +const tx = await makeSTXTokenTransfer({ + recipient: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + amount: 1000, + postConditions: [exactAmountCondition], + postConditionMode: PostConditionMode.Deny, + // ... other properties +}); +``` + +### Fungible token post-conditions + +Ensure fungible tokens are transferred as expected in contract calls. + +```ts +import { Pc, makeContractCall } from '@stacks/transactions'; + +// Minimum amount condition +const ftCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendGte(500) + .ft('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6.token-ft', 'token'); + +// Use in a contract call +const tx = await makeContractCall({ + contractAddress: 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6', + contractName: 'token-transfer', + functionName: 'transfer', + functionArgs: [ + // ... function arguments + ], + postConditions: [ftCondition], + // ... other properties +}); +``` + +### NFT transfer post-conditions + +Control NFT ownership changes with specific post-conditions. + +```ts +import { Pc, Cl } from '@stacks/transactions'; + +// Ensure NFT is sent +const sendNftCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendAsset() + .nft('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6.nft-contract::nft-name', Cl.uint(1)); + +// Ensure NFT is NOT sent (protection against unwanted transfers) +const keepNftCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willNotSendAsset() + .nft('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6.nft-contract::nft-name', Cl.uint(1)); +``` + +Use `willNotSendAsset()` to protect valuable NFTs from being transferred unexpectedly. + +### Semi-fungible token (SFT) post-conditions + +SFTs require special handling as they have both fungible and non-fungible properties. + +```ts +import { Cl, Pc } from '@stacks/transactions'; + +// SFT as NFT (specific token ID) +const sftNftCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendAsset() + .nft( + 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.sft-contract::sft-id', + Cl.tuple({ + 'token-id': Cl.uint(1), + owner: Cl.principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + }) + ); + +// SFT as FT (amount-based) +const sftFtCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendEq(500) + .ft('ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.sft-contract', 'sft-token'); +``` + +## Multiple post-conditions + +Complex transactions often require multiple post-conditions to fully protect all asset transfers. + +```ts +const tx = await makeContractCall({ + // ... transaction properties + postConditions: [ + // Sender must send exactly 1000 uSTX + Pc.principal(senderAddress).willSendEq(1000).ustx(), + + // Contract must send at least 100 tokens to user + Pc.principal(contractAddress).willSendGte(100) + .ft(contractAddress + '.my-token', 'my-token'), + + // User must not lose their NFT + Pc.principal(senderAddress).willNotSendAsset() + .nft(nftContract + '::my-nft', Cl.uint(1)), + ], + postConditionMode: PostConditionMode.Deny, +}); +``` diff --git a/docs/build/post-conditions/overview.md b/docs/build/post-conditions/overview.md new file mode 100644 index 0000000000..f6e889491f --- /dev/null +++ b/docs/build/post-conditions/overview.md @@ -0,0 +1,177 @@ +--- +description: Learn how post-conditions protect users from unexpected transaction outcomes. +--- + +# Overview + +

        source: Hiro Blog

        + +### What are post-conditions? + +Post-conditions are assertions about an on-chain transaction that must be met; otherwise, the transaction will abort during execution. In other words, post-conditions act as a safety net, allowing you to specify what state changes can occur in a transaction. This logic helps limit the amount of damage that can be done to a user and their assets, whether due to a bug or malicious behavior. + +Put simply, post conditions are a set of conditions that must be met before a user's transaction will execute. The primary goal behind post conditions is to limit the amount of damage that can be done to a user's assets due to a bug, intentional or otherwise. + +Post conditions are an additional safety feature built into the Stacks chain itself that help to protect end users. Rather than being a function of Clarity smart contracts, they are implemented on the client side and meant to be an additional failsafe against malicious contracts. + +They are sent as part of the transaction when the user initiates it, meaning we need to implement post-conditions on the frontend. Whenever you are transferring an asset (fungible or non-fungible) from one address to another, you should take advantage of post conditions. + +### The post-condition stack + +Post-conditions are enforced by the Stacks protocol itself but do not exist in the smart contracts themselves. Instead, they are programmatically constructed in your front-end application code using Stacks.js, specifically by passing them in as options to the transaction payload construction. + +By having post-conditions in the frontend code, Stacks-enabled wallets, such as Leather and Xverse, are able to display the post-conditions in a human-readable format for the user when confirming their transactions. Once a user confirms the transaction, the post-conditions get carried along with the transaction payload where eventually the Stacks protocol will evaluate them together. + +

        A visualization of the “post-conditions stack" and the entities involved

        + +If there were no post-conditions in the front-end application code, a user’s wallet will display an abstract warning message, where it would be up to the user to decide whether they want to blindly proceed with the transaction or not. And whatever the underlying contract code wants to do, it will do without any post-condition restrictions. So if a contract tries to send your STX tokens to a drainer wallet, it will without you knowing. + +Even with post-conditions set up on the frontend code, a user is still blind to the underlying Clarity smart contract code, but at least they know what to _expect_ will happen in the transaction. And if that expectation is not met, the transaction will abort and fail. + +### Example of a post-condition + +Post-conditions act as safeguards that verify asset transfers match your expectations. They can check STX transfers, fungible tokens, and non-fungible token ownership changes. + +```ts +import { Pc } from '@stacks/transactions'; + +const tx = await makeContractCall({ + // ... + postConditions: [ + Pc.principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6').willSendEq(1000).ustx(), + ], +}); +``` + +In the example code snippet below, we are declaring that the user should expect to send exactly 1000 uSTX during the execution of this contract call transaction. If this condition is not met, the transaction will fail. + +### Using the Pc helper + +The `Pc` helper provides a fluent API for creating post-conditions with better type safety and readability. + +```ts +import { Pc } from '@stacks/transactions'; + +// STX transfer post-condition +const stxCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendGte(1000) + .ustx(); + +// Fungible token post-condition +const ftCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendEq(50) + .ft('SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.my-token', 'my-token'); + +// NFT post-condition +const nftCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendAsset() + .nft('SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.my-nft::my-asset', Cl.uint(1)); +``` + +### Manual creation + +Create post-conditions manually using type definitions when building conditions dynamically. + +```ts +import { + StxPostCondition, + FungiblePostCondition, + NonFungiblePostCondition +} from '@stacks/transactions'; + +// STX post-condition +const stxPostCondition: StxPostCondition = { + type: 'stx-postcondition', + address: 'SP2JXKMSH007NPYAQHKJPQMAQYAD90NQGTVJVQ02B', + condition: 'gte', // 'eq' | 'gt' | 'gte' | 'lt' | 'lte' + amount: '100', +}; +``` + +Available condition types: + +* `eq`: Exactly equal to amount +* `gt`: Greater than amount +* `gte`: Greater than or equal to amount +* `lt`: Less than amount +* `lte`: Less than or equal to amount + +#### Fungible tokens + +```ts +const ftPostCondition: FungiblePostCondition = { + type: 'ft-postcondition', + address: 'SP2JXKMSH007NPYAQHKJPQMAQYAD90NQGTVJVQ02B', + condition: 'eq', + amount: '100', + asset: 'SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.my-ft-token::my-token', +}; +``` + +#### Non-fungible tokens + +```ts +const nftPostCondition: NonFungiblePostCondition = { + type: 'nft-postcondition', + address: 'SP2JXKMSH007NPYAQHKJPQMAQYAD90NQGTVJVQ02B', + condition: 'sent', // 'sent' | 'not-sent' + asset: 'SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.my-nft::my-asset', + assetId: Cl.uint(602), +}; +``` + +### Post-condition modes + +Control how unspecified, unexpected, or unforeseen asset transfers are handled with post-condition mode. + +#### **Deny Mode** + +Deny mode is the default for post-conditions. Deny is a more-strict setting for post-conditions, and it says that any other asset transfers that do not meet the criteria of the post-condition are denied. In deny mode, any transaction that does not meet the post-condition criteria will fail. + +```ts +import { PostConditionMode } from '@stacks/transactions'; + +const tx = await makeContractCall({ + // ... + postConditionMode: PostConditionMode.Deny, + postConditions: [ + // your conditions + ], +}); +``` + +This setting is useful when you want to limit any transfer events to a specific set of criteria. This setting is ultimately what makes post-conditions powerful, hence the reason why this is defaulted as `deny` if you don’t or forget to pass in a `postConditionMode` option. + +{% hint style="warning" %} +Post-condition mode\ +Always use `Deny` mode unless you have a specific reason to allow additional transfers. This provides maximum security for users. +{% endhint %} + +#### Allow mode + +Allow mode is a less-strict setting, in which it “allows” any transaction to execute as long as it meets the criteria of the specified post-conditions. In other words, this `allow` mode enables additional transactions to occur as long as the post-condition is met in that process. This setting is useful when you want to allow other unknown or dynamic transfers to happen. But usually you wouldn’t want to have this happen as this can open up unintended consequences for the user. + +### How post-conditions appear to the user + +Since post-conditions are declared on your frontend code, they also need to be visually displayed to users. Stacks-supported wallets handle that by displaying post-conditions on the transaction confirmation modals that popup when a user needs to confirm/approve a transaction. + +

        How post-conditions appear in wallets under different post-condition modes

        + +### Things to remember + +While powerful, post-conditions have some limitations you should keep in mind. Post-conditions only track who _sends_ an asset, and how much. They do not monitor who owns any set of assets when the transaction finishes, nor do they monitor the sequence of owners an asset might have during transaction execution. + +Alongside those limitations, it should be obvious, but it’s worth explicitly stating that post-conditions are not a catch-all. Just because you implement post-conditions doesn’t mean your contract or next transaction are guaranteed to be safe. Bugs can still occur, and you still need to build with security in mind. Debugging and extensive tests are still your best friend. + +*** + +### Additional Resources + +* \[[Hiro Blog](https://www.hiro.so/blog/a-developers-guide-to-post-conditions)] A Developer’s Guide to Post-Conditions +* \[[dev.to](https://dev.to/stacks/understanding-stacks-post-conditions-e65)] Understanding Stacks Post Conditions +* \[[Hiro YT](https://youtu.be/xXgQB8NfdEY?si=aEY_wrLybfWPMJTt)] ELI5: Post-Condtions on Stacks +* \[[Hiro YT](https://youtu.be/wagcE_IXfME?si=kDqxzPAQ-XsA478l)] Understanding Post-Conditions in a Stacks Blockchain Transaction +* \[[StacksGov](https://github.com/stacksgov/sips/blob/main/sips/sip-005/sip-005-blocks-and-transactions.md#transaction-post-conditions)] Post-conditions section in SIP-005 diff --git a/docs/build/rendezvous/overview.md b/docs/build/rendezvous/overview.md new file mode 100644 index 0000000000..5e344bbc61 --- /dev/null +++ b/docs/build/rendezvous/overview.md @@ -0,0 +1,159 @@ +--- +description: >- + Rendezvous is a fuzzer for Clarity smart contracts that finds vulnerabilities + through stateful, randomized testing, all in Clarity. +--- + +# Overview + +{% hint style="danger" %} +Smart contracts on Stacks are immutable. Bugs are forever. Test early. Test often. Fuzzing finds edge cases that unit tests often miss. +{% endhint %} + +## What is Fuzz Testing? + +Fuzzing hits your code with random inputs. It helps uncover unexpected behavior and subtle bugs. Unlike unit tests, it explores paths you didn't think of. + +## What is Rendezvous? + +Rendezvous (`rv`) is a Clarity fuzzer. It supports: + +### Property-Based Testing + +You extract properties about your smart contract using Clarity. Rendezvous checks them multiple times with random inputs, in a stateful manner (the smart contract's state is not refreshed during the run). + +**What is a property?** + +A property is a universal truth about your smart contract's state, functions, etc. + +**How to extract a property?** + +Say that your smart contract has a function that reverses a list of `uint`s. In this case, one property can be that "reversing a list twice returns the original list". The property will look like this: + +```clarity +(define-public (test-reverse-list (seq (list 127 uint))) + (begin + (asserts! + (is-eq seq + (reverse-uint + (reverse-uint seq) + ) + ) + (err u999) + ) + (ok true) + ) +) +``` + +**Making your property valid for Rendezvous** + +> For a property to be cosidered valid by Rendezvous, it has to comply with the following rules: +> +> * Function name starts with `test-` +> * Function is declared as `public` +> * Test passes when it returns `(ok true)` +> * Test would be discarded if it returned `(ok false)` +> * Test fails if it returns an error or throws an exception + +*** + +### Invariant Testing + +You define read-only conditions in Clarity that must always hold true. Rendezvous attempts to create state transitions in your smart contract and continuously checks the conditions you defined to hold. + +**What is an invariant?** + +An invariant is a general truth regarding your smart contract's internal state. It will not be able to mutate the state, its role being solely to check the integrity of the state. + +**How to extract an invariant?** + +Say that you have a counter contract, having functions to `increment` and `decrement`. In this case, you could use the Rendezvous [`context`](https://stacks-network.github.io/rendezvous/chapter_6.html?#the-rendezvous-context) to extract an invariant regarding your smart contract's internal state: + +```clarity +(define-read-only (invariant-counter-gt-zero) + (let + ( + (increment-num-calls + (default-to u0 (get called (map-get? context "increment"))) + ) + (decrement-num-calls + (default-to u0 (get called (map-get? context "decrement"))) + ) + ) + (if + (<= increment-num-calls decrement-num-calls) + true + (> (var-get counter) u0) + ) + ) +) +``` + +**Making your invariant valid for Rendezvous** + +> For an invariant to be cosidered valid by Rendezvous, it has to complain to the following ruleset: +> +> * Function name starts with invariant- +> * Function is declared as read-only (not public) +> * Function returns a boolean value (true if the invariant holds, false if violated) +> * The test can use the special context map to access execution history + +## Why Test in Clarity? + +{% stepper %} +{% step %} +#### Same constraints as production + +Tests operate under the exact same constraints as production code. +{% endstep %} + +{% step %} +#### Better understanding of Clarity + +Writing tests in Clarity improves your familiarity with the language and its semantics. +{% endstep %} + +{% step %} +#### No need to expose internals + +You don't have to expose internal functions as public solely for testing. +{% endstep %} + +{% step %} +#### Fewer tools to manage + +Running tests in Clarity reduces the number of external tools and integrations you need to maintain. +{% endstep %} +{% endstepper %} + +## Getting Started + +Put tests next to contracts. Rendezvous will find them. + +``` +my-project/ +├── Clarinet.toml +├── contracts/ +│ ├── my-contract.clar # Contract +│ ├── my-contract.tests.clar # Tests +└── settings/ + └── Devnet.toml +``` + +### Installation + +To install Rendezvous as a dependency in your project, use `npm`: + +``` +npm install @stacks/rendezvous +``` + +This will add Rendezvous to your project's `node_modules` and update your `package.json`. + +*** + +### Additional Resources + +* \[[Github](https://stacks-network.github.io/rendezvous/)] Rendezvous repo +* \[[Youtube @jofawole](https://youtu.be/deWQxCEy9_M?si=bBpUoKGpJvFLFu_9)] How to Use Rendezvous to Fuzz Clarity Contracts diff --git a/docs/build/rendezvous/quickstart.md b/docs/build/rendezvous/quickstart.md new file mode 100644 index 0000000000..28f0b2aa51 --- /dev/null +++ b/docs/build/rendezvous/quickstart.md @@ -0,0 +1,694 @@ +--- +description: >- + Learn how to test your Clarity smart contracts thoroughly using Rendezvous + property-based testing. +--- + +# Quickstart Tutorial + +This tutorial walks you through testing a DeFi lending contract with Rendezvous. You’ll see how Rendezvous uncovers subtle vulnerabilities that traditional unit tests might miss, and learn how to design property-based tests that help expose real bugs. + +## What You'll Learn + +You will test a simplified DeFi lending contract that allows users to deposit STX and borrow against those deposits. This contract hides a subtle bug that passes all example-based tests, but fails when running Rendezvous property-based testing. + +You’ll learn to: + +- Write property-based tests directly in Clarity +- Catch real vulnerabilities using randomized, stateful test runs +- Replay and fix failing test sequences deterministically + +> Note: This example is adapted from the [stx-defi](https://github.com/hirosystems/clarity-examples/blob/ccd9ecf0bf136d7f28ef116706ed2936f6d8781a/examples/stx-defi/contracts/stx-defi.clar) contract in the [hirosystems/clarity-examples](https://github.com/hirosystems/clarity-examples) repository. + +## Prerequisites + +Before you begin, make sure you have: + +- Node.js (version >= 20) +- Clarinet installed ([installation guide](https://github.com/stx-labs/clarinet)) + +## Step 1: Create a New Clarinet Project + +Open your terminal and create a new Clarinet project: + +```bash +clarinet new rendezvous-tutorial +cd rendezvous-tutorial +``` + +This creates a new directory with the basic Clarinet structure: + +``` +rendezvous-tutorial/ +├── Clarinet.toml +├── contracts/ +├── settings/ +│ └── Devnet.toml +└── tests/ +``` + +## Step 2: Add Rendezvous + +Add Rendezvous to your project: + +```bash +npm install @stacks/rendezvous +``` + +Verify the installation: + +```bash +npx rv --help +``` + +## Step 3: Add the Lending Contract + +Add a new contract to the Clarinet project: + +```bash +clarinet contract new stx-defi +``` + +Open `contracts/stx-defi.clar` and add this Clarity code: + +```clarity +;; stx-defi.clar +;; A simplified DeFi lending protocol. + +(define-map deposits + { owner: principal } + { amount: uint } +) + +(define-map loans + principal + { amount: uint } +) + +(define-constant err-overborrow (err u300)) + +(define-public (deposit (amount uint)) + (let + ( + (current-balance + (default-to u0 (get amount (map-get? deposits { owner: tx-sender }))) + ) + ) + (try! (stx-transfer? amount tx-sender (as-contract tx-sender))) + (map-set deposits + { owner: tx-sender } + { amount: (+ current-balance amount) } + ) + (ok true) + ) +) + +(define-public (borrow (amount uint)) + (let + ( + (user-deposit + (default-to u0 (get amount (map-get? deposits { owner: tx-sender }))) + ) + (allowed-borrow (/ user-deposit u2)) + (current-loan + (default-to u0 (get amount (map-get? loans tx-sender))) + ) + (new-loan (+ amount)) + ) + (asserts! (<= new-loan allowed-borrow) err-overborrow) + (let + ((recipient tx-sender)) + (try! (as-contract (stx-transfer? amount tx-sender recipient))) + ) + (map-set loans tx-sender { amount: new-loan }) + (ok true) + ) +) + +(define-read-only (get-loan-amount) + (ok (default-to u0 (get amount (map-get? loans tx-sender)))) +) +``` + +**What this contract does:** + +- Users can `deposit` STX into the protocol +- Users can `borrow` up to 50% of their deposit value +- The contract tracks deposits and loans for each user + +## Step 4: Write Some Unit Tests + +Let's first write some example-based unit tests. + +Open `tests/stx-defi.test.ts` and add these example-based unit tests: + +```typescript +describe("stx-defi unit tests", () => { + it("can deposit", () => { + const amountToDeposit = 1000; + + const { result } = simnet.callPublicFn( + "stx-defi", + "deposit", + [Cl.uint(amountToDeposit)], + address1 + ); + + expect(result).toBeOk(Cl.bool(true)); + }); + + it("can borrow half of deposit", () => { + const amountToDeposit = 1000; + const amountToBorrow = 500; + + const { result: depositResult } = simnet.callPublicFn( + "stx-defi", + "deposit", + [Cl.uint(amountToDeposit)], + address1 + ); + expect(depositResult).toBeOk(Cl.bool(true)); + + const { result } = simnet.callPublicFn( + "stx-defi", + "borrow", + [Cl.uint(amountToBorrow)], + address1 + ); + + expect(result).toBeOk(Cl.bool(true)); + }); + + it("loan amount is correct", () => { + const amountToDeposit = 1000; + const amountToBorrow = 500; + + simnet.callPublicFn( + "stx-defi", + "deposit", + [Cl.uint(amountToDeposit)], + address1 + ); + + simnet.callPublicFn( + "stx-defi", + "borrow", + [Cl.uint(amountToBorrow)], + address1 + ); + + const { result } = simnet.callReadOnlyFn( + "stx-defi", + "get-loan-amount", + [], + address1 + ); + + expect(result).toBeOk(Cl.uint(amountToBorrow)); + }); + + it("cannot borrow more than half of deposit", () => { + const amountToDeposit = 1000; + const amountToBorrow = 501; + + simnet.callPublicFn( + "stx-defi", + "deposit", + [Cl.uint(amountToDeposit)], + address1 + ); + + const { result } = simnet.callPublicFn( + "stx-defi", + "borrow", + [Cl.uint(amountToBorrow)], + address1 + ); + + // err-overborrow + expect(result).toBeErr(Cl.uint(300)); + }); +}); +``` + +Install dependencies and run the tests: + +```bash +npm install +npm test +``` + +**Looking good!** ✅ (or so it seems...) + +The main functions and state of the contract are now covered by tests. Line coverage is probably high as well. Looks great, right? But here's the thing: example-based tests only verify the examples you thought of. Let's see if the contract holds up under **Rendezvous property-based testing**. + +## Step 5: Add Rendezvous Property-Based Tests + +Rendezvous lets you test a broader range of inputs, not just specific examples. Let's see how to write your first property-based test and why it matters. + +### Create the Test File + +Create the Rendezvous test file: + +```bash +touch contracts/stx-defi.tests.clar +``` + +### Add an Ice-Breaker Test + +Before writing any meaningful properties, it's a good idea to check that Rendezvous can run. Add a simple "always-true" test to verify your setup. +Open `contracts/stx-defi.tests.clar` and add an always-true test: + +```clarity +(define-public (test-always-true) + (ok true) +) +``` + +Check if Rendezvous can execute the test: + +```bash +npx rv . stx-defi test +``` + +Expected output: + +``` +$ npx rv . stx-defi test +Using manifest path: Clarinet.toml +Target contract: stx-defi + +Starting property testing type for the stx-defi contract... + +₿ 1 Ӿ 3 deployer [PASS] stx-defi test-always-true (ok true) +₿ 1 Ӿ 4 wallet_5 [PASS] stx-defi test-always-true (ok true) +₿ 11 Ӿ 15 wallet_1 [PASS] stx-defi test-always-true (ok true) +₿ 690 Ӿ 695 wallet_1 [PASS] stx-defi test-always-true (ok true) +... +₿ 12348 Ӿ 12447 wallet_3 [PASS] stx-defi test-always-true (ok true) +₿ 12348 Ӿ 12448 wallet_3 [PASS] stx-defi test-always-true (ok true) +₿ 12357 Ӿ 12458 wallet_5 [PASS] stx-defi test-always-true (ok true) + +OK, properties passed after 100 runs. + + +EXECUTION STATISTICS + +│ PROPERTY TEST CALLS +│ +├─ + PASSED +│ └─ test-always-true: x100 +│ +├─ ! DISCARDED +│ └─ test-always-true: x0 +│ +└─ - FAILED + └─ test-always-true: x0 + +LEGEND: + + PASSED properties verified for given inputs + DISCARDED skipped due to invalid preconditions + FAILED property violations or unexpected behavior +``` + +If you see similar output, your setup works. You're ready to write a **real property-based test**. + +### Define a Borrowing Property + +You want to test that **borrowing always updates the loan amount correctly**: + +```clarity +;; stx-defi.tests.clar + +;; Property: Borrowing should always update the loan amount correctly. +;; The new loan amount should equal the old loan amount plus the borrowed +;; amount. +(define-public (test-borrow (amount uint)) + (let ( + ;; Record the loan amount before performing any action that would end up + ;; changing the internal state of the smart contract. Query the loans map + ;; for the selected tx-sender and store the result in the initial-loan + ;; local variable. + (initial-loan (default-to u0 (get amount (map-get? loans tx-sender)))) + ) + ;; Since the initial-loan is recorded before the borrow call, you can now + ;; call the borrow function to allow checking the effects after the call. + (try! (borrow amount)) + ;; Verify the property: updated loan = initial loan + borrowed amount + (asserts! + (is-eq (default-to u0 (get amount (map-get? loans tx-sender))) + (+ initial-loan amount) + ) + (err u999) ;; any error code to identify the test failure. + ) + (ok true) + ) +) +``` + +> At this stage, **the test will likely fail**. This is an important learning moment: Rendezvous runs your tests in a **stateful, randomized environment** that simulates real contract interactions. + +### How Rendezvous Executes Property Tests + +Rendezvous: + +1. Injects all property-based tests directly into the deployed contract. +2. Detects all public `test-*` functions automatically. +3. Generates a random sequence to call each test. +4. Produces random argument values for each function parameter. +5. Randomly selects senders from settings/Devnet.toml. +6. Randomly advances Bitcoin and Stacks block heights during testing. +7. Accumulates state across test calls instead of resetting each time. +8. Discards test cases where preconditions fail, returning (ok false). + +This design allows you to test your contract in **realistic, varied scenarios** that a simple/example-based unit test could never reach. + +### Why the First Test Fails + +The test likely failed because the `borrow` call failed—the contract wasn't in a suitable state. Rendezvous allows you to discard test cases when preconditions aren't met (wrong state, invalid arguments, caller, height, etc.). In our case, `borrow` will fail for one of these reasons: + +- no deposits were made +- the generated amount argument is non-positive (u0) +- the generated amount argument is more than the allowed borrow value + +To fix this, you need to **simulate deposits** and add **discard logic**. + +Let's address them one by one. + +### Handle Preconditions + +**First, you need deposits.** You can create a helper function that Rendezvous will pick up during property-based testing runs. This helper will allow deposits to be created so other tests can check properties that require deposits: + +```clarity +;; This is a helper function that will eventually be picked up during +;; property-based-testing runs. It allows creating deposits in the smart +;; contract so other tests can check properties requiring a deposit. +(define-public (test-deposit-helper (amount uint)) + (let ( + ;; Call the deposit function and ignore the result. + (deposit-result (deposit amount)) + ) + (ok true) + ) +) +``` + +**Next, add discard logic to the borrow test.** A test is discarded when it returns `(ok false)`. Wrap the core test logic in a conditional that checks for invalid preconditions (the three cases listed above) and returns `(ok false)` to discard those cases: + +```clarity +;; Property: Borrowing should always update the loan amount correctly. +;; The new loan amount should equal the old loan amount plus the borrowed +;; amount. +(define-public (test-borrow (amount uint)) + (if (or + ;; If amount is 0, the STX transfer performed in the borrow operation + ;; would fail, resulting in a false negative. + (is-eq amount u0) + ;; If the amount to borrow would exceed the allowed limit defined in the + ;; borrow function, the borrow operation would fail, resulting in a false + ;; negative. + (> (+ (default-to u0 (get amount (map-get? loans tx-sender))) amount) + (/ (default-to u0 (get amount (map-get? deposits { owner: tx-sender }))) + u2 + )) + ) + ;; Discard the test if preconditions aren't met. + (ok false) + ;; Run the test. + (let ((initial-loan (default-to u0 (get amount (map-get? loans tx-sender))))) + (unwrap! (borrow amount) (err "Borrow call failed")) + (let ((updated-loan (default-to u0 (get amount (map-get? loans tx-sender))))) + ;; Verify the property: new loan = old loan + borrowed amount + (asserts! (is-eq updated-loan (+ initial-loan amount)) + (err "Loan amount not updated correctly") + ) + (ok true) + ) + ) + ) +) +``` + +The test discards invalid cases: when `amount` is `u0`, or when the new total loan would exceed half the deposit (which also covers cases with no deposits). + +> Now the test only runs when valid preconditions are met. + +### Run Rendezvous and Catch the Bug + +Start a new property-based testing run: + +```bash +npx rv . stx-defi test +``` + +Rendezvous will probably catch the bug in the very first run, showing output like this: + +``` +$ npx rv . stx-defi test +Using manifest path: Clarinet.toml +Target contract: stx-defi + +Starting property testing type for the stx-defi contract... + +₿ 1 Ӿ 3 wallet_7 [PASS] stx-defi test-deposit-helper 2 (ok true) +₿ 1001 Ӿ 1004 wallet_7 [WARN] stx-defi test-borrow 2015589496 (ok false) +₿ 1001 Ӿ 1005 wallet_8 [PASS] stx-defi test-deposit-helper 2147483636 (ok true) +₿ 1898 Ӿ 1903 wallet_6 [WARN] stx-defi test-borrow 1984339073 (ok false) +₿ 1898 Ӿ 1904 deployer [PASS] stx-defi test-deposit-helper 195930186 (ok true) +₿ 1898 Ӿ 1905 wallet_2 [PASS] stx-defi test-deposit-helper 13 (ok true) +... +₿ 3464 Ӿ 3485 deployer [PASS] stx-defi test-borrow 28 (ok true) +₿ 3468 Ӿ 3490 wallet_1 [WARN] stx-defi test-borrow 25 (ok false) +₿ 3468 Ӿ 3491 wallet_8 [FAIL] stx-defi test-borrow 11 (err "Loan amount not updated correctly") +₿ 3468 Ӿ 3492 wallet_1 [PASS] stx-defi test-deposit-helper 1653600941 (ok true) +₿ 4058 Ӿ 4083 wallet_8 [PASS] stx-defi test-deposit-helper 1653600941 (ok true) +₿ 4058 Ӿ 4084 wallet_8 [WARN] stx-defi test-borrow 1653600941 (ok false) +₿ 4058 Ӿ 4085 wallet_8 [WARN] stx-defi test-borrow 0 (ok false) +₿ 4058 Ӿ 4086 wallet_8 [FAIL] stx-defi test-borrow 6 (err "Loan amount not updated correctly") +₿ 4058 Ӿ 4087 wallet_8 [FAIL] stx-defi test-borrow 3 (err "Loan amount not updated correctly") +₿ 4058 Ӿ 4088 wallet_8 [FAIL] stx-defi test-borrow 2 (err "Loan amount not updated correctly") +₿ 4058 Ӿ 4089 wallet_8 [FAIL] stx-defi test-borrow 1 (err "Loan amount not updated correctly") +₿ 4058 Ӿ 4090 wallet_8 [WARN] stx-defi test-borrow 0 (ok false) +₿ 4058 Ӿ 4091 wallet_8 [FAIL] stx-defi test-borrow 1 (err "Loan amount not updated correctly") +₿ 4058 Ӿ 4092 wallet_8 [WARN] stx-defi test-borrow 0 (ok false) +₿ 4058 Ӿ 4093 wallet_8 [FAIL] stx-defi test-borrow 1 (err "Loan amount not updated correctly") + +Error: Property failed after 22 tests. +Seed : 1880056597 + +Counterexample: +- Test Contract : stx-defi +- Test Function : test-borrow (public) +- Arguments : [1] +- Caller : wallet_8 +- Outputs : {"type":{"response":{"ok":"bool","error":{"string-ascii":{"length":33}}}}} + +What happened? Rendezvous went on a rampage and found a weak spot: + +The test function "test-borrow" returned: + +(err "Loan amount not updated correctly") + +EXECUTION STATISTICS + +│ PROPERTY TEST CALLS +│ +├─ + PASSED +│ ├─ test-borrow: x2 +│ └─ test-deposit-helper: x13 +│ +├─ ! DISCARDED +│ ├─ test-borrow: x12 +│ └─ test-deposit-helper: x0 +│ +└─ - FAILED + ├─ test-borrow: x7 + └─ test-deposit-helper: x0 + +LEGEND: + + PASSED properties verified for given inputs + DISCARDED skipped due to invalid preconditions + FAILED property violations or unexpected behavior + +! FAILED tests indicate that your function properties don't hold for all inputs. Review the counterexamples above for debugging. +``` + +The output shows a failure: `(err "Loan amount not updated correctly")`. The contract isn't tracking loan amounts correctly. + +> **Note:** The output includes a seed (`1880056597`) you can use to reproduce this exact sequence. + +You can also stop at the first failure: + +```bash +npx rv . stx-defi test --bail +``` + +## Step 6: Identify and Fix the Borrow Bug + +After taking a closer look at the lending contract, the bug is in this line of the `borrow` function: + +```clarity +(new-loan (+ amount)) +``` + +Change the line to correctly accumulate loans: + +```clarity +(new-loan (+ current-loan amount)) +``` + +### Re-run Rendezvous with the Same Seed + +Re-run with the same seed, to find out if you completely fixed the bug for that random sequence of events: + +```clarity +npx rv . stx-defi test --seed=1880056597 +``` + +Output: + +``` +$ npx rv . stx-defi test --seed=1880056597 +Using manifest path: Clarinet.toml +Target contract: stx-defi +Using seed: 1880056597 + +Starting property testing type for the stx-defi contract... + +₿ 1 Ӿ 3 wallet_7 [PASS] stx-defi test-deposit-helper 2 (ok true) +₿ 1001 Ӿ 1004 wallet_7 [WARN] stx-defi test-borrow 2015589496 (ok false) +₿ 1001 Ӿ 1005 wallet_8 [PASS] stx-defi test-deposit-helper 2147483636 (ok true) +₿ 1898 Ӿ 1903 wallet_6 [WARN] stx-defi test-borrow 1984339073 (ok false) +₿ 1898 Ӿ 1904 deployer [PASS] stx-defi test-deposit-helper 195930186 (ok true) +₿ 1898 Ӿ 1905 wallet_2 [PASS] stx-defi test-deposit-helper 13 (ok true) +... +₿ 17291 Ӿ 17388 wallet_4 [PASS] stx-defi test-borrow 708340522 (ok true) +₿ 17291 Ӿ 17389 wallet_4 [PASS] stx-defi test-deposit-helper 589199221 (ok true) +₿ 17565 Ӿ 17664 wallet_2 [PASS] stx-defi test-deposit-helper 2147483627 (ok true) +₿ 18559 Ӿ 18659 wallet_8 [PASS] stx-defi test-borrow 1622181282 (ok true) +₿ 18559 Ӿ 18660 wallet_3 [PASS] stx-defi test-deposit-helper 2147483630 (ok true) + +OK, properties passed after 100 runs. +``` + +> The bug is fixed! The contract now correctly tracks cumulative loans. + +### Run Multiple Random Sequences + +Test additional random sequences (each run generates a new random sequence): + +```bash +npx rv . stx-defi test +``` + +Run more tests to increase confidence (default is 100 runs): + +```bash +npx rv . stx-defi test --runs=1000 +``` + +**Rendezvous caught the bug and you successfully fixed it!** 🎯 + +## Step 7: Understand the Bug + +**What was the bug?** + +Rendezvous discovered that when a user borrows multiple times, only the most recent borrow amount is recorded. + +The bug means the contract doesn't track cumulative borrows correctly. When a user borrows multiple times, only the most recent borrow amount is recorded, not the total. The existing loan amount (`current-loan`) is completely ignored! + +**Why did example-based unit tests miss this?** + +The unit tests passed because they only tested single borrow scenarios. Look back at the unit test: + +```typescript +it("loan amount is correct after single borrow", () => { + // Only ONE borrow call - bug not triggered! + simnet.callPublicFn( + "stx-defi", + "borrow", + [Cl.uint(amountToBorrow)], + address1 + ); + // ... +}); +``` + +When there's only one borrow, `(+ amount)` and `(+ current-loan amount)` produce the same result because the initial loan is `u0`. + +**Rendezvous caught the bug by:** + +1. Randomly generating test sequences +2. Calling `borrow` multiple times with different amounts +3. Verifying the property holds for ALL sequences + +This is the power of using Rendezvous! + +## What You Learned + +You've successfully: + +✅ Created a simple DeFi lending contract + +✅ Wrote traditional unit tests that passed but missed a critical bug + +✅ Wrote your first Rendezvous property-based test + +✅ Discovered how Rendezvous catches bugs through random stateful testing + +✅ Fixed the bug and verified the fix + +✅ Understood the difference between stateless example-based and stateful property-based testing + +## The Key Insight + +**Example-based tests check specific examples. Property-based tests check a much broader range of inputs.** + +Example-based tests ask: + +- "Does this work for input A?" +- "Does this work for input B?" + +Property-based tests ask: + +- "Does this ALWAYS work?" +- "Can I find ANY input that breaks this?" + +Rendezvous explores your contract's state space automatically, finding edge cases you might never think to test manually. + +## Real-World Impact + +This bug in a production DeFi protocol would allow users to: + +1. Deposit 1000 STX +2. Borrow 500 STX (maximum allowed) +3. Borrow another 500 STX (should fail, but succeeds due to bug) +4. Total borrowed: 1000 STX with only 500 STX recorded +5. User only needs to repay 500 STX despite borrowing 1000 STX + +This would drain the protocol's funds — a critical vulnerability caught by Rendezvous in seconds. + +## Example Implementation + +You can see a complete step-by-step implementation of this tutorial with commit-by-commit progress in the [rendezvous-tutorial repository](https://github.com/BowTiedRadone/rendezvous-tutorial) ([view commits](https://github.com/BowTiedRadone/rendezvous-tutorial/commits/master/)). + +## Next Steps + +Now that you understand the power of Rendezvous, explore: + +- **More examples**: Study other smart contracts in the [Examples Chapter](https://stacks-network.github.io/rendezvous/chapter_8.html) of the [Rendezvous Docs](https://stacks-network.github.io/rendezvous/) +- **Your own contracts**: Apply Rendezvous to your projects and find bugs before they reach production + +--- + +## Get Involved + +**Found this tutorial useful?** Star the [Rendezvous repository on GitHub](https://github.com/stacks-network/rendezvous) to show your support! + +Have questions, found a bug, or want to contribute? We'd love to hear from you: + +- **Open an issue** on [GitHub](https://github.com/stacks-network/rendezvous/issues) +- **Reach out** with questions or feedback +- **Share your findings** — contribute examples of bugs you've caught to show others how powerful advanced testing techniques can be diff --git a/docs/build/stacks-connect/broadcast-transactions.md b/docs/build/stacks-connect/broadcast-transactions.md new file mode 100644 index 0000000000..bd62c7a742 --- /dev/null +++ b/docs/build/stacks-connect/broadcast-transactions.md @@ -0,0 +1,168 @@ +# Broadcast Transactions + +The process of broadcasting transactions is fundamental for interacting with blockchains, whether you're transferring tokens, deploying contracts, or executing contract functions. + +In this guide you will: + +* Install the required packages +* Connect to a user's wallet +* Sign and broadcast different transaction types +* Handle transaction results + +## Setup and installation + +Install the required packages to start building and broadcasting transactions. + +{% tabs %} +{% tab title="npm" %} +```bash +npm install @stacks/connect @stacks/transactions +``` +{% endtab %} + +{% tab title="yarn" %} +```bash +yarn add @stacks/connect @stacks/transactions +``` +{% endtab %} + +{% tab title="pnpm" %} +```bash +pnpm add @stacks/connect @stacks/transactions +``` +{% endtab %} +{% endtabs %} + +## Connect to a user's wallet + +Before signing transactions, users need to connect their wallet to your application. Use the `connect` function to initiate a wallet connection: + +```ts +import { connect, isConnected } from '@stacks/connect'; + +async function connectWallet() { + if (!isConnected()) { + const response = await connect(); + console.log('Connected with addresses:', response); + } +} +``` + +## Sign and broadcast transactions + +There are three common transaction flows you can build: + +{% stepper %} +{% step %} +#### STX transfer + +Use `stx_transferStx` to send tokens: + +```ts +import { request } from '@stacks/connect'; + +async function transferStx() { + const response = await request('stx_transferStx', { + recipient: 'ST2EB9WEQNR9P0K28D2DC352TM75YG3K0GT7V13CV', + amount: '100', + memo: 'Reimbursement', + }); + + console.log('Transaction ID:', response.txId); +} +``` +{% endstep %} + +{% step %} +#### Contract deployment + +Deploy a contract with `stx_deployContract`: + +```ts +import { request } from '@stacks/connect'; + +async function deployContract() { + const codeBody = '(define-public (say-hi) (ok "hello world"))'; + + const response = await request('stx_deployContract', { + name: 'my-contract', + code: codeBody, + clarityVersion: 3, + }); + + console.log('Transaction ID:', response.txId); +} +``` + +{% hint style="info" %} +Contracts deploy to the Stacks address of the connected wallet. +{% endhint %} +{% endstep %} + +{% step %} +#### Contract execution + +Call contract functions with `stx_callContract`: + +```clarity +(define-public (say-hi) + (print "hi") + (ok u0) +) +``` + +```ts +import { request } from '@stacks/connect'; + +async function callContract() { + const response = await request('stx_callContract', { + contractAddress: 'ST22T6ZS7HVWEMZHHFK77H4GTNDTWNPQAX8WZAKHJ', + contractName: 'my-contract', + functionName: 'say-hi', + functionArgs: [], + }); + + console.log('Transaction ID:', response.txId); +} +``` + +When passing arguments, construct Clarity values via `Cl`: + +```ts +import { Cl } from '@stacks/transactions'; + +const functionArgs = [ + Cl.uint(123), + Cl.stringAscii('hello'), + Cl.standardPrincipalCV('ST1X..'), +]; +``` +{% endstep %} +{% endstepper %} + +## Handle transaction results + +When a transaction is signed and broadcast, the `request` method returns a response object containing information about the transaction: + +```ts +interface TransactionResponse { + txId: string; // The transaction ID + txRaw: string; // The raw transaction hex +} +``` + +You can use the transaction ID to create a link to view the transaction in the explorer: + +```ts +import { request } from '@stacks/connect'; + +async function handleTransaction() { + const response = await request('stx_transferStx', { + recipient: 'ST2EB9WEQNR9P0K28D2DC352TM75YG3K0GT7V13CV', + amount: '100', + }); + + const explorerUrl = `https://explorer.stacks.co/txid/${response.txId}`; + console.log('View transaction in explorer:', explorerUrl); +} +``` diff --git a/docs/build/stacks-connect/connect-wallet.md b/docs/build/stacks-connect/connect-wallet.md new file mode 100644 index 0000000000..d89bdee0f2 --- /dev/null +++ b/docs/build/stacks-connect/connect-wallet.md @@ -0,0 +1,125 @@ +# Connect Wallet + +Learn how to integrate wallet connections into your Stacks application. Connecting a wallet authenticates users and enables blockchain interactions like transfers and contract calls. + +

        source: Hiro blog

        + +{% hint style="success" %} +For the latest releases and versions of `@stacks/connect`, check out its npm page [here](https://www.npmjs.com/package/@stacks/connect). +{% endhint %} + +## What you'll learn + +* Install the `@stacks/connect` package +* Connect to a wallet and authenticate users +* Manage authentication state +* Access user account data + +{% hint style="info" %} +Prerequisites: + +* Node.js installed on your machine +* A web application setup (React, Vue, or vanilla JS) +* Basic understanding of async/await +{% endhint %} + +## Quickstart + +{% stepper %} +{% step %} +#### Install package + +Add Stacks Connect to your project: + +{% code title="Install" %} +```bash +npm install @stacks/connect +``` +{% endcode %} +{% endstep %} + +{% step %} +#### Connect and authenticate + +Use `connect` to initiate a wallet session and persist user data: + +{% code title="connect.ts" %} +```ts +import { connect, isConnected } from '@stacks/connect'; + +async function connectWallet() { + if (isConnected()) { + console.log('Already authenticated'); + return; + } + + const response = await connect(); + console.log('Connected:', response.addresses); +} +``` +{% endcode %} + +Manage authentication state in your app: + +{% code title="auth.ts" %} +```ts +import { disconnect, isConnected } from '@stacks/connect'; + +const authenticated = isConnected(); + +function logout() { + disconnect(); + console.log('User disconnected'); +} +``` +{% endcode %} +{% endstep %} + +{% step %} +#### Access user data + +Read persisted addresses and request full account details: + +{% code title="user-data.ts" %} +```ts +import { getLocalStorage, request } from '@stacks/connect'; + +const userData = getLocalStorage(); +if (userData?.addresses) { + const stxAddress = userData.addresses.stx[0].address; + const btcAddress = userData.addresses.btc[0].address; + console.log('STX:', stxAddress); + console.log('BTC:', btcAddress); +} + +const accounts = await request('stx_getAccounts'); +const account = accounts.addresses[0]; +console.log('Address:', account.address); +console.log('Public key:', account.publicKey); +console.log('Gaia URL:', account.gaiaHubUrl); +``` +{% endcode %} +{% endstep %} + +{% step %} +#### Make your first transaction + +Request the wallet to broadcast a transfer: + +{% code title="send-transaction.ts" %} +```ts +import { request } from '@stacks/connect'; + +async function sendTransaction() { + const response = await request('stx_transferStx', { + amount: '1000000', + recipient: 'SP2MF04VAGYHGAZWGTEDW5VYCPDWWSY08Z1QFNDSN', + memo: 'First transfer', + }); + + console.log('Transaction ID:', response.txid); +} +``` +{% endcode %} +{% endstep %} +{% endstepper %} diff --git a/docs/build/stacks-connect/message-signing.md b/docs/build/stacks-connect/message-signing.md new file mode 100644 index 0000000000..437aca5574 --- /dev/null +++ b/docs/build/stacks-connect/message-signing.md @@ -0,0 +1,191 @@ +# Message Signing + +Learn how to implement message signing in your Stacks application. Message signing allows users to cryptographically prove they control an address without making an on-chain transaction, enabling authentication, authorization, and verifiable statements. + +## What you'll learn + +* Connect to a user's wallet and request message signatures +* Sign both simple text messages and structured data +* Verify signatures to ensure authenticity + +## Prerequisites + +* Node.js installed on your machine +* A code editor like VS Code + +## Installation + +Install the required packages for message signing and verification. + +```bash +npm install @stacks/connect @stacks/encryption +``` + +## Connect to wallet + +Before signing messages, establish a connection to the user's wallet. The connection persists across page reloads. + +```ts +import { connect, isConnected } from '@stacks/connect'; + +async function connectWallet() { + if (!isConnected()) { + const response = await connect(); + console.log('Connected addresses:', response.addresses); + } +} +``` + +Call this function when your app loads or when the user clicks a connect button. + +## Sign text messages + +Request a signature for a simple text message using the `request` method. + +```ts +import { request } from '@stacks/connect'; + +async function signMessage() { + const message = 'Hello World'; + + const response = await request('stx_signMessage', { + message, + }); + + console.log('Signature:', response.signature); + console.log('Public key:', response.publicKey); + + return response; +} +``` + +The wallet will display the message to the user for approval before signing. + +## Sign structured data + +For more complex data, use structured message signing with Clarity values. + +```ts +import { request } from '@stacks/connect'; +import { Cl } from '@stacks/transactions'; + +async function signStructuredMessage() { + const message = Cl.tuple({ + action: Cl.stringAscii('transfer'), + amount: Cl.uint(1000), + recipient: Cl.stringAscii('alice.btc') + }); + + const domain = Cl.tuple({ + name: Cl.stringAscii('My App'), + version: Cl.stringAscii('1.0.0'), + 'chain-id': Cl.uint(1) // 1 for mainnet + }); + + const response = await request('stx_signStructuredMessage', { + message, + domain + }); + + return response; +} +``` + +Structured messages provide better type safety and are easier to parse on-chain. + +## Verify signatures + +Validate signatures to ensure they match the expected message and public key. + +```ts +import { verifyMessageSignatureRsv } from '@stacks/encryption'; + +async function verifySignature( + message: string, + signature: string, + publicKey: string +): Promise { + const isValid = verifyMessageSignatureRsv({ + message, + signature, + publicKey + }); + + if (isValid) { + console.log('✓ Signature verified successfully'); + } else { + console.log('✗ Invalid signature'); + } + + return isValid; +} +``` + +Always verify signatures before trusting the signed data. + +## Complete verification flow + +```ts +async function signAndVerify() { + // Request signature + const message = 'Authorize login at ' + new Date().toISOString(); + const signResponse = await request('stx_signMessage', { message }); + + // Verify immediately + const isValid = await verifySignature( + message, + signResponse.signature, + signResponse.publicKey + ); + + if (isValid) { + // Proceed with authenticated action + console.log('Authentication successful'); + } +} +``` + +## Try it out + +Create a simple authentication system using message signatures. + +```ts +// Generate a unique challenge +function generateChallenge(): string { + const nonce = Math.random().toString(36).substring(7); + const timestamp = Date.now(); + return `Sign this message to authenticate:\nNonce: ${nonce}\nTime: ${timestamp}`; +} + +// Complete auth flow +async function authenticate() { + const challenge = generateChallenge(); + + try { + const response = await request('stx_signMessage', { + message: challenge + }); + + const isValid = verifyMessageSignatureRsv({ + message: challenge, + signature: response.signature, + publicKey: response.publicKey + }); + + if (isValid) { + // Store auth token or session + localStorage.setItem('auth', JSON.stringify({ + publicKey: response.publicKey, + timestamp: Date.now() + })); + + return { success: true }; + } + } catch (error) { + console.error('Authentication failed:', error); + } + + return { success: false }; +} +``` + diff --git a/docs/build/stacks-connect/migration-guide.md b/docs/build/stacks-connect/migration-guide.md new file mode 100644 index 0000000000..9690e9c120 --- /dev/null +++ b/docs/build/stacks-connect/migration-guide.md @@ -0,0 +1,90 @@ +# Migration Guide + +For a while now, the Stacks community has been working on a new standard for wallet-to-dapp communication. Stacks Connect and related projects now use standards like [WBIPs](https://wbips.netlify.app/) and [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md) to allow wallets to communicate with dapps in a more simplified and flexible way. + +{% hint style="info" %} +Migration status\ +Feel free to continue using Stacks Connect `7.x.x` while things stabilize. The `7.x.x` version may still be better supported by some wallets. + +Legacy installs: + +```bash +npm install @stacks/connect@7.10.1 +``` +{% endhint %} + +## Deprecations + +The following classes, methods, and types are deprecated in favor of the new `request` RPC methods: + +* `show...` and `open...` methods +* `authenticate` method +* `UserSession` class and related functionality +* `AppConfig` class +* `SessionOptions` interface +* `SessionData` interface +* `UserData` interface +* `SessionDataStore` class +* `InstanceDataStore` class +* `LocalStorageStore` class + +{% hint style="info" %} +Backwards compatibility\ +`UserSession` and `AppConfig` remain available in `8.x.x` for caching addresses via `loadUserData`, but consider them temporary helpers while you migrate. +{% endhint %} + +## Migration steps + +{% stepper %} +{% step %} +#### Update your @stacks/connect version + +```bash +npm install @stacks/connect@latest +``` +{% endstep %} + +{% step %} +#### Replace legacy methods with `request` + +Switch from `showXyz`, `openXyz`, and `doXyz` helpers to the generic `request(method, params)` API. The `request` function is async, so replace `onFinish`/`onCancel` callbacks with `await` or `.then().catch()` chains. + +Examples: + +* `showConnect()`, `authenticate()` → `connect()` +* `useConnect().doContractCall({})` → `request('stx_callContract', {})` +* `openContractDeploy()` → `request('stx_deployContract', {})` +{% endstep %} + +{% step %} +#### Use `connect` instead of `showConnect` / `authenticate` + +`connect()` is an alias for `request('getAddresses', { forceWalletSelect: true })` and caches the selected address in local storage by default. +{% endstep %} + +{% step %} +#### Update authentication state management + +* Replace `UserSession.isSignedIn()` with `isConnected()` +* Replace `UserSession.signUserOut()` with `disconnect()` +{% endstep %} + +{% step %} +#### Remove legacy code + +* Delete references to deprecated helpers (`AppConfig`, `UserSession`, etc.) +* Remove the `@stacks/connect-react` package + * Manually reload components if you rely on local storage updates + * Hooks are no longer required for Stacks Connect +* A new `@stacks/react` package is in development to simplify state tracking (transaction status, network changes, and more) +{% endstep %} +{% endstepper %} + +## Address Access + +Previously, the `UserSession` class was used to access the user's addresses and data, which abstracted away the underlying implementation details. Now, the `request` method is used to directly interact with the wallet, giving developers more explicit control and clarity over what's happening under the hood. This manual approach makes the wallet interaction more transparent and customizable. Developers can manually manage the currently connected user's address in e.g. local storage, jotai, etc. or use the `connect()`/`request()` method to cache the address in local storage. + +{% hint style="warning" %} +Security note\ +`8.x.x` wallets return only the current network's address (previous versions returned both mainnet and testnet). +{% endhint %} diff --git a/docs/build/stacks-connect/wallet-implementation.md b/docs/build/stacks-connect/wallet-implementation.md new file mode 100644 index 0000000000..be24d485c9 --- /dev/null +++ b/docs/build/stacks-connect/wallet-implementation.md @@ -0,0 +1,197 @@ +--- +description: Support Stacks Connect in your own wallet +--- + +# Wallet Implementation + +
        + +Connect provides a streamlined way for wallets to integrate with dapps by using a simple, direct RPC-based protocol, avoiding unnecessary abstraction layers. It defines a clear wallet provider interface and discovery mechanism, enabling consistent, conflict-free wallet connections. This approach makes it easier for applications to integrate wallets and for anyone to build a wallet that is reliably discoverable by Connect-enabled dapps. + +### Discovery Mechanism + +**Enable Your Custom Wallet to be Detected by Stacks Apps** + +
        + +We will show you how your wallet can interact with incoming JSON RPC 2.0 requests and responses to handle modern Connect methods in order to connect to apps. But first, you’ll want to make sure you have a good understanding of the different context script standards of a [Chrome extension](https://developer.chrome.com/docs/extensions). The context scripts mainly consist of your popup script, background script, and content script. + +**3 scripts of a Chrome extension:** + +* **Popup**: This is the main script that handles the visual UI of the actual popup modal when interacting with an extension. +* **Background**: This script allows your extension to hand off logic that may require intensive computation or for dealing with secure data. +* **Content**: This allows your extension to interact with the web page itself. + +In your content script, which enables you to run scripts on the web page a user is currently on, you’ll want to “inject” a `StacksProvider` object type into the global `window` object of the web page. It’s important to note that this must be handled by your extension’s content script, which should automatically load anytime you land on a webpage. This injected object is what will allow web apps to directly interact with your wallet. It’s like your wallet extension saying, “Hey! I’m available to communicate with your app, let’s connect!” + +The `StacksProvider` object needs to at least have a `.request` method that takes in the name of a string literal method, and an parameters object. + +{% code title="injection.js" expandable="true" %} +```typescript +// --snip-- + +window.MyProvider = { + async request(method, params) { + // Somehow communicate with the wallet (e.g. via events) + + // Recommendation: Create a JSON RPC 2.0 request object + // https://www.jsonrpc.org/specification + + return Promise.resolve({ + // Respond with a JSON RPC 2.0 response object + id: crypto.randomUUID(), // required, same as request + jsonrpc: '2.0', // required + + // `.result` is required on success + result: { + // object matching specified RPC methods + }, + + // `.error` is required on error + error: { + // Use existing codes from https://www.jsonrpc.org/specification#error_object + code: number, // required, integer + message: string, // recommended, single sentence + data: object, // optional + }, + }); + }, + isMyWallet: true, // optional, a way of identifying the wallet for developers +}; + +// --snip-- +``` +{% endcode %} + +This `StacksProvider` object type could be named anything. In the example above, it’s named `MyProvider`. + +From here, web apps can directly call your wallet extension provider via `window.MyProvider` directly, and you don’t even need to use the Stacks Connect library. However, your wallet app would need to manually handle other important implementation details, such as the storage of the wallet info and individual method calling. + +But with the Connect library, apps don’t have to manually roll their own methods and implementations. The Connect library will handle all those functionalities for the app. + +In order for you to make your wallet provider object (from the previous section) be discoverable by the Connect modal UI wallet selector used by frontend apps, you’ll need to then pass it into a separate `wbip_providers` array on the `window` object. The `wbip_providers` array is a new standard set forth by [WBIP004](https://wbips.netlify.app/wbips/WBIP004). + +Any wallet that registers their provider in this array is declaring that they are conforming to the WBIP standards, which are a set of specifications for web apps and client providers to facilitate communication with Bitcoin-related apps. Wallets SHOULD register their provider information under `window.wbip_providers` to be discoverable by websites/libraries expecting this WBIP. + +{% code title="injection.js" expandable="true" %} +```typescript +// --snip-- + +window.wbip_providers = window.wbip_providers || []; +window.wbip_providers.push({ + // `WbipProvider` type + /** The global "path" of the provider (e.g. `"MyProvider"` if registered at `window.MyProvider`) */ + id: 'MyProvider', + /** The name of the provider, as displayed to the user */ + name: 'My Wallet'; + /** The data URL of an image to show (e.g. `data:image/png;base64,iVBORw0...`) @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs */ + icon?: 'data:image/png;base64,iVBORw0...'; + /** Web URL of the provider */ + webUrl?: 'https://mywallet.example.com'; + + // Additional URLs + chromeWebStoreUrl?: string; + mozillaAddOnsUrl?: string; + googlePlayStoreUrl?: string; + iOSAppStoreUrl?: string; +}); + +// --snip-- +``` +{% endcode %} + +Literally injecting these scripts can come from your content script as shown below. You could leverage the content.js script for injecting the `injection.js` into the document page and forwarding messages between the document page and the background script. Setup is dependent on your architecture. + +{% code title="content.js" %} +```typescript +// --snip-- + +const script = document.createElement("script"); +script.src = chrome.runtime.getURL("injection.js"); +script.type = "module"; +document.head.prepend(script); + +// --snip-- +``` +{% endcode %} + +### Handling Method Requests and Responses + +**Enable your wallet to handle requests from the frontend app** + +Structuring the manner in which your wallet handles methods internally is up to your discretion (most methods can be properly handled by methods from [@stacks/transactions](https://docs.hiro.so/stacks/stacks.js/packages/transactions)), but receiving and responding to messages should adhere to the JSON RPC 2.0 standard and data types based on the string literal methods of the incoming request. + +Let’s take the most basic function of connecting. From the Connect modal UI wallet selector, once a user clicks on the `connect` button of your wallet, it will invoke the string literal method of `getAddresses`, which accepts an optional parameter of `network`. + +

        Communication flows are based off of standards like WBIP and SIP-030 to allow wallets to communicate with apps in a more simplified and flexible way.

        + +Once your wallet receives this JSON RPC 2.0 request message, it needs to handle the request and then return a response that conforms to the return type for `getAddresses`. + +Using the `MethodParams` and `MethodResult` type helpers from the Connect library can help you here. Here’s a simplified example of how your wallet should handle the string literal method of `getAddresses`, which allows a standard connection between your wallet and app. + +{% code expandable="true" %} +```typescript +import { type MethodResult, type MethodParams } from "@stacks/connect"; + +async function handleGetAddresses(payload: JsonRpcRequest) { + let params: MethodParams<"getAddresses"> = payload.params; + + // handle generation of account addresses to return back to the app + + let result: MethodResult<"getAddresses"> = { + addresses: [ + { + symbol: "BTC", + address: btcP2PKHAddress, + publicKey: pubKey, + }, + { + symbol: "BTC", + address: btcP2TRAddress, + publicKey: pubKey, + }, + { + symbol: "STX", + address: stxAddress, + publicKey: pubKey, + } + ] + }; + + return result +} +``` +{% endcode %} + +You can also add your own unstandardized methods to your wallet. However, the minimum recommended methods to handle basic wallet functions are standardized and include: + +* `getAddresses` +* `sendTransfer` +* `signPsbt` +* `stx_getAddresses` +* `stx_transferStx` +* `stx_callContract` +* `stx_signMessage` +* `stx_signStructuredMessage` + +### Stacks Wallet Template + +**Build your own Stacks wallet with the Wallet Template in the Hiro Platform** + +
        + +This template is a Chrome extension that comes with basic wallet functionalities, such as generating Stacks and Bitcoin addresses, changing accounts, and importing of external mnemonic seed phrases. Using this template as a starting point, you can build on this template to add other wallet features, such as displaying Stacks NFTs, fetching [Ordinals](https://www.hiro.so/ordinals-api) or [Runes](https://www.hiro.so/runes-api) balances with our dedicated APIs, securing of user mnemonic seed phrases, and much more. + +It’s important to have an ecosystem that boasts a plethora of diverse wallet providers for different use cases, and learning how to build a wallet is a great entry point to Web3 and the Stacks ecosystem. Check out this [article](https://www.hiro.so/blog/an-intro-to-web3-wallets-for-web3-founders) to learn more about the importance of web3 wallets for web3 founders. + +Head to the [Hiro Platform](https://platform.hiro.so/) to start building with this template. + +*** + +### Additional Resources + +* \[[Hiro Platform](https://platform.hiro.so/templates/wallet-extension)] Stacks Wallet Extension Template +* \[[Hiro YT](https://www.youtube.com/watch?v=PdluvfFPWoU)] Build Your Own Bitcoin L2 Wallet Browser Extension +* \[[Github repo](https://github.com/hirosystems/platform-template-stacks-wallet)] Open-source repo of the Stacks wallet extension template +* \[[WBIP](https://wbips.netlify.app/)] Stacks Wallet BIPs +* \[[SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md)] Definition of a Modern Stacks Wallet Interface Standard diff --git a/docs/build/stacks-connect/wallet-support.md b/docs/build/stacks-connect/wallet-support.md new file mode 100644 index 0000000000..12eeb41025 --- /dev/null +++ b/docs/build/stacks-connect/wallet-support.md @@ -0,0 +1,61 @@ +# Wallet Support + +{% hint style="info" %} +Legend: + +* 🔴 No support (yet) +* 🟡 Partial support +* 🟢 Supported +* 🔵 Compatibility overrides present (may transform/normalize behavior) +{% endhint %} + +## Wallet Support + +This page provides detailed information about which methods and events are supported by different wallet providers in the Stacks ecosystem. + +### Method Compatibility + +| Method | Leather | Xverse-like | +| --------------------------- | --------------------------------------- | ------------------------------------------------------------------- | +| `getAddresses` | 🟡 No support for experimental purposes | 🟡 Use `wallet_connect` instead | +| `sendTransfer` | 🟡 Expects `amount` as string | 🟡 Expects `amount` as number | +| `signPsbt` | 🟡 Uses signing index array only | 🟡 Uses `signInputs` record instead of array | +| `stx_getAddresses` | 🟢 | 🔴 | +| `stx_getAccounts` | 🔴 | 🟢 | +| `stx_getNetworks` | 🔴 | 🔴 | +| `stx_transferStx` | 🟢 | 🟢 | +| `stx_transferSip10Ft` | 🟢 | 🔴 | +| `stx_transferSip9Nft` | 🟢 | 🔴 | +| `stx_callContract` | 🟡 Hex-encoded Clarity values only | 🟡 Hex-encoded Clarity values only, no support for `postConditions` | +| `stx_deployContract` | 🟡 Hex-encoded Clarity values only | 🟡 Hex-encoded Clarity values only, no support for `postConditions` | +| `stx_signTransaction` | 🟡 Hex-encoded Clarity values only | 🟡 Hex-encoded Clarity values only | +| `stx_signMessage` | 🟡 Hex-encoded Clarity values only | 🟡 Hex-encoded Clarity values only | +| `stx_signStructuredMessage` | 🟡 Hex-encoded Clarity values only | 🟡 Hex-encoded Clarity values only | +| `stx_updateProfile` | 🔴 | 🔴 | + +### Event Compatibility + +| Event | Leather | Xverse | +| ------------------- | ------- | ------ | +| `stx_accountChange` | 🔴 | 🔴 | +| `stx_networkChange` | 🔴 | 🔴 | + +### Compatibility Layer + +The `request` method in `@stacks/connect` adds a layer of auto-compatibility for different wallet providers. This helps unify the interface where wallet providers may implement methods and results differently. + +* 🟢 No overrides needed for any wallet +* 🔵 Has compatibility overrides that maintain functionality +* 🟡 Has breaking overrides that may lose some information + +| Method | Status | Notes | +| --------------------------- | ------ | ----------------------------------------------------------------------------------------- | +| `getAddresses` | 🔵 | Maps to `wallet_connect` for Xverse-like wallets | +| `sendTransfer` | 🔵 | Converts `amount` to number for Xverse, string for Leather | +| `signPsbt` | 🟡 | Transforms PSBT format for Leather (base64 to hex) with lossy restructure of `signInputs` | +| `stx_getAddresses` | 🔵 | Maps to `wallet_connect` for Xverse-like wallets | +| `stx_callContract` | 🔵 | Transforms Clarity values to hex-encoded format for compatibility | +| `stx_deployContract` | 🔵 | Transforms Clarity values to hex-encoded format for compatibility | +| `stx_signTransaction` | 🔵 | Transforms Clarity values to hex-encoded format for compatibility | +| `stx_signMessage` | 🔵 | Transforms Clarity values to hex-encoded format for compatibility | +| `stx_signStructuredMessage` | 🔵 | Transforms Clarity values to hex-encoded format for compatibility | diff --git a/docs/build/stacks-devtools-ecosystem.md b/docs/build/stacks-devtools-ecosystem.md new file mode 100644 index 0000000000..a49dd0e169 --- /dev/null +++ b/docs/build/stacks-devtools-ecosystem.md @@ -0,0 +1,140 @@ +--- +description: A Devtools Catalog for Building with Stacks +--- + +# Stacks Devtools Ecosystem + +
        + +Discover the full range of developer tools available for building on Stacks. + +This page aggregates all open-sourced developer & experimental tools for building on Stacks. **Tools vary in scope, maturity, and maintenance status.** Whether you’re developing smart contracts, integrating wallets, querying blockchain data, or experimenting with new ideas, this page helps you find the resources to accelerate your projects and navigate the Stacks devtooling ecosystem. + +{% hint style="info" %} +Official Stacks devtools are built and maintained by either **Stacks Labs** or **Hiro**. +{% endhint %} + +### Smart Contract Development + +* [**Clarinet**](https://github.com/stx-labs/clarinet) **\[StacksLabs]** – A local Clarity smart contract development environment with testing and deployment tools, designed to help you build, debug, and iterate on Stacks contracts quickly. +* [**Clarity Playground**](https://github.com/stx-labs/clarity-playground) **\[StacksLabs]** - Run Clarity code in the browser with the Clarinet SDK. +* [**Clarity-Zed**](https://github.com/stx-labs/clarity-zed) **\[StacksLabs]** - Clarity Language Support for Zed editor. +* [**Stacks Pyth Bridge**](https://github.com/stx-labs/stacks-pyth-bridge) **\[StacksLabs]** - The Pyth protocol integration is available as a beta on both testnet and mainnet networks for Stacks, to help developers test, give feedback, and ensure the reliability and stability of the integration. +* [**Clarity-WASM**](https://github.com/stx-labs/clarity-wasm) **\[StacksLabs]** - `clar2wasm` is a compiler for generating WebAssembly from Clarity. + +
        + +Community & Experimental + +* [**Clarity Deployed Contracts**](https://github.com/boomcrypto/clarity-deployed-contracts) - Browse the source code of all deployed Clarity contracts on Stacks' mainnet and testnet. + +
        + +*** + +### SDKs & Libraries + +* [**Stacks.js**](https://github.com/stx-labs/stacks.js) **\[StacksLabs]** – A JavaScript/Typescript library that makes it easy to interact with the Stacks blockchain, including authentication, transactions, and smart contract calls from web apps. +* [**Clarity-Go**](https://github.com/stx-labs/clarity-go) **\[StacksLabs]** - A Go library for decoding Clarity values from their binary serialization format used by the Stacks blockchain. + +
        + +Community & Experimental + +* [**Clarigen**](https://www.clarigen.dev/) - Clarigen is a developer tool that automatically generates TypeScript-friendly clients that can interact with Clarity smart contracts. +* [**Stacks.js Starters**](https://github.com/stx-labs/stacks.js-starters) **\[StacksLabs]** - Quickly bootstrap frontend applications with Stacks.js on top of multiple JavaScript frameworks as the foundation. +* [**SecondLayer**](https://github.com/ryanwaits/secondlayer) - Generate fully typed contract interfaces, functions, and React hooks for Clarity smart contracts. +* [**Clarity-types**](https://github.com/ryanwaits/clarity-types) - Strict TypeScript types for Clarity ABI properties and values. +* [**Stacks Kit**](https://github.com/karkigrishmin/stacks-kit) - React toolkit for Stacks blockchain. Think RainbowKit, but for Bitcoin L2. +* [**Stacks.rs**](https://github.com/52/stacks.rs) - A Rust toolkit to interact with the Stacks Blockchain. +* [**StacksPy**](https://github.com/rohitverma007/stackspy) - Python Library to interact with the Stacks blockchain. +* [**Bitcoin TX Proof**](https://github.com/kenrogers/bitcoin-tx-proof) - A TypeScript library for generating Bitcoin transaction proofs, including witness data and merkle proofs. This package helps developers verify Bitcoin transactions in Clarity. +* [**Clarity Bitcoin Client**](https://github.com/BigMarketDao/clarity-bitcoin-client) - TypeScript library for interacting with the `clarity-bitcoin-lib` contract on Stacks. + +
        + +*** + +### Indexing & Data + +* [**Stacks Blockchain APIs**](https://github.com/hirosystems/stacks-blockchain-api) **\[Hiro]** – High-performance APIs to query blockchain data, explore blocks, transactions, smart contracts, and more, without running your own node. +* [**Token Metadata APIs**](https://github.com/hirosystems/token-metadata-api) **\[Hiro] -** Fetch metadata for every token on Stacks and effortlessly put tokens into your app. Verify and display tokens in your app, for everything from DeFi to NFTs. +* [**Bitcoin Indexer**](https://github.com/hirosystems/bitcoin-indexer) **\[Hiro] -** Index Bitcoin meta-protocols like Ordinals, BRC-20, and Runes. +* [**Chainhooks**](https://github.com/hirosystems/chainhook) **\[Hiro]** – A notification service for dApps that triggers webhooks on specific blockchain events, helping you respond to transactions, contract calls, and chain reorganizations in real time. + +
        + +Community & Experimental + +* [**Chainhooks-Go-Client**](https://github.com/tony1908/chainhooks-client-go) - A comprehensive Go client library for interacting with the Hiro Chainhooks API. +* [**BNS**](https://www.bnsv2.com/docs) - [API](https://github.com/Strata-Labs/bnsv2-api) and [SDK](https://github.com/Strata-Labs/bns-v2-sdk) documentation for BNSv2, covering how to programmatically register, resolve, and manage Bitcoin Name System identities using on-chain contracts and developer libraries. +* [**Signal21**](https://signal21.io/) - Data analytics platform for the Bitcoin Economy: on-chain visibility into Bitcoin L1, L2s, and Dapps. +* [**Velar Devtools**](https://docs.velar.com/velar/developers) - Velar SDK and APIs allowing developers to implement token swaps, manage liquidity, and interact with the Velar DEX/DeFi ecosystem. +* [**Bitflow Devtools**](https://docs.bitflow.finance/bitflow-documentation/developers/overview) - Documentation on Bitflow's SDKs and contracts for interacting with Bitflow's DeFi ecosystem. +* [**x402-Stacks**](https://github.com/tony1908/x402Stacks) - A TypeScript library for implementing the x402 payment protocol on Stacks blockchain. +* [**StacksAgent MCP**](https://github.com/kai-builder/stacksagent-mcp) - A Model Context Protocol (MCP) server that enables Claude Desktop to interact with the Stacks blockchain. +* [**AIBTC**](https://github.com/aibtcdev) - AI agent tooling for Stacks. + +
        + +*** + +### Testing & Simulation + +* [**Rendezvous**](https://stacks-network.github.io/rendezvous/) - A smart contract fuzzer for Clarity. + +*** + +### Wallet & Auth + +* [**Stacks Connect**](https://github.com/stx-labs/connect) **\[StacksLabs]** – A protocol-agnostic wallet integration library that enables apps to securely connect with multiple Stacks wallets without reinventing the onboarding flow. + +
        + +Community & Experimental + +* [**Sign-In with Stacks**](https://github.com/pradel/sign-in-with-stacks/) - A library for creating and verifying Sign-In with Stacks messages. +* [**sBTC Pay**](https://github.com/STX-CITY/sbtc-pay) - A complete "Stripe for sBTC" payment gateway that enables businesses to easily accept Bitcoin payments via sBTC on Stacks blockchain. +* [**Bolt Protocol**](https://github.com/ronoel/bolt-protocol) - Bolt Protocol is a next-generation framework designed to enable near-instant transactions on the Bitcoin network and enable users to pay fees directly in sBTC. + +
        + +*** + +### Monitoring & Analytics + +* [**Explorer**](https://github.com/stx-labs/explorer) **\[StacksLabs] -** Explore transactions and accounts on the Stacks blockchain. Clone any contract and experiment in your browser with the Explorer sandbox. +* [**Platform**](https://www.hiro.so/platform) **\[Hiro]** – Manage API keys, Chainhooks, and analyze onchain data streams in one command center. Also houses ready-to-use starter templates for different use case applications. + +
        + +Community & Experimental + +* [**STXER**](https://stxer.xyz/) - Community built explorer, debugger and simulator for Stacks transactions. + +
        + +*** + +### Docs & Education + +* [**Hiro Docs**](https://docs.hiro.so/) **\[Hiro]** - Official Hiro developer documentation website. +* [**Stacks Docs**](https://docs.stacks.co/) **\[StacksLabs]** - Official Stacks developer documentation website. +* [**SIPs**](https://github.com/stacksgov/sips) - Community-submitted Stacks Improvement Proposals (SIPs). + +### Core + +* [**Stacks Core**](https://github.com/stacks-network/stacks-core) **\[StacksLabs]** - Reference implementation of the Stacks blockchain in Rust. +* [**Stacks Blockchain Docker**](https://github.com/stacks-network/stacks-blockchain-docker) **\[StacksLabs]** - Run your own Stacks Blockchain node with Docker. + +
        + +Community & Experimental + +* [**Stacks-Monitoring**](https://github.com/alexlmiller/stacks-monitoring) - Observability stack for Stacks blockchain nodes and signers - dashboards, alerts, and log processing. + +
        + +*** + +If you’ve built a Stacks devtool you’d like included, reach out to us via GitHub or Discord with a brief description and link, and we’ll review it for addition to this devtools list. diff --git a/docs/build/stacks.js/accounts-and-addresses.md b/docs/build/stacks.js/accounts-and-addresses.md new file mode 100644 index 0000000000..8fdb78fbdd --- /dev/null +++ b/docs/build/stacks.js/accounts-and-addresses.md @@ -0,0 +1,182 @@ +# Accounts & Addresses + +Stacks uses the concept of an "account" to represent a user's identity on the blockchain. An account is identified by a unique address derived from the account's public key. + +## Address formats + +Stacks addresses use different prefixes to indicate the network they belong to, making it easy to distinguish between mainnet and testnet addresses. + +```ts +// Mainnet address starts with 'SP' +const mainnetAddress = 'SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159'; + +// Testnet address starts with 'ST' +const testnetAddress = 'ST2F4BK4GZH6YFBNHYDDGN4T1RKBA7DA1BJZPJEJJ'; +``` + +The address format ensures that tokens on testnet cannot be accidentally sent to mainnet addresses and vice versa. + +## Getting an address + +There are several ways to obtain a Stacks address depending on your use case and what cryptographic material you have available. + +{% stepper %} +{% step %} +#### Using Stacks Connect + +When building user-facing applications, you'll typically get addresses from users who connect their wallets through Stacks Connect. + +```ts +import { connect, getLocalStorage, request } from '@stacks/connect'; + +async function handleWalletConnection() { + // Connect to wallet + const response = await connect(); + + // Get stored addresses + const data = getLocalStorage(); + const stxAddresses = data.addresses.stx; + + if (stxAddresses && stxAddresses.length > 0) { + const address = stxAddresses[0].address; + console.log('STX Address:', address); + // 'SP1MXSZF4NFC8JQ1TTYGEC2WADMC7Y3GHVZYRX6RF' + } + + // Get detailed account info if needed + const accounts = await request('stx_getAccounts'); + console.log('Account details:', accounts.addresses[0]); +} +``` + +Stacks Connect stores the connected addresses in local storage, allowing your app to persist the connection across page reloads. +{% endstep %} + +{% step %} +#### Using a seed phrase + +For programmatic wallet generation or when restoring accounts from backup, you can derive addresses from a seed phrase (also known as a mnemonic). + +```ts +import { generateWallet, generateSecretKey } from '@stacks/wallet-sdk'; + +async function createWalletFromSeed() { + // Generate a new 24-word seed phrase + const secretKey = generateSecretKey(); + + // Or use an existing seed phrase + // const secretKey = 'already owned seed phrase ...'; + + const wallet = await generateWallet({ + secretKey, + password: 'optional-encryption-password', + }); + + // Get the first account's address + const account = wallet.accounts[0]; + const mainnetAddress = account.address; + + console.log('Address:', mainnetAddress); + console.log('Private key:', account.stxPrivateKey); +} +``` + +Each wallet can contain multiple accounts, all derived from the same seed phrase using different derivation paths. +{% endstep %} + +{% step %} +#### Using a private key + +If you already have a private key, you can directly derive the corresponding address without going through the wallet generation process. + +```ts +import { privateKeyToAddress, TransactionVersion } from '@stacks/transactions'; + +function getAddressFromPrivateKey() { + // Compressed private key (64 or 66 characters) + const privateKey = 'your-private-key-here'; + + // For mainnet + const mainnetAddress = privateKeyToAddress( + privateKey, + TransactionVersion.Mainnet + ); + + // For testnet + const testnetAddress = privateKeyToAddress( + privateKey, + TransactionVersion.Testnet + ); + + console.log('Mainnet:', mainnetAddress); + console.log('Testnet:', testnetAddress); +} +``` + +The same private key will generate different addresses for mainnet and testnet due to the network-specific version bytes. +{% endstep %} + +{% step %} +#### Using a public key + +When you only have access to a public key (for example, in a watch-only wallet scenario), you can still derive the corresponding address. + +```ts +import { publicKeyToAddress, TransactionVersion } from '@stacks/transactions'; + +function getAddressFromPublicKey() { + // Compressed public key (66 characters starting with 02 or 03) + const publicKey = '03b3e0a76b292b2c83fc0ac14ae6160d0438ebe94e14bbb7d0ded3c217f3d29ba7'; + + // For mainnet + const mainnetAddress = publicKeyToAddress( + publicKey, + TransactionVersion.Mainnet + ); + + // For testnet + const testnetAddress = publicKeyToAddress( + publicKey, + TransactionVersion.Testnet + ); + + console.log('Mainnet:', mainnetAddress); + // 'SP1MXSZF4NFC8JQ1TTYGEC2WADMC7Y3GHVZYRX6RF' +} +``` + +This is useful for creating watch-only wallets or verifying addresses without access to private keys. +{% endstep %} +{% endstepper %} + +## Address validation + +Before sending transactions, it's important to validate that addresses are properly formatted. + +```ts +import { validateStacksAddress } from '@stacks/transactions'; + +function isValidAddress(address: string): boolean { + try { + // Check if it's a valid mainnet address + if (address.startsWith('SP')) { + return validateStacksAddress(address); + } + + // Check if it's a valid testnet address + if (address.startsWith('ST')) { + return validateStacksAddress(address); + } + + return false; + } catch (error) { + return false; + } +} + +// Examples +console.log(isValidAddress('SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159')); // true +console.log(isValidAddress('invalid-address')); // false +``` + +Always validate addresses before using them in transactions to prevent loss of funds due to typos or formatting errors. diff --git a/docs/build/stacks.js/address-validation.md b/docs/build/stacks.js/address-validation.md new file mode 100644 index 0000000000..5cdc1b5b1a --- /dev/null +++ b/docs/build/stacks.js/address-validation.md @@ -0,0 +1,585 @@ +# Address Validation + +Validate and format Stacks addresses and principals. + +## Overview + +Stacks addresses follow specific formats that differ between mainnet and testnet. Proper validation ensures your application handles addresses correctly, preventing loss of funds and improving user experience. This guide covers address validation, formatting, and conversion utilities. + +## Basic address validation + +Validate Stacks addresses: + +{% code title="basic-validation.ts" %} +```ts +import { + validateStacksAddress, + validateContractName +} from '@stacks/transactions'; + +// Validate standard addresses +const isValidMainnet = validateStacksAddress('SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY'); +console.log('Valid mainnet:', isValidMainnet); // true + +const isValidTestnet = validateStacksAddress('ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC'); +console.log('Valid testnet:', isValidTestnet); // true + +const isInvalid = validateStacksAddress('invalid-address'); +console.log('Valid:', isInvalid); // false + +// Validate contract names +const validContract = validateContractName('my-contract'); +console.log('Valid contract name:', validContract); // true + +const invalidContract = validateContractName('My Contract!'); +console.log('Valid contract name:', invalidContract); // false +``` +{% endcode %} + +## Address types and detection + +Identify address types and networks: + +{% code title="address-info.ts" %} +```ts +import { + getAddressFromPrivateKey, + getAddressFromPublicKey, + TransactionVersion +} from '@stacks/transactions'; + +// Detect address type from prefix +function getAddressInfo(address: string): { + type: 'standard' | 'contract' | 'multisig' | 'invalid'; + network: 'mainnet' | 'testnet' | 'unknown'; +} { + if (!validateStacksAddress(address)) { + return { type: 'invalid', network: 'unknown' }; + } + + // Mainnet prefixes + if (address.startsWith('SP')) { + return { type: 'standard', network: 'mainnet' }; + } else if (address.startsWith('SM')) { + return { type: 'multisig', network: 'mainnet' }; + } + + // Testnet prefixes + else if (address.startsWith('ST')) { + return { type: 'standard', network: 'testnet' }; + } else if (address.startsWith('SN')) { + return { type: 'multisig', network: 'testnet' }; + } + + // Contract address (contains .) + if (address.includes('.')) { + const [principal] = address.split('.'); + const info = getAddressInfo(principal); + return { ...info, type: 'contract' }; + } + + return { type: 'invalid', network: 'unknown' }; +} + +// Usage +const info = getAddressInfo('SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY.my-contract'); +console.log(info); // { type: 'contract', network: 'mainnet' } +``` +{% endcode %} + +## Address generation + +Generate addresses from keys: + +{% code title="address-generation.ts" %} +```ts +import { + makeRandomPrivKey, + getPublicKey, + getAddressFromPrivateKey, + getAddressFromPublicKey, + TransactionVersion, + AddressHashMode +} from '@stacks/transactions'; + +// Generate new random address +function generateNewAddress(network: 'mainnet' | 'testnet') { + const privateKey = makeRandomPrivKey(); + const publicKey = getPublicKey(privateKey); + + const version = network === 'mainnet' + ? TransactionVersion.Mainnet + : TransactionVersion.Testnet; + + const address = getAddressFromPrivateKey(privateKey, version); + + return { + privateKey, + publicKey, + address, + }; +} + +// Generate address from existing private key +function getAddressFromKey(privateKey: string, network: 'mainnet' | 'testnet') { + const version = network === 'mainnet' + ? TransactionVersion.Mainnet + : TransactionVersion.Testnet; + + return getAddressFromPrivateKey(privateKey, version); +} + +// Generate multisig address +function generateMultisigAddress( + publicKeys: string[], + signaturesRequired: number, + network: 'mainnet' | 'testnet' +) { + const version = network === 'mainnet' + ? TransactionVersion.Mainnet + : TransactionVersion.Testnet; + + const hashMode = AddressHashMode.SerializeP2SH; + + // Implementation depends on multisig setup + // This is a simplified example + return getAddressFromPublicKey( + publicKeys[0], // Simplified - real implementation needs all keys + version, + hashMode + ); +} +``` +{% endcode %} + +## Contract address handling + +Work with contract principals: + +{% code title="contract-address.ts" %} +```ts +// Parse contract address components +function parseContractAddress(contractAddress: string): { + principal: string; + contractName: string; + isValid: boolean; +} { + const parts = contractAddress.split('.'); + + if (parts.length !== 2) { + return { principal: '', contractName: '', isValid: false }; + } + + const [principal, contractName] = parts; + + const isValid = validateStacksAddress(principal) && + validateContractName(contractName); + + return { principal, contractName, isValid }; +} + +// Build contract address +function buildContractAddress(principal: string, contractName: string): string { + if (!validateStacksAddress(principal)) { + throw new Error('Invalid principal address'); + } + + if (!validateContractName(contractName)) { + throw new Error('Invalid contract name'); + } + + return `${principal}.${contractName}`; +} + +// Validate full contract identifier +function validateContractAddress(address: string): boolean { + const { isValid } = parseContractAddress(address); + return isValid; +} + +// Usage +const parsed = parseContractAddress('SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY.my-token'); +console.log(parsed); +// { principal: 'SP2J6...', contractName: 'my-token', isValid: true } +``` +{% endcode %} + +## Address conversion utilities + +Convert between formats and networks: + +{% code title="conversion-utils.ts" %} +```ts +import { c32addressDecode, c32address } from 'c32check'; + +// Convert between testnet and mainnet addresses +function convertAddressNetwork( + address: string, + toNetwork: 'mainnet' | 'testnet' +): string { + try { + // Decode the address + const decoded = c32addressDecode(address); + + // Determine new version + let newVersion: number; + if (toNetwork === 'mainnet') { + newVersion = decoded[0] === 26 ? 22 : 20; // Multi-sig or standard + } else { + newVersion = decoded[0] === 22 ? 26 : 21; // Multi-sig or standard + } + + // Re-encode with new version + const newAddress = c32address(newVersion, decoded[1]); + return newAddress; + } catch (error) { + throw new Error('Invalid address format'); + } +} + +// Extract address hash +function getAddressHash(address: string): string { + const decoded = c32addressDecode(address); + return Buffer.from(decoded[1]).toString('hex'); +} + +// Check if addresses are same (ignoring network) +function isSameAddress(addr1: string, addr2: string): boolean { + try { + const hash1 = getAddressHash(addr1); + const hash2 = getAddressHash(addr2); + return hash1 === hash2; + } catch { + return false; + } +} +``` +{% endcode %} + +## Advanced validation patterns + +### Comprehensive address validator + +Create a robust validation system: + +{% code title="address-validator.ts" %} +```ts +class AddressValidator { + private cache = new Map(); + + validate(address: string, options?: { + network?: 'mainnet' | 'testnet'; + allowContracts?: boolean; + allowMultisig?: boolean; + }): { valid: boolean; reason?: string } { + // Check cache + const cacheKey = `${address}-${JSON.stringify(options)}`; + if (this.cache.has(cacheKey)) { + return { valid: this.cache.get(cacheKey)! }; + } + + // Basic validation + if (!validateStacksAddress(address)) { + return { valid: false, reason: 'Invalid address format' }; + } + + const info = getAddressInfo(address); + + // Check network if specified + if (options?.network && info.network !== options.network) { + return { + valid: false, + reason: `Address is for ${info.network}, expected ${options.network}` + }; + } + + // Check contract addresses + if (info.type === 'contract' && !options?.allowContracts) { + return { valid: false, reason: 'Contract addresses not allowed' }; + } + + // Check multisig + if (info.type === 'multisig' && !options?.allowMultisig) { + return { valid: false, reason: 'Multisig addresses not allowed' }; + } + + // Cache result + this.cache.set(cacheKey, true); + + return { valid: true }; + } + + validateBatch(addresses: string[], options?: any): Map { + const results = new Map(); + + for (const address of addresses) { + const { valid } = this.validate(address, options); + results.set(address, valid); + } + + return results; + } +} +``` +{% endcode %} + +### Address formatting + +Format addresses for display: + +{% code title="format-address.tsx" %} +```ts +function formatAddress( + address: string, + options?: { + truncate?: boolean; + length?: number; + separator?: string; + } +): string { + if (!validateStacksAddress(address)) { + return 'Invalid Address'; + } + + if (options?.truncate) { + const length = options.length || 8; + const start = address.slice(0, length); + const end = address.slice(-length); + const separator = options.separator || '...'; + return `${start}${separator}${end}`; + } + + return address; +} + +// Format for display with copy functionality +function AddressDisplay({ address }: { address: string }) { + const [copied, setCopied] = useState(false); + + const formatted = formatAddress(address, { + truncate: true, + length: 6 + }); + + const copyToClipboard = () => { + navigator.clipboard.writeText(address); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( +
        + {formatted} + {copied && ✓ Copied} +
        + ); +} +``` +{% endcode %} + +## Input validation hooks + +React hooks for address inputs: + +{% code title="useAddressInput.tsx" %} +```ts +import { useState, useCallback } from 'react'; + +function useAddressInput(options?: { + network?: 'mainnet' | 'testnet'; + allowContracts?: boolean; +}) { + const [value, setValue] = useState(''); + const [error, setError] = useState(null); + const [isValid, setIsValid] = useState(false); + + const validate = useCallback((address: string) => { + if (!address) { + setError(null); + setIsValid(false); + return; + } + + const validator = new AddressValidator(); + const result = validator.validate(address, options); + + setError(result.reason || null); + setIsValid(result.valid); + }, [options]); + + const handleChange = useCallback((newValue: string) => { + setValue(newValue); + validate(newValue); + }, [validate]); + + return { + value, + error, + isValid, + setValue: handleChange, + validate, + }; +} + +// Usage in component +function AddressInput() { + const address = useAddressInput({ + network: 'mainnet', + allowContracts: false + }); + + return ( +
        + address.setValue(e.target.value)} + placeholder="Enter Stacks address" + className={address.error ? 'error' : ''} + /> + {address.error && ( + {address.error} + )} +
        + ); +} +``` +{% endcode %} + +## Security considerations + +Implement secure address handling: + +{% code title="security.ts" %} +```ts +// Sanitize user input +function sanitizeAddress(input: string): string { + // Remove whitespace and common separators + return input.trim().replace(/[\s\-_]/g, ''); +} + +// Verify address ownership +async function verifyAddressOwnership( + address: string, + signature: string, + message: string +): Promise { + try { + // Verify the signature matches the address + const verified = verifyMessageSignature({ + message, + signature, + publicKey: await getPublicKeyFromAddress(address), + }); + + return verified; + } catch { + return false; + } +} + +// Validate address for specific use case +function validateRecipientAddress( + address: string, + options: { + blockList?: string[]; + allowList?: string[]; + requireMainnet?: boolean; + } +): { valid: boolean; reason?: string } { + // Check blocklist + if (options.blockList?.includes(address)) { + return { valid: false, reason: 'Address is blocked' }; + } + + // Check allowlist + if (options.allowList && !options.allowList.includes(address)) { + return { valid: false, reason: 'Address not in allowlist' }; + } + + // Check network + const info = getAddressInfo(address); + if (options.requireMainnet && info.network !== 'mainnet') { + return { valid: false, reason: 'Mainnet address required' }; + } + + return { valid: true }; +} +``` +{% endcode %} + +## Testing utilities + +Test address validation: + +{% code title="address-validation.test.ts" %} +```ts +import { describe, it, expect } from 'vitest'; + +describe('Address validation', () => { + const validAddresses = [ + 'SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY', + 'ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC', + 'SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY.my-contract', + ]; + + const invalidAddresses = [ + 'invalid', + 'SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZ', // Too short + 'XP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY', // Wrong prefix + 'SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY.', // Missing contract + ]; + + validAddresses.forEach(address => { + it(`should validate ${address}`, () => { + expect(validateStacksAddress(address)).toBe(true); + }); + }); + + invalidAddresses.forEach(address => { + it(`should reject ${address}`, () => { + expect(validateStacksAddress(address)).toBe(false); + }); + }); +}); +``` +{% endcode %} + +## Best practices + +* Always validate user input: Never trust addresses from users +* Check network compatibility: Ensure addresses match your network +* Handle edge cases: Contract addresses, multisig, etc. +* Cache validation results: Avoid redundant validation +* Provide clear error messages: Help users fix invalid inputs + +## Common mistakes + +Not checking network type: + +{% code title="bad-vs-good-network.ts" %} +```ts +// Bad: Accepting any valid address +const isValid = validateStacksAddress(userInput); + +// Good: Checking network matches +const info = getAddressInfo(userInput); +if (info.network !== 'mainnet') { + throw new Error('Please use a mainnet address'); +} +``` +{% endcode %} + +Assuming address format: + +{% code title="bad-vs-good-parse.ts" %} +```ts +// Bad: Assuming standard address +const [principal, contract] = address.split('.'); + +// Good: Proper validation +const parsed = parseContractAddress(address); +if (!parsed.isValid) { + throw new Error('Invalid contract address'); +} +``` +{% endcode %} diff --git a/docs/build/stacks.js/build-transactions.md b/docs/build/stacks.js/build-transactions.md new file mode 100644 index 0000000000..f384996e91 --- /dev/null +++ b/docs/build/stacks.js/build-transactions.md @@ -0,0 +1,259 @@ +# Build Transactions + +Learn how to build transactions programmatically for complete control over blockchain interactions. + +## Objectives + +* Build signed transactions for immediate broadcast +* Create unsigned transactions for multi-signature workflows +* Implement sponsored transactions to pay fees for users + +## Transaction types + +Stacks supports five primary transaction types, each serving a specific purpose. + +```ts +// STX Transfer - Send native tokens +const stxTransfer = await makeSTXTokenTransfer(options); + +// Contract Deployment - Deploy Clarity contracts +const deployment = await makeContractDeploy(options); + +// Contract Call - Execute contract functions +const contractCall = await makeContractCall(options); + +// Each transaction type accepts similar base options: +interface TransactionOptions { + senderKey: string; // Private key for signing + network: string; // 'mainnet' or 'testnet' + fee?: bigint; // Manual fee in microSTX + nonce?: bigint; // Manual nonce + anchorMode?: AnchorMode; // Block anchoring strategy +} +``` + +## Building signed transactions + +Signed transactions are ready to broadcast immediately. The private key signs during creation. + +{% stepper %} +{% step %} +#### STX token transfer + +```ts +import { makeSTXTokenTransfer, broadcastTransaction } from '@stacks/transactions'; + +const transaction = await makeSTXTokenTransfer({ + recipient: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG', + amount: 1000000n, // 1 STX = 1,000,000 microSTX + senderKey: 'your-private-key-hex', + network: 'testnet', + memo: 'Payment for services', // Optional memo (max 34 bytes) +}); + +const result = await broadcastTransaction({ transaction }); +console.log('Transaction ID:', result.txid); +``` +{% endstep %} + +{% step %} +#### Smart contract deployment + +```ts +import { makeContractDeploy, ClarityVersion } from '@stacks/transactions'; + +const contractCode = ` +(define-public (say-hello) + (ok "Hello, Stacks!")) +`; + +const transaction = await makeContractDeploy({ + contractName: 'hello-world', + codeBody: contractCode, + senderKey: 'your-private-key-hex', + network: 'testnet', + clarityVersion: ClarityVersion.Clarity3, +}); + +const result = await broadcastTransaction({ transaction }); +``` +{% endstep %} + +{% step %} +#### Contract function calls + +```ts +import { makeContractCall, Cl } from '@stacks/transactions'; + +const transaction = await makeContractCall({ + contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + contractName: 'counter', + functionName: 'increment', + functionArgs: [Cl.uint(1)], + senderKey: 'your-private-key-hex', + network: 'testnet', +}); +``` +{% endstep %} +{% endstepper %} + +## Unsigned transactions + +Create unsigned transactions for multi-signature workflows or offline signing. + +```ts +import { makeUnsignedSTXTokenTransfer, TransactionSigner } from '@stacks/transactions'; +import { publicKeyFromPrivate } from '@stacks/encryption'; + +// Create unsigned transaction +const publicKey = publicKeyFromPrivate('your-private-key'); + +const unsignedTx = await makeUnsignedSTXTokenTransfer({ + recipient: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG', + amount: 1000000n, + publicKey, + network: 'testnet', +}); + +// Sign separately +const signer = new TransactionSigner(unsignedTx); +signer.signOrigin('your-private-key'); + +const signedTx = signer.transaction; +``` + +## Sponsored transactions + +Let one account pay fees for another account's transaction. + +```ts +// User creates sponsored transaction +const userTx = await makeContractCall({ + contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + contractName: 'my-contract', + functionName: 'transfer', + functionArgs: [Cl.principal('ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG')], + senderKey: 'user-private-key', + sponsored: true, + fee: 0n, // User doesn't pay + network: 'testnet', +}); + +// Sponsor completes and pays +import { sponsorTransaction } from '@stacks/transactions'; + +const sponsoredTx = await sponsorTransaction({ + transaction: userTx, + sponsorPrivateKey: 'sponsor-private-key', + fee: 2000n, // Sponsor pays the fee + network: 'testnet', +}); + +const result = await broadcastTransaction({ transaction: sponsoredTx }); +``` + +## Multi-signature transactions + +Require multiple signatures for enhanced security. + +```ts +// Create multi-sig transaction (2-of-3) +const publicKeys = [publicKey1, publicKey2, publicKey3]; + +const multiSigTx = await makeUnsignedSTXTokenTransfer({ + recipient: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG', + amount: 5000000n, + numSignatures: 2, // Require 2 of 3 + publicKeys, + network: 'testnet', +}); + +// Collect signatures +const signer = new TransactionSigner(multiSigTx); +signer.signOrigin(privateKey1); // First signature +signer.appendOrigin(privateKey2); // Second signature + +const signedTx = signer.transaction; +``` + +## Working with Clarity values + +Use the `Cl` helper for type-safe contract arguments. + +```ts +import { Cl } from '@stacks/transactions'; + +const functionArgs = [ + // Primitives + Cl.uint(42), + Cl.int(-10), + Cl.bool(true), + Cl.stringUtf8('Hello 世界'), + Cl.stringAscii('Hello World'), + + // Principals + Cl.standardPrincipal('ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG'), + Cl.contractPrincipal('ST123...', 'my-contract'), + + // Composites + Cl.list([Cl.uint(1), Cl.uint(2), Cl.uint(3)]), + Cl.tuple({ + name: Cl.stringUtf8('Alice'), + age: Cl.uint(30) + }), + + // Optionals and responses + Cl.some(Cl.uint(42)), + Cl.none(), + Cl.ok(Cl.uint(200)), + Cl.err(Cl.uint(404)) +]; +``` + +## Post-conditions + +Add safety constraints to protect users from unexpected transfers. + +```ts +import { Pc, PostConditionMode } from '@stacks/transactions'; + +const transaction = await makeContractCall({ + // ... other options + postConditions: [ + // Sender must send exactly 1 STX + Pc.principal('ST1ADDRESS...') + .willSendEq(1000000n) + .ustx(), + + // Contract must transfer tokens + Pc.principal('ST2CONTRACT...') + .willSendGte(100n) + .ft('ST2CONTRACT.token-contract', 'my-token') + ], + postConditionMode: PostConditionMode.Deny, // Strict mode +}); +``` + +## Fee estimation + +Get accurate fee estimates before broadcasting. + +```ts +import { estimateFee } from '@stacks/transactions'; + +// Build transaction first +const tx = await makeSTXTokenTransfer({ + recipient: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG', + amount: 1000000n, + senderKey: privateKey, + network: 'testnet', + fee: 1n, // Minimal fee for estimation +}); + +// Estimate appropriate fee +const feeRate = await estimateFee(tx); +tx.setFee(feeRate); + +// Now broadcast with accurate fee +const result = await broadcastTransaction({ transaction: tx }); +``` diff --git a/docs/build/stacks.js/contract-calls.md b/docs/build/stacks.js/contract-calls.md new file mode 100644 index 0000000000..6fb3b0cc07 --- /dev/null +++ b/docs/build/stacks.js/contract-calls.md @@ -0,0 +1,229 @@ +# Contract Calls + +Contract calls allow you to execute state-changing functions in smart contracts. Unlike read-only calls, these create transactions that must be signed and broadcast to the network. + +## Basic contract call + +Execute a simple contract function by creating a transaction with the required parameters. + +```ts +import { + makeContractCall, + broadcastTransaction, + AnchorMode +} from '@stacks/transactions'; + +async function callContract() { + const txOptions = { + contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + contractName: 'my-contract', + functionName: 'transfer', + functionArgs: [], + senderKey: 'your-private-key', + network: 'testnet', + anchorMode: AnchorMode.Any, + }; + + const transaction = await makeContractCall(txOptions); + const broadcastResponse = await broadcastTransaction({ transaction }); + + console.log('Transaction ID:', broadcastResponse.txid); +} +``` + +The `makeContractCall` function creates a transaction that will execute the specified function when confirmed on-chain. + +## Passing function arguments + +Most contract functions require arguments. Use Clarity value constructors to match the expected parameter types. + +```ts +import { + Cl, + makeContractCall, +} from '@stacks/transactions'; + +const functionArgs = [ + Cl.principal('ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG'), // recipient + Cl.uint(1000000), // amount + Cl.buffer(Buffer.from('Transfer memo', 'utf-8')), // memo +]; + +const txOptions = { + contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + contractName: 'sip-010-token', + functionName: 'transfer', + functionArgs, + senderKey: 'your-private-key', + network: "testnet", +}; + +const transaction = await makeContractCall(txOptions); +const result = await broadcastTransaction({ transaction }); +console.log('Transaction ID:', result.txid); +``` + +Each Clarity type has a corresponding constructor function that ensures proper encoding for the blockchain. + +### Complex argument types + +```ts +// Tuple arguments +const userInfo = Cl.tuple({ + name: Cl.string('Alice'), + age: Cl.uint(30), + active: Cl.bool(true), +}); + +// List arguments +const addresses = Cl.list([ + Cl.principal('ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM'), + Cl.principal('ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG'), +]); +``` + +Optional and response values have dedicated constructors for proper type safety. + +```ts +// Optional values +const optionalValue = Cl.some(Cl.uint(42)); // (some 42) +const noValue = Cl.none(); // none + +// Response values +const successResponse = Cl.ok(Cl.uint(100)); +const errorResponse = Cl.err(Cl.uint(404)); +``` + +## Contract call with STX transfer + +Some contracts require STX to be sent along with the function call, such as when minting NFTs or paying for services. + +```ts +async function mintNFT() { + const mintPrice = 1000000; // 1 STX + + const txOptions = { + contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + contractName: 'nft-collection', + functionName: 'mint', + functionArgs: [], + senderKey: 'your-private-key', + network: new StacksTestnet(), + anchorMode: AnchorMode.Any, + // Attach STX to the contract call + amount: mintPrice, + }; + + const transaction = await makeContractCall(txOptions); + return broadcastTransaction(transaction, network); +} +``` + +## Handling contract responses + +Process transaction results and contract responses: + +```ts +async function executeAndMonitor() { + // Execute contract call + const transaction = await makeContractCall({ + contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + contractName: 'my-contract', + functionName: 'process', + functionArgs: [uintCV(100)], + senderKey: 'your-private-key', + network: new StacksTestnet(), + anchorMode: AnchorMode.Any, + }); + + const broadcastResponse = await broadcastTransaction(transaction, network); + const txId = broadcastResponse.txid; + + // Wait for confirmation + const txInfo = await waitForConfirmation(txId, network); + + // Check transaction result + if (txInfo.tx_status === 'success') { + console.log('Contract returned:', txInfo.tx_result); + // Parse the result based on expected return type + } else { + console.error('Transaction failed:', txInfo.tx_result); + } +} + +async function waitForConfirmation(txId: string, network: StacksNetwork) { + let attempts = 0; + const maxAttempts = 30; + + while (attempts < maxAttempts) { + const response = await fetch( + `${network.coreApiUrl}/extended/v1/tx/${txId}` + ); + const txInfo = await response.json(); + + if (txInfo.tx_status === 'success' || txInfo.tx_status === 'abort_by_response') { + return txInfo; + } + + await new Promise(resolve => setTimeout(resolve, 10000)); + attempts++; + } + + throw new Error('Transaction confirmation timeout'); +} +``` + +## Multi-step contract interactions + +{% stepper %} +{% step %} +#### Approve spending + +First, create and broadcast an approval transaction. + +```ts +const approveTx = await makeContractCall({ + contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + contractName: 'token', + functionName: 'approve', + functionArgs: [ + standardPrincipalCV('ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG'), + uintCV(1000000), + ], + senderKey: 'your-private-key', + network: new StacksTestnet(), + anchorMode: AnchorMode.Any, +}); + +const approveResult = await broadcastTransaction(approveTx, network); +await waitForConfirmation(approveResult.txid, network); +``` + +This step ensures the spender is authorized before subsequent actions. +{% endstep %} + +{% step %} +#### Execute swap after approval + +After the approval is confirmed, execute the swap transaction. + +```ts +const swapTx = await makeContractCall({ + contractAddress: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG', + contractName: 'dex', + functionName: 'swap-tokens', + functionArgs: [ + standardPrincipalCV('ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM'), + uintCV(1000000), + ], + senderKey: 'your-private-key', + network: new StacksTestnet(), + anchorMode: AnchorMode.Any, +}); + +return broadcastTransaction(swapTx, network); +``` + +This step performs the token swap that depends on the prior approval. +{% endstep %} +{% endstepper %} diff --git a/docs/build/stacks.js/contract-deployment.md b/docs/build/stacks.js/contract-deployment.md new file mode 100644 index 0000000000..57bb015df5 --- /dev/null +++ b/docs/build/stacks.js/contract-deployment.md @@ -0,0 +1,46 @@ +# Contract Deployment + +Deploy smart contracts to the Stacks blockchain. + +## Overview + +Contract deployment creates new smart contracts on the blockchain. Stacks.js provides tools to compile, deploy, and verify Clarity contracts programmatically. Deployments can be simple single contracts or complex multi-contract systems with dependencies. + +## Basic contract deployment + +Deploy a simple smart contract: + +{% code title="deploy.ts" %} +```ts +import { + makeContractDeploy, + broadcastTransaction, + AnchorMode +} from '@stacks/transactions'; +import { STACKS_TESTNET } from '@stacks/network'; +import { readFileSync } from 'fs'; + +async function deployContract() { + const network = new StacksTestnet(); + + // Read contract source code + const contractSource = readFileSync('./contracts/my-contract.clar', 'utf-8'); + + const txOptions = { + contractName: 'my-contract', + codeBody: contractSource, + senderKey: 'your-private-key', + network, + anchorMode: AnchorMode.Any, + fee: 10000, // Higher fee for deployment + }; + + const transaction = await makeContractDeploy(txOptions); + const broadcastResponse = await broadcastTransaction(transaction, network); + + console.log('Contract deployed!'); + console.log('Transaction ID:', broadcastResponse.txid); + console.log('Contract address:', `${senderAddress}.${txOptions.contractName}`); +} +``` +{% endcode %} diff --git a/docs/build/stacks.js/encoding-and-decoding.md b/docs/build/stacks.js/encoding-and-decoding.md new file mode 100644 index 0000000000..c2b1db8c39 --- /dev/null +++ b/docs/build/stacks.js/encoding-and-decoding.md @@ -0,0 +1,557 @@ +# Encoding & Decoding + +Convert between Clarity values and JavaScript types. + +## Overview + +Stacks uses Clarity values (CVs) to represent data in smart contracts. When building applications, you'll need to encode JavaScript values into CVs for contract calls and decode CVs back to JavaScript for display. This guide covers all CV types and conversion patterns. + +## Basic type conversions + +### Integers + +Convert between JavaScript numbers and Clarity integers: + +{% code title="integers.ts" %} +```ts +import { + intCV, + uintCV, + cvToValue, + cvToJSON +} from '@stacks/transactions'; + +// Encoding +const positiveInt = uintCV(42); // u42 +const negativeInt = intCV(-100); // -100 +const largeUint = uintCV(1000000); // u1000000 + +// Decoding +const jsValue = cvToValue(positiveInt); // 42 +const jsonValue = cvToJSON(positiveInt); // { type: 'uint', value: '42' } + +// Working with BigInt for large numbers +const bigNumber = uintCV(BigInt('123456789012345678901234567890')); +const decoded = cvToValue(bigNumber); // '123456789012345678901234567890' +``` +{% endcode %} + +### Booleans + +Simple true/false values: + +{% code title="booleans.ts" %} +```ts +import { trueCV, falseCV, boolCV } from '@stacks/transactions'; + +// Encoding +const clarityTrue = trueCV(); // true +const clarityFalse = falseCV(); // false +const fromBoolean = boolCV(true); // true + +// Decoding +const jsBoolean = cvToValue(clarityTrue); // true +``` +{% endcode %} + +### Strings + +Handle ASCII and UTF-8 strings: + +{% code title="strings.ts" %} +```ts +import { + stringAsciiCV, + stringUtf8CV, + cvToString +} from '@stacks/transactions'; + +// ASCII strings (limited character set) +const asciiString = stringAsciiCV('Hello World'); +const asciiDecoded = cvToValue(asciiString); // 'Hello World' + +// UTF-8 strings (full Unicode support) +const utf8String = stringUtf8CV('Hello 世界! 🌍'); +const utf8Decoded = cvToValue(utf8String); // 'Hello 世界! 🌍' + +// Direct string extraction +const directString = cvToString(utf8String); // 'Hello 世界! 🌍' +``` +{% endcode %} + +### Principals + +Encode Stacks addresses and contract principals: + +{% code title="principals.ts" %} +```ts +import { + standardPrincipalCV, + contractPrincipalCV, + cvToValue +} from '@stacks/transactions'; + +// Standard principal (user address) +const userPrincipal = standardPrincipalCV('SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY'); + +// Contract principal +const contractPrincipal = contractPrincipalCV( + 'SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY', + 'my-contract' +); + +// Decoding +const address = cvToValue(userPrincipal); +// 'SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY' + +const contract = cvToValue(contractPrincipal); +// 'SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY.my-contract' +``` +{% endcode %} + +## Complex type handling + +### Buffers + +Work with binary data: + +{% code title="buffers.ts" %} +```ts +import { + bufferCV, + bufferCVFromString, + cvToValue +} from '@stacks/transactions'; + +// From hex string +const hexBuffer = bufferCV(Buffer.from('deadbeef', 'hex')); + +// From UTF-8 string +const stringBuffer = bufferCVFromString('Hello Buffer'); + +// From byte array +const byteBuffer = bufferCV(new Uint8Array([1, 2, 3, 4])); + +// Decoding returns hex string +const decoded = cvToValue(hexBuffer); // '0xdeadbeef' + +// Get raw buffer +const rawBuffer = hexBuffer.buffer; // Buffer instance +``` +{% endcode %} + +### Optional values + +Handle Clarity's optional type: + +{% code title="optional.ts" %} +```ts +import { + someCV, + noneCV, + cvToValue +} from '@stacks/transactions'; + +// Some value (contains a value) +const someValue = someCV(uintCV(42)); // (some u42) +const someString = someCV(stringUtf8CV('hi')); // (some u"hi") + +// None value (no value) +const noneValue = noneCV(); // none + +// Decoding +const decodedSome = cvToValue(someValue); // 42 +const decodedNone = cvToValue(noneValue); // null + +// Check optional type +if (someValue.type === ClarityType.OptionalSome) { + const innerValue = cvToValue(someValue.value); +} +``` +{% endcode %} + +### Response values + +Handle contract response types: + +{% code title="response.ts" %} +```ts +import { + responseOkCV, + responseErrorCV, + cvToValue +} from '@stacks/transactions'; + +// Success response +const okResponse = responseOkCV(uintCV(100)); // (ok u100) +const okString = responseOkCV(stringUtf8CV('Success')); // (ok u"Success") + +// Error response +const errorResponse = responseErrorCV(uintCV(404)); // (err u404) +const errorMsg = responseErrorCV(stringUtf8CV('Not found')); // (err u"Not found") + +// Decoding and checking +const result = okResponse; +if (result.type === ClarityType.ResponseOk) { + console.log('Success:', cvToValue(result.value)); +} else { + console.log('Error:', cvToValue(result.value)); +} +``` +{% endcode %} + +### Tuples + +Create and decode structured data: + +{% code title="tuple.ts" %} +```ts +import { + tupleCV, + cvToValue, + cvToJSON +} from '@stacks/transactions'; + +// Create tuple +const userInfo = tupleCV({ + id: uintCV(1), + name: stringUtf8CV('Alice'), + balance: uintCV(1000000), + active: trueCV(), + metadata: tupleCV({ + created: uintCV(Date.now()), + tags: listCV([stringAsciiCV('user'), stringAsciiCV('premium')]) + }) +}); + +// Decode to JavaScript object +const decoded = cvToValue(userInfo); +// { +// id: 1, +// name: 'Alice', +// balance: 1000000, +// active: true, +// metadata: { +// created: 1234567890, +// tags: ['user', 'premium'] +// } +// } + +// Access tuple fields +const nameCV = userInfo.data.name; +const name = cvToValue(nameCV); // 'Alice' +``` +{% endcode %} + +### Lists + +Work with arrays of values: + +{% code title="lists.ts" %} +```ts +import { + listCV, + cvToValue +} from '@stacks/transactions'; + +// List of same type +const numbers = listCV([uintCV(1), uintCV(2), uintCV(3)]); +const strings = listCV([ + stringUtf8CV('apple'), + stringUtf8CV('banana'), + stringUtf8CV('cherry') +]); + +// List of tuples (common pattern) +const users = listCV([ + tupleCV({ id: uintCV(1), name: stringUtf8CV('Alice') }), + tupleCV({ id: uintCV(2), name: stringUtf8CV('Bob') }), +]); + +// Decoding +const decodedNumbers = cvToValue(numbers); // [1, 2, 3] +const decodedUsers = cvToValue(users); +// [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }] + +// Iterate over list +numbers.list.forEach((cv, index) => { + console.log(`Item ${index}:`, cvToValue(cv)); +}); +``` +{% endcode %} + +## Advanced encoding patterns + +### Dynamic type encoding + +Build encoders for runtime values: + +{% code title="dynamic-encoder.ts" %} +```ts +function encodeValue(value: any): ClarityValue { + if (typeof value === 'number') { + return value >= 0 ? uintCV(value) : intCV(value); + } else if (typeof value === 'string') { + // Check if valid ASCII + if (/^[\x00-\x7F]*$/.test(value)) { + return stringAsciiCV(value); + } + return stringUtf8CV(value); + } else if (typeof value === 'boolean') { + return boolCV(value); + } else if (value === null || value === undefined) { + return noneCV(); + } else if (Array.isArray(value)) { + return listCV(value.map(encodeValue)); + } else if (typeof value === 'object') { + const tupleData: { [key: string]: ClarityValue } = {}; + for (const [key, val] of Object.entries(value)) { + tupleData[key] = encodeValue(val); + } + return tupleCV(tupleData); + } + + throw new Error(`Cannot encode value: ${value}`); +} + +// Usage +const encoded = encodeValue({ + name: 'Alice', + age: 30, + tags: ['user', 'admin'], + active: true +}); +``` +{% endcode %} + +### Type-safe decoding + +Create decoders with type validation: + +{% code title="decode-user.ts" %} +```ts +interface UserData { + id: number; + name: string; + balance: number; + active: boolean; +} + +function decodeUser(cv: ClarityValue): UserData { + if (cv.type !== ClarityType.Tuple) { + throw new Error('Expected tuple'); + } + + const data = cv.data; + + // Validate and extract each field + if (!data.id || data.id.type !== ClarityType.UInt) { + throw new Error('Invalid id field'); + } + + if (!data.name || ( + data.name.type !== ClarityType.StringASCII && + data.name.type !== ClarityType.StringUTF8 + )) { + throw new Error('Invalid name field'); + } + + return { + id: Number(cvToValue(data.id)), + name: cvToString(data.name), + balance: Number(cvToValue(data.balance)), + active: cvToValue(data.active) as boolean, + }; +} +``` +{% endcode %} + +### Batch encoding utilities + +Encode multiple values efficiently: + +{% code title="clarity-encoder.ts" %} +```ts +class ClarityEncoder { + static encodeArray( + items: T[], + encoder: (item: T) => ClarityValue + ): ClarityValue { + return listCV(items.map(encoder)); + } + + static encodeTuple>( + obj: T, + schema: { [K in keyof T]: (value: T[K]) => ClarityValue } + ): TupleCV { + const tupleData: { [key: string]: ClarityValue } = {}; + + for (const [key, encoder] of Object.entries(schema)) { + tupleData[key] = encoder(obj[key as keyof T]); + } + + return tupleCV(tupleData); + } + + static encodeOptional( + value: T | null | undefined, + encoder: (value: T) => ClarityValue + ): OptionalCV { + if (value === null || value === undefined) { + return noneCV(); + } + return someCV(encoder(value)); + } +} + +// Usage +const users = [ + { id: 1, name: 'Alice', balance: 1000 }, + { id: 2, name: 'Bob', balance: 2000 }, +]; + +const encoded = ClarityEncoder.encodeArray(users, user => + ClarityEncoder.encodeTuple(user, { + id: (id) => uintCV(id), + name: (name) => stringUtf8CV(name), + balance: (balance) => uintCV(balance), + }) +); +``` +{% endcode %} + +## Serialization and deserialization + +Work with serialized Clarity values: + +{% code title="serialization.ts" %} +```ts +import { + serializeCV, + deserializeCV, + cvToHex, + hexToCV +} from '@stacks/transactions'; + +// Serialize to buffer +const cv = tupleCV({ amount: uintCV(1000), memo: stringUtf8CV('Payment') }); +const serialized = serializeCV(cv); // Buffer + +// Convert to hex for storage/transport +const hex = cvToHex(cv); // '0x0c00000002046d656d6f...' + +// Deserialize from hex +const deserialized = hexToCV(hex); +const value = cvToValue(deserialized); // { amount: 1000, memo: 'Payment' } + +// Work with raw buffers +const buffer = Buffer.from(hex, 'hex'); +const fromBuffer = deserializeCV(buffer); +``` +{% endcode %} + +## Common conversion patterns + +### Contract call arguments + +Prepare arguments for contract calls: + +{% code title="prepare-args.ts" %} +```ts +function prepareTransferArgs( + recipient: string, + amount: number, + memo?: string +): ClarityValue[] { + const args = [ + standardPrincipalCV(recipient), + uintCV(amount), + ]; + + if (memo) { + args.push(someCV(stringUtf8CV(memo))); + } else { + args.push(noneCV()); + } + + return args; +} + +// Usage in contract call +const functionArgs = prepareTransferArgs( + 'SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY', + 1000000, + 'Monthly payment' +); +``` +{% endcode %} + +### Response handling + +Process contract responses: + +{% code title="handle-response.ts" %} +```ts +function handleContractResponse(response: ClarityValue): { + success: boolean; + data: any; + error?: string; +} { + if (response.type === ClarityType.ResponseOk) { + return { + success: true, + data: cvToValue(response.value), + }; + } else if (response.type === ClarityType.ResponseErr) { + const errorValue = cvToValue(response.value); + return { + success: false, + data: null, + error: typeof errorValue === 'string' ? errorValue : `Error: ${errorValue}`, + }; + } + + throw new Error('Invalid response type'); +} +``` +{% endcode %} + +## Best practices + +{% hint style="info" %} +* Validate types: Always check CV types before decoding +* Handle edge cases: Consider null, undefined, and empty values +* Use appropriate string types: ASCII for simple text, UTF-8 for international +* Preserve precision: Use BigInt for large numbers +* Type narrowing: Use TypeScript type guards for safety +{% endhint %} + +## Common mistakes + +
        + +String type confusion + +```ts +// Bad: Using ASCII for Unicode +const bad = stringAsciiCV('Hello 世界'); // Will throw error + +// Good: Use UTF-8 for Unicode +const good = stringUtf8CV('Hello 世界'); +``` + +
        + +
        + +Number overflow + +```ts +// Bad: JavaScript number too large +const bad = uintCV(Number.MAX_SAFE_INTEGER + 1); // Precision loss + +// Good: Use BigInt +const good = uintCV(BigInt('9007199254740992')); +``` + +
        diff --git a/docs/build/stacks.js/network-configuration.md b/docs/build/stacks.js/network-configuration.md new file mode 100644 index 0000000000..eba1caae70 --- /dev/null +++ b/docs/build/stacks.js/network-configuration.md @@ -0,0 +1,544 @@ +# Network Configuration + +Configure and customize Stacks network connections. + +## Overview + +Stacks.js supports multiple networks—mainnet for production, testnet for development, and custom networks for local testing. Proper network configuration ensures your app connects to the right blockchain instance with optimal settings. + +## Basic network setup + +Configure standard networks: + +```ts +import { + StacksMainnet, + StacksTestnet, + StacksMocknet +} from '@stacks/network'; + +// Production network +const mainnet = new StacksMainnet(); +console.log('Mainnet API:', mainnet.coreApiUrl); +// https://api.hiro.so + +// Test network +const testnet = new StacksTestnet(); +console.log('Testnet API:', testnet.coreApiUrl); +// https://api.testnet.hiro.so + +// Local development network +const mocknet = new StacksMocknet(); +console.log('Mocknet API:', mocknet.coreApiUrl); +// http://localhost:3999 +``` + +## Custom network configuration + +Create networks with custom endpoints: + +```ts +import { StacksNetwork } from '@stacks/network'; + +// Custom mainnet configuration +const customMainnet = new StacksMainnet({ + url: 'https://my-custom-node.com', + fetchFn: fetch, // Custom fetch implementation +}); + +// Custom testnet with specific endpoints +const customTestnet = new StacksTestnet({ + url: 'https://my-testnet-node.com:3999', +}); + +// Fully custom network +class CustomNetwork extends StacksNetwork { + constructor() { + super({ + url: 'https://custom-stacks-node.com', + networkType: 'mainnet', // or 'testnet', 'mocknet' + }); + } + + // Override methods as needed + getBroadcastApiUrl() { + return `${this.coreApiUrl}/custom/broadcast`; + } +} +``` + +## Environment-based configuration + +Manage networks across environments: + +```ts +// config/network.ts +import { + StacksNetwork, + StacksMainnet, + StacksTestnet, + StacksMocknet +} from '@stacks/network'; + +interface NetworkConfig { + network: StacksNetwork; + apiUrl: string; + wsUrl?: string; + explorerUrl: string; + faucetUrl?: string; +} + +const configs: Record = { + production: { + network: new StacksMainnet(), + apiUrl: 'https://api.hiro.so', + wsUrl: 'wss://api.hiro.so', + explorerUrl: 'https://explorer.hiro.so', + }, + staging: { + network: new StacksTestnet(), + apiUrl: 'https://api.testnet.hiro.so', + wsUrl: 'wss://api.testnet.hiro.so', + explorerUrl: 'https://explorer.hiro.so/?chain=testnet', + faucetUrl: 'https://api.testnet.hiro.so/extended/v1/faucets/stx', + }, + development: { + network: new StacksMocknet(), + apiUrl: 'http://localhost:3999', + explorerUrl: 'http://localhost:8000', + }, +}; + +export function getNetworkConfig(): NetworkConfig { + const env = process.env.NODE_ENV || 'development'; + return configs[env]; +} + +// Usage +const { network, apiUrl } = getNetworkConfig(); +``` + +## Network detection and validation + +Detect and validate network connections: + +```ts +async function detectNetwork(url: string): Promise<'mainnet' | 'testnet' | 'unknown'> { + try { + const response = await fetch(`${url}/v2/info`); + const info = await response.json(); + + // Check network ID + if (info.network_id === 1) { + return 'mainnet'; + } else if (info.network_id === 2147483648) { + return 'testnet'; + } + + return 'unknown'; + } catch (error) { + console.error('Failed to detect network:', error); + return 'unknown'; + } +} + +// Validate network matches expectation +async function validateNetwork( + network: StacksNetwork, + expected: 'mainnet' | 'testnet' +): Promise { + const detected = await detectNetwork(network.coreApiUrl); + + if (detected !== expected) { + console.warn(`Network mismatch! Expected ${expected}, got ${detected}`); + return false; + } + + return true; +} +``` + +## Advanced network features + +### Custom headers and authentication + +Add authentication or custom headers: + +```ts +class AuthenticatedNetwork extends StacksMainnet { + private apiKey: string; + + constructor(apiKey: string) { + super(); + this.apiKey = apiKey; + } + + createFetchFn(): FetchFn { + return (url: string, init?: RequestInit) => { + const headers = { + ...init?.headers, + 'x-api-key': this.apiKey, + 'x-client-version': '1.0.0', + }; + + return fetch(url, { ...init, headers }); + }; + } +} + +// Usage +const network = new AuthenticatedNetwork(process.env.API_KEY!); +``` + +### Request retry and timeout + +Implement resilient network requests: + +```ts +class ResilientNetwork extends StacksTestnet { + private maxRetries = 3; + private timeout = 30000; // 30 seconds + + createFetchFn(): FetchFn { + return async (url: string, init?: RequestInit) => { + for (let attempt = 0; attempt < this.maxRetries; attempt++) { + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), this.timeout); + + const response = await fetch(url, { + ...init, + signal: controller.signal, + }); + + clearTimeout(timeoutId); + + if (!response.ok && attempt < this.maxRetries - 1) { + // Retry on server errors + if (response.status >= 500) { + await this.delay(1000 * Math.pow(2, attempt)); + continue; + } + } + + return response; + } catch (error) { + if (attempt === this.maxRetries - 1) throw error; + await this.delay(1000 * Math.pow(2, attempt)); + } + } + + throw new Error('Max retries exceeded'); + }; + } + + private delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } +} +``` + +## Network-specific configurations + +### Configure by chain ID + +Set up network based on chain identifier: + +```ts +function getNetworkByChainId(chainId: number): StacksNetwork { + switch (chainId) { + case 1: // Mainnet + return new StacksMainnet(); + case 2147483648: // Testnet + return new StacksTestnet(); + default: + throw new Error(`Unknown chain ID: ${chainId}`); + } +} + +// Dynamic network from wallet +async function getNetworkFromWallet(): Promise { + const userData = userSession.loadUserData(); + const address = userData.profile.stxAddress.testnet; + + // Determine network from address prefix + if (address.startsWith('SP') || address.startsWith('SM')) { + return new StacksMainnet(); + } else if (address.startsWith('ST') || address.startsWith('SN')) { + return new StacksTestnet(); + } + + throw new Error('Unable to determine network from address'); +} +``` + +### Multi-network support + +Support multiple networks simultaneously: + +```ts +class NetworkManager { + private networks: Map = new Map(); + private currentNetwork: string = 'testnet'; + + constructor() { + this.networks.set('mainnet', new StacksMainnet()); + this.networks.set('testnet', new StacksTestnet()); + this.networks.set('devnet', new StacksMocknet()); + } + + getNetwork(name?: string): StacksNetwork { + const networkName = name || this.currentNetwork; + const network = this.networks.get(networkName); + + if (!network) { + throw new Error(`Unknown network: ${networkName}`); + } + + return network; + } + + setCurrentNetwork(name: string): void { + if (!this.networks.has(name)) { + throw new Error(`Unknown network: ${name}`); + } + this.currentNetwork = name; + } + + addCustomNetwork(name: string, url: string): void { + const network = new StacksNetwork({ url }); + this.networks.set(name, network); + } +} + +// Usage +const manager = new NetworkManager(); +const mainnet = manager.getNetwork('mainnet'); +manager.setCurrentNetwork('mainnet'); +``` + +## Network connection monitoring + +Monitor network health and status: + +```ts +class NetworkMonitor { + private network: StacksNetwork; + private isHealthy = true; + private listeners: Set<(healthy: boolean) => void> = new Set(); + + constructor(network: StacksNetwork) { + this.network = network; + this.startMonitoring(); + } + + private async startMonitoring() { + setInterval(async () => { + try { + const response = await fetch( + `${this.network.coreApiUrl}/v2/info`, + { signal: AbortSignal.timeout(5000) } + ); + + const wasHealthy = this.isHealthy; + this.isHealthy = response.ok; + + if (wasHealthy !== this.isHealthy) { + this.notifyListeners(); + } + } catch (error) { + const wasHealthy = this.isHealthy; + this.isHealthy = false; + + if (wasHealthy) { + this.notifyListeners(); + } + } + }, 30000); // Check every 30 seconds + } + + onHealthChange(callback: (healthy: boolean) => void): () => void { + this.listeners.add(callback); + return () => this.listeners.delete(callback); + } + + private notifyListeners() { + this.listeners.forEach(callback => callback(this.isHealthy)); + } + + async waitForHealth(timeout = 60000): Promise { + const start = Date.now(); + + while (!this.isHealthy && Date.now() - start < timeout) { + await new Promise(resolve => setTimeout(resolve, 1000)); + } + + if (!this.isHealthy) { + throw new Error('Network unhealthy after timeout'); + } + } +} +``` + +## WebSocket configuration + +Set up real-time connections: + +```ts +interface WebSocketConfig { + url: string; + reconnectInterval: number; + maxReconnectAttempts: number; +} + +class StacksWebSocketClient { + private ws: WebSocket | null = null; + private config: WebSocketConfig; + private reconnectAttempts = 0; + + constructor(network: StacksNetwork) { + this.config = { + url: this.getWebSocketUrl(network), + reconnectInterval: 5000, + maxReconnectAttempts: 10, + }; + } + + private getWebSocketUrl(network: StacksNetwork): string { + const apiUrl = network.coreApiUrl; + return apiUrl.replace('https://', 'wss://').replace('http://', 'ws://'); + } + + connect(): void { + this.ws = new WebSocket(this.config.url); + + this.ws.onopen = () => { + console.log('WebSocket connected'); + this.reconnectAttempts = 0; + }; + + this.ws.onclose = () => { + this.handleReconnect(); + }; + + this.ws.onerror = (error) => { + console.error('WebSocket error:', error); + }; + } + + private handleReconnect(): void { + if (this.reconnectAttempts < this.config.maxReconnectAttempts) { + setTimeout(() => { + this.reconnectAttempts++; + this.connect(); + }, this.config.reconnectInterval); + } + } + + subscribe(event: string, callback: (data: any) => void): void { + if (!this.ws) throw new Error('WebSocket not connected'); + + this.ws.send(JSON.stringify({ + method: 'subscribe', + params: { event } + })); + + this.ws.onmessage = (message) => { + const data = JSON.parse(message.data); + if (data.event === event) { + callback(data); + } + }; + } +} +``` + +## Testing with different networks + +Set up tests across networks: + +```ts +import { describe, it, beforeEach } from 'vitest'; + +describe('Cross-network tests', () => { + const networks = [ + { name: 'mainnet', network: new StacksMainnet() }, + { name: 'testnet', network: new StacksTestnet() }, + ]; + + networks.forEach(({ name, network }) => { + describe(`${name} tests`, () => { + it('should connect to network', async () => { + const response = await fetch(`${network.coreApiUrl}/v2/info`); + expect(response.ok).toBe(true); + }); + + it('should have correct chain ID', async () => { + const response = await fetch(`${network.coreApiUrl}/v2/info`); + const info = await response.json(); + + if (name === 'mainnet') { + expect(info.network_id).toBe(1); + } else { + expect(info.network_id).toBe(2147483648); + } + }); + }); + }); +}); +``` + +## Best practices + +* Use environment variables: Never hardcode network URLs +* Implement retry logic: Networks can be temporarily unavailable +* Monitor connection health: Detect and handle network issues +* Cache network info: Reduce redundant API calls +* Validate network type: Ensure you're on the expected network + +## Common issues + +
        + +CORS errors + +```ts +// Configure proxy for development +const devNetwork = new StacksTestnet({ + url: '/api', // Proxy through your dev server +}); + +// Or use CORS-enabled endpoints +const corsNetwork = new StacksTestnet({ + url: 'https://api.testnet.hiro.so', +}); +``` + +
        + +
        + +Timeout handling + +```ts +// Add timeout to all requests +class TimeoutNetwork extends StacksMainnet { + async fetchWithTimeout(url: string, timeout = 30000): Promise { + const controller = new AbortController(); + const id = setTimeout(() => controller.abort(), timeout); + + try { + const response = await fetch(url, { + signal: controller.signal, + }); + clearTimeout(id); + return response; + } catch (error) { + clearTimeout(id); + throw new Error(`Request timeout: ${url}`); + } + } +} +``` + +
        diff --git a/docs/build/stacks.js/networks.md b/docs/build/stacks.js/networks.md new file mode 100644 index 0000000000..c2faabd293 --- /dev/null +++ b/docs/build/stacks.js/networks.md @@ -0,0 +1,44 @@ +# Networks + +Typically, we speak of `mainnet` and `testnet` as the networks of Stacks. Most wallets are configured to `mainnet` by default—this is the production environment, the actual blockchain that holds real STX tokens. + +As the name suggests, `testnet` is a public network for testing. It's a separate blockchain state that holds test tokens, which have no value. + +For completeness we also mention `devnet`. This isn't "one" network, but how developers refer to ephemeral local networks used for testing. It is the same as `testnet`, but for local development. [Learn more](../../build/clarinet/local-blockchain-development.md). + +## Setting the network + +Most Stacks.js functions accept a `network` parameter or an optional last argument. + +The `network` type is a string, and can be one of: + +* `'mainnet'` (default) +* `'testnet'` +* `'devnet'` +* `'mocknet'` (alias of `devnet`) + +### Examples + +Network in transaction signing: + +{% code title="transaction.ts" %} +```ts +const tx = makeSTXTokenTransfer({ + // ... + network: 'testnet', +}); +``` +{% endcode %} + +Network in address derivation: + +{% code title="address.ts" %} +```ts +const address = privateKeyToAddress(privateKey, 'devnet'); +// ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP +``` +{% endcode %} + +{% hint style="info" %} +For more advanced workflows, pass a custom network configuration object. See the `@stacks/network` package for details. +{% endhint %} diff --git a/docs/build/stacks.js/overview.md b/docs/build/stacks.js/overview.md new file mode 100644 index 0000000000..6b41402101 --- /dev/null +++ b/docs/build/stacks.js/overview.md @@ -0,0 +1,84 @@ +# Overview + +

        source: Hiro blog

        + +## Overview + +Stacks.js is a collection of JavaScript libraries that enable you to build web applications on the Stacks blockchain. From wallet authentication to smart contract interactions. + +{% hint style="success" %} +For the latest releases and versions of Stacks.js packages, check out its open-source repo [here](https://github.com/stx-labs/stacks.js). +{% endhint %} + +## Key features + +* **Transaction construction** - Build and broadcast all transaction types with type-safe APIs +* **Smart contract interaction** - Deploy contracts and call functions with automatic encoding +* **Wallet integration** - Connect to Leather, Xverse, and other Stacks wallets seamlessly +* **Post-conditions** - Protect users with built-in asset transfer validations + +## Installation + +Stacks.js is separated into focused packages published under the `@stacks` scope. Install only what you need: + +{% tabs %} +{% tab title="Transactions" %} +{% code title="terminal" %} +```bash +npm install @stacks/transactions +``` +{% endcode %} +{% endtab %} + +{% tab title="Wallet Connections" %} +{% code title="terminal" %} +```bash +npm install @stacks/connect +``` +{% endcode %} +{% endtab %} + +{% tab title="Network Config" %} +{% code title="terminal" %} +```bash +npm install @stacks/network +``` +{% endcode %} +{% endtab %} + +{% tab title="Common Utils" %} +{% code title="terminal" %} +```bash +npm install @stacks/common +``` +{% endcode %} +{% endtab %} +{% endtabs %} + +Other available packages include: + +* `@stacks/api` +* `@stacks/auth` +* `@stacks/encryption` +* `@stacks/network` +* `@stacks/stacking` +* `@stacks/transactions` +* `@stacks/bns` +* `@stacks/common` +* `@stacks/profile` +* `@stacks/storage` +* `@stacks/wallet-sdk` + +*** + +{% hint style="info" %} +Need help building with Stacks.js? + +Reach out to us on the **#stacks-js** channel on [Discord](https://stacks.chat/) under the Developer Tools section. +{% endhint %} + +*** + +### Additional Resources + +* \[[stacks.js.org](https://stacks.js.org/)] For a complete list of definitions on types, methods, classes, & etc. diff --git a/docs/build/stacks.js/private-keys.md b/docs/build/stacks.js/private-keys.md new file mode 100644 index 0000000000..11122b87a9 --- /dev/null +++ b/docs/build/stacks.js/private-keys.md @@ -0,0 +1,184 @@ +# Private Keys + +Stacks applications can work with two different account types: web wallets (like Hiro Wallet or Xverse) that users control, or local accounts you manage the private keys directly. + +## Web wallets (user-controlled) + +Most users interact with Stacks apps through web wallets, where the wallet handles all private key management and transaction signing. + +```ts +import { connect } from '@stacks/connect'; + +// Users connect their wallet +const response = await connect(); +console.log('Connected addresses:', response.addresses); + +// The wallet handles all cryptographic operations +// when signing transactions or messages +``` + +Use web wallets when building user-facing applications where users should maintain control of their keys. + +## Local accounts (application-controlled) + +Local accounts give your application direct control over private keys, enabling programmatic transaction signing without user interaction. + +```ts +import { makeSTXTokenTransfer } from '@stacks/transactions'; + +// Your application controls the private key +const privateKey = 'your-private-key-here'; + +const txOptions = { + recipient: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + amount: 1000000n, + senderKey: privateKey, // Direct private key usage + network: 'testnet', +}; + +const transaction = await makeSTXTokenTransfer(txOptions); +// Transaction is signed programmatically +``` + +Use local accounts for backend services, automated systems, or development tools that need to sign transactions without user interaction. + +## Working with private keys + +When building applications that use local accounts, you'll need to generate and manage private keys securely. + +### Generating random private keys + +Create a new private key for one-time use or testing purposes. + +```ts +import { randomPrivateKey } from '@stacks/transactions'; + +function generateNewAccount() { + const privateKey = randomPrivateKey(); + console.log('Private key:', privateKey); + // 'f5a31c1268a1e37d4edaa05c7d11183c5fbf...' + + // IMPORTANT: Store this securely! + // Anyone with this key can control the account + return privateKey; +} +``` + +{% hint style="warning" %} +Private keys in Stacks are 256-bit numbers, typically represented as 64-character hexadecimal strings. Anyone with the private key can control the account—store keys securely. +{% endhint %} + +## Private key formats + +Stacks.js supports multiple private key formats for different use cases. + +```ts +import { PrivateKey } from '@stacks/transactions'; + +// Hex string format (most common) +const hexKey = 'f5a31c1268a1e37d4edaa05c7d11183c5fbf...'; + +// Compressed format with suffix +const compressedKey = 'f5a31c1268a1e37d4edaa05c7d11183c5fbf...01'; + +// Create from raw bytes +const bytes = new Uint8Array(32); // 32 bytes = 256 bits +crypto.getRandomValues(bytes); +const privateKeyFromBytes = PrivateKey.fromBytes(bytes); +``` + +The compressed format includes a suffix byte (01) that indicates the key should use compressed public key encoding. + +## Wallet generation with seed phrases + +For better security and recoverability, use hierarchical deterministic (HD) wallets based on seed phrases. + +{% stepper %} +{% step %} +#### Generate a seed phrase + +Create a new 24-word mnemonic seed phrase that can regenerate all wallet accounts. + +```ts +import { generateSecretKey } from '@stacks/wallet-sdk'; + +function createNewWallet() { + const secretKey = generateSecretKey(); + console.log('Seed phrase:', secretKey); + // "warrior volume sport ... figure cake since" + + // Users should write this down and store it securely + // This phrase can regenerate all accounts in the wallet + return secretKey; +} +``` + +{% hint style="warning" %} +Seed phrases provide a human-readable backup that can restore an entire wallet hierarchy. Users should write this down and store it securely. +{% endhint %} +{% endstep %} + +{% step %} +#### Create wallet from seed phrase + +Generate a complete wallet structure from a seed phrase, including multiple accounts. + +```ts +import { generateWallet, generateSecretKey } from '@stacks/wallet-sdk'; + +async function setupWallet() { + // Use existing seed phrase or generate new one + const seedPhrase = generateSecretKey(); + + const wallet = await generateWallet({ + secretKey: seedPhrase, + password: 'optional-encryption-password', + }); + + // Access the first account + const account = wallet.accounts[0]; + console.log('STX address:', account.address); + console.log('STX private key:', account.stxPrivateKey); + console.log('Data private key:', account.dataPrivateKey); + + return wallet; +} +``` + +Each wallet can contain multiple accounts, all derived from the same seed phrase but with different private keys. +{% endstep %} + +{% step %} +#### Managing multiple accounts + +HD wallets support multiple accounts from a single seed phrase, useful for organizing funds or separating concerns. + +```ts +import { generateNewAccount, generateWallet, generateSecretKey } from '@stacks/wallet-sdk'; + +async function createMultipleAccounts() { + const seedPhrase = generateSecretKey(); + + let wallet = await generateWallet({ + secretKey: seedPhrase, + password: 'my-password', + }); + + console.log('Accounts:', wallet.accounts.length); // 1 (default) + + // Add more accounts + wallet = generateNewAccount(wallet); + wallet = generateNewAccount(wallet); + + console.log('Accounts:', wallet.accounts.length); // 3 + + // Each account has its own keys and address + wallet.accounts.forEach((account, index) => { + console.log(`Account ${index}:`, account.address); + }); +} +``` + +All accounts can be regenerated from the original seed phrase, making backup simple while maintaining separate addresses. +{% endstep %} +{% endstepper %} diff --git a/docs/build/stacks.js/react-native-integration.md b/docs/build/stacks.js/react-native-integration.md new file mode 100644 index 0000000000..45dfe7671b --- /dev/null +++ b/docs/build/stacks.js/react-native-integration.md @@ -0,0 +1,289 @@ +# React Native Integration + +Stacks.js can be integrated into React Native applications to bring blockchain functionality to mobile devices. This tutorial walks you through setting up a React Native project with Expo and configuring it to work with Stacks.js libraries. + +## Objectives + +* Set up an Expo project configured for Stacks.js +* Install and configure necessary polyfills for React Native +* Generate wallets and sign transactions in a mobile app +* Handle React Native's JavaScript environment limitations +* Build a working Stacks mobile application + +## Prerequisites + +* Node.js and npm installed on your development machine +* Basic knowledge of React Native and Expo +* Familiarity with Stacks.js concepts +* iOS or Android device or simulator for testing + +## Set up the Expo project + +Start by creating a new Expo project. The latest version of Expo provides the best compatibility with Stacks.js polyfills. + +```bash +npx create-expo-app@latest my-stacks-app +cd my-stacks-app +``` + +The boilerplate project includes everything needed to start building. Test the initial setup by running the development server. + +```bash +npm start +``` + +Connect your mobile device using the Expo Go app and scan the QR code to verify the base project works correctly. + +## Install necessary dependencies + +React Native's JavaScript environment lacks certain Node.js and browser APIs that Stacks.js requires. Install the core Stacks libraries along with necessary polyfills. + +```bash +npm install @stacks/transactions @stacks/wallet-sdk +``` + +Install the polyfill dependencies as dev dependencies to handle missing APIs. + +```bash +npm install --save-dev buffer process react-native-get-random-values \ + text-encoding readable-stream crypto-browserify @peculiar/webcrypto +``` + +These polyfills provide: + +* `buffer` and `process` for Node.js globals +* `react-native-get-random-values` for crypto random values +* `text-encoding` for `TextEncoder` and `TextDecoder` +* `crypto-browserify` and `@peculiar/webcrypto` for cryptographic functions + +## Configure Metro bundler + +Metro bundler needs configuration to properly resolve Node.js modules. Create a custom Metro configuration file. + +```bash +npx expo customize metro.config.js +``` + +Update `metro.config.js` to map Node.js modules to their React Native-compatible versions. + +```ts +const { getDefaultConfig } = require('expo/metro-config'); + +const config = getDefaultConfig(__dirname); + +config.resolver.extraNodeModules = { + stream: require.resolve('readable-stream'), + crypto: require.resolve('crypto-browserify'), +}; + +module.exports = config; +``` + +This configuration ensures that when Stacks.js requests Node.js modules, Metro provides the appropriate polyfills. + +## Set up global polyfills + +Create a polyfill system to make browser and Node.js APIs available in React Native. This requires modifying the app's entry point. + +### Create the polyfill file + +Create `polyfill.js` to initialize the required global objects. + +```ts +import { Buffer } from 'buffer/'; +import process from 'process'; +import 'react-native-get-random-values'; +import { TextDecoder, TextEncoder } from 'text-encoding'; + +global.process = process; +global.Buffer = Buffer; +global.TextEncoder = TextEncoder; +global.TextDecoder = TextDecoder; +``` + +### Create a custom entry point + +Create `index.js` so the app loads polyfills before the UI renders. + +```ts +import './polyfill'; +import { Crypto } from '@peculiar/webcrypto'; + +Object.assign(global.crypto, new Crypto()); + +import 'expo-router/entry'; +``` + +{% hint style="warning" %} +Runtime initialization errors: Polyfills must be loaded in separate files as shown. Loading them in the same file can cause runtime initialization errors. +{% endhint %} + +### Update package.json + +Point the app to use the new entry point. + +```json +{ + "main": "index.js" +} +``` + +## Implement Stacks functionality + +With the environment configured, you can now use Stacks.js in your React Native components. Update the main screen to demonstrate wallet generation and transaction signing. + +### Import Stacks.js modules + +Edit `app/(tabs)/index.tsx` to import the necessary Stacks.js functions. + +```ts +import { + TransactionVersion, + getAddressFromPrivateKey, + makeSTXTokenTransfer, +} from '@stacks/transactions'; +import { Wallet, generateSecretKey, generateWallet } from '@stacks/wallet-sdk'; +import { useState } from 'react'; +import { Button } from 'react-native'; +``` + +### Set up component state + +Create state variables to manage wallet data and user feedback. + +```ts +export default function HomeScreen() { + const [mnemonic, setMnemonic] = useState('Press button to generate'); + const [wallet, setWallet] = useState(null); + const [log, setLog] = useState(''); + + // Component implementation continues... +} +``` + +### Generate a wallet and sign a transaction + +Implement the core functionality to create a wallet and sign a transaction. + +```ts +const generate = async () => { + try { + const mnemonic = generateSecretKey(); + setMnemonic(mnemonic); + + const wallet = await generateWallet({ + secretKey: mnemonic, + password: '', + }); + setWallet(wallet); + + const txOptions = { + amount: 1000, + anchorMode: 'any' as const, + recipient: 'SP3W993D3BRDYB284CY3SBFDEGTC5XEDJPDEA21CN', + senderKey: wallet.accounts[0].stxPrivateKey, + fee: 300, + network: 'testnet' as const, + nonce: 0, + }; + + const transaction = await makeSTXTokenTransfer(txOptions); + setLog('Transaction signed successfully'); + } catch (error) { + setLog(`Error: ${error.message}`); + } +}; +``` + +### Build the user interface + +Show wallet information and trigger wallet generation from the UI. + +```ts +return ( + + Stacks Wallet Demo + + + Seed Phrase + {mnemonic} +