2020-01-17 22:18:10 +00:00
<!DOCTYPE html>
2020-03-20 18:02:02 +00:00
< html lang = "en" dir = "ltr" >
2020-01-17 22:18:10 +00:00
< head >
2020-03-20 18:02:02 +00:00
< meta charset = "UTF-8" >
< title > Getting Started | SUBSPACE< / title >
< meta name = "description" content = "Reactive ÐApp Development" >
< meta http-equiv = "X-UA-CompatibleGetting Started | SUBSPACE" content = "IE=Edge,chrome=1" >
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
<!-- Canonical links -->
< link rel = "canonical" href = "http://localhost:4000/getting-started.html" >
<!-- Icon -->
< meta name = "msapplication-TileColor" content = "#080E1A" >
< link rel = "icon" type = "image/png" href = "/images/favicon.png" sizes = "32x32" / >
< link rel = "apple-touch-icon" sizes = "76x76" href = "/images/apple-touch-icon-60x60-precomposed.png" >
< link rel = "apple-touch-icon" sizes = "76x76" href = "/images/apple-touch-icon-76x76-precomposed.png" >
< link rel = "apple-touch-icon" sizes = "120x120" href = "/images/apple-touch-icon-120x120-precomposed.png" >
< link rel = "apple-touch-icon" sizes = "152x152" href = "/images/apple-touch-icon-152x152-precomposed.png" >
< link rel = "apple-touch-icon" sizes = "180x180" href = "/images/apple-touch-icon-precomposed.png" >
< link rel = "apple-touch-icon" href = "/images/apple-touch-icon-precomposed.png" >
<!-- CSS -->
< link rel = "stylesheet" href = "/css/application.css" >
<!-- endbuild -->
< link href = "https://fonts.googleapis.com/css?family=Roboto:400,700" rel = "stylesheet" >
< meta property = "og:image" content = "/img/share.png?v=0.0.5" / >
< link rel = "stylesheet" href = "//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.4/styles/dracula.min.css" >
< meta name = "generator" content = "Hexo 4.2.0" > < / head >
< body >
2020-01-17 22:18:10 +00:00
< body >
2020-03-23 17:04:33 +00:00
< div id = "stars" > < / div >
< div id = "stars2" > < / div >
< header role = "banner" class = "o-header" >
2020-03-20 18:02:02 +00:00
< div class = "o-header__container c-spotlightbox" >
< div class = "o-constrained" >
< div class = "o-header__top js-header" >
< div > < a href = "/" class = "a-logo" > Keycard< / a > < / div >
< nav role = "navigation" class = "o-navigation c-navigation" >
< a href = "#" class = "c-navigation__trigger js-navigation-open" >
< svg xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 24 24" class = "icon" > < title > navigation-menu< / title > < rect x = "0.5" y = "2.5" width = "23" height = "3" rx = "1" ry = "1" / > < rect x = "0.5" y = "10.5" width = "23" height = "3" rx = "1" ry = "1" / > < rect x = "0.5" y = "18.5" width = "23" height = "3" rx = "1" ry = "1" / > < / svg >
< / a >
< div class = "o-navigation__list c-navigation__list js-navigation-list" >
< a href = "/" class = "a-logo" > < / a >
< a href = "#" class = "o-navigation__close js-navigation-close" >
< svg xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 24 24" class = "icon" > < title > close< / title > < path d = "M14.3,12.179a.25.25,0,0,1,0-.354l9.263-9.262A1.5,1.5,0,0,0,21.439.442L12.177,9.7a.25.25,0,0,1-.354,0L2.561.442A1.5,1.5,0,0,0,.439,2.563L9.7,11.825a.25.25,0,0,1,0,.354L.439,21.442a1.5,1.5,0,0,0,2.122,2.121L11.823,14.3a.25.25,0,0,1,.354,0l9.262,9.263a1.5,1.5,0,0,0,2.122-2.121Z" / > < / svg >
< / a >
< a href = "/getting-started.html" title = "Getting started"
class="o-navigation__item is-active">
Getting started
< / a >
< a href = "/integrations-overview.html" title = "Getting started"
class="o-navigation__item ">
Integrations
< / a >
< a href = "/api.html" title = "Getting started"
class="o-navigation__item ">
API
< / a >
< / div >
< / nav >
< / div >
< / div >
< / div >
< / header >
< div class = "o-distance" >
<!-- The main content area -->
< main role = "main" >
< div class = "o-constrained o-content" >
< div class = "o-grid" >
< div class = "o-grid__column-1-1 o-grid__column-large-1-3" >
< a href = "" title = "Docs categories" class = "a-button js-docs-trigger c-side-navigation__trigger" > Docs categories< / a >
< ul class = "c-side-navigation js-docs-sidebar" >
< li class = "c-side-navigation__header" >
< a href = "#" class = "c-side-navigation__header__offset js-docs-trigger-close" >
< svg width = "18" height = "17" viewBox = "0 0 18 17" fill = "none" xmlns = "http://www.w3.org/2000/svg" > < rect x = "1.5752" y = "0.368273" width = "22" height = "1" rx = "0.5" transform = "rotate(45 1.5752 0.368273)" fill = "#090909" > < / rect > < rect x = "0.868164" y = "15.9246" width = "22" height = "1" rx = "0.5" transform = "rotate(-45 0.868164 15.9246)" fill = "#090909" > < / rect > < / svg >
< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "how-it-works.html" class = "c-side-navigation__item__anchor" > How it works< / a >
< / li >
< li class = "c-side-navigation__item is-active" >
< a href = "getting-started.html" class = "c-side-navigation__item__anchor" > Getting Started< / a >
< ul class = "c-side-navigation__item__tree" >
< li class = "c-side-navigation__item " >
< a href = "getting-started.html#Installation" class = "c-side-navigation__item__anchor" > Installation< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "getting-started.html#Importing-the-library" class = "c-side-navigation__item__anchor" > Importing the library< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "getting-started.html#Connecting-to-a-web3-provider" class = "c-side-navigation__item__anchor" > Connecting to a web3 provider< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "getting-started.html#Enhancing-your-contract-objects" class = "c-side-navigation__item__anchor" > Enhancing your contract objects< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "getting-started.html#Reacting-to-data" class = "c-side-navigation__item__anchor" > Reacting to data< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "getting-started.html#Tracking-state" class = "c-side-navigation__item__anchor" > Tracking state< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "getting-started.html#Tracking-events" class = "c-side-navigation__item__anchor" > Tracking events< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "getting-started.html#Tracking-balances" class = "c-side-navigation__item__anchor" > Tracking balances< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "getting-started.html#Getting-block-data-gas-prices-and-block-time" class = "c-side-navigation__item__anchor" > Getting block data, gas prices and block time< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "getting-started.html#Subscriptions" class = "c-side-navigation__item__anchor" > Subscriptions< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "getting-started.html#Cleanup" class = "c-side-navigation__item__anchor" > Cleanup< / a >
< / li >
< / ul >
< / li >
< li class = "c-side-navigation__item " >
< span class = "c-side-navigation__item__anchor" > Integrations< / span >
< ul class = "c-side-navigation__item__tree" >
< li class = "c-side-navigation__item " >
< a href = "integrations-overview.html" class = "c-side-navigation__item__anchor" > Overview< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "react.html" class = "c-side-navigation__item__anchor" > React< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "vue.html" class = "c-side-navigation__item__anchor" > Vue< / a >
< / li >
< li class = "c-side-navigation__item " >
< span class = "c-side-navigation__item__anchor" > Redux< / span >
< ul class = "c-side-navigation__item__tree" >
< li class = "c-side-navigation__item " >
< a href = "redux.html" class = "c-side-navigation__item__anchor" > redux< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "redux-observable.html" class = "c-side-navigation__item__anchor" > redux-observable< / a >
< / li >
< / ul >
< / li >
< li class = "c-side-navigation__item " >
< a href = "reactive-graphql.html" class = "c-side-navigation__item__anchor" > reactive-graphql< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "apollo-client.html" class = "c-side-navigation__item__anchor" > apollo-client< / a >
< / li >
< / ul >
< / li >
< li class = "c-side-navigation__item " >
< a href = "tutorial.html" class = "c-side-navigation__item__anchor" > Tutorial< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "api.html" class = "c-side-navigation__item__anchor" > API< / a >
< ul class = "c-side-navigation__item__tree" >
< li class = "c-side-navigation__item " >
< a href = "api.html#general" class = "c-side-navigation__item__anchor" > General< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "api.html#Contract-methods" class = "c-side-navigation__item__anchor" > Contract methods< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "api.html#Blocks-gas-price-and-block-time" class = "c-side-navigation__item__anchor" > Blocks, gas price and block time< / a >
< / li >
< li class = "c-side-navigation__item " >
< a href = "api.html#Low-level-API-for-data-tracking" class = "c-side-navigation__item__anchor" > Low level API for data tracking< / a >
< / li >
< / ul >
< / li >
< / ul >
< / div >
< div class = "o-grid__column-1-1 o-grid__column-large-2-3" >
< h1 id = "Getting-Started" > < a href = "#Getting-Started" class = "headerlink" title = "Getting Started" > < / a > Getting Started< / h1 > < h2 id = "Installation" > < a href = "#Installation" class = "headerlink" title = "Installation" > < / a > Installation< / h2 > < p > < strong > Subspace< / strong > can be used in browser, node and native script environments. To get started install the package < code > @embarklabs/subspace< / code > using < code > npm< / code > or < code > yarn< / code > by executing this command in your project directory:< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-bash" > # Using npm
npm install --save @embarklabs/ subspace
# Using yarn
yarn add @embarklabs/ subspace
< / code > < / pre >
< h2 id = "Importing-the-library" > < a href = "#Importing-the-library" class = "headerlink" title = "Importing the library" > < / a > Importing the library< / h2 >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > / / ESM (might require babel / browserify)
import Subspace from ' @embarklabs/ subspace' ;
/ / CommonJS
const Subspace = require(' @embarklabs/ subspace' );
< / code > < / pre >
< h2 id = "Connecting-to-a-web3-provider" > < a href = "#Connecting-to-a-web3-provider" class = "headerlink" title = "Connecting to a web3 provider" > < / a > Connecting to a web3 provider< / h2 > < p > To interact with the EVM, < strong > Subspace< / strong > requires a valid Web3 object, connected to a provider< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > const subspace = new Subspace(web3);
await subspace.init();
< / code > < / pre >
< p > In addition to the provider, < code > Subspace< / code > also accepts an < code > options< / code > object with settings that can change its behavior:< / p >
< ul >
< li > < code > dbFilename< / code > - Name of the database where the information will be stored (default < code > ' subspace.db' < / code > )< / li >
< li > < code > callInterval< / code > - Interval of time in milliseconds to query a contract/address to determine changes in state or balance. It’ s only used with HttpProviders (default: < code > undefined< / code > . Obtains data every block using the average block time as an interval).< / li >
< li > < code > refreshLastNBlocks< / code > - Ignores last N blocks (from current block), stored in the local db and refresh them via a web3 subscription. Useful for possible reorgs (default: 12),< / li >
< li > < code > disableSubscriptions< / code > - Subspace by default will attempt to use websocket subscriptions if the current provider supports them, otherwise it will use polling because it asumes the provider is an HttpProvider. This functionality can be disabled by passing true to this option. (default: < code > undefined< / code > )< / li >
< / ul >
< h2 id = "Enhancing-your-contract-objects" > < a href = "#Enhancing-your-contract-objects" class = "headerlink" title = "Enhancing your contract objects" > < / a > Enhancing your contract objects< / h2 > < p > Subspace provides a method to enhance your web3 Contract objects: < code > subspace.contract(instance|{abi,address})< / code > . Calling this method will return a new contract object decorated with a < code > .track()< / code > method for your contract view functions and events.< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > const myRxContract = subspace.contract(myContractInstance);
< / code > < / pre >
< p > You can also instantiate a contract directly by passing the contract ABI and its address:< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > const myRXContract = subspace.contract({abi: ...., address: ' 0x1234...CDEF' })
< / code > < / pre >
< h2 id = "Reacting-to-data" > < a href = "#Reacting-to-data" class = "headerlink" title = "Reacting to data" > < / a > Reacting to data< / h2 > < p > Once it’ s initialized, you can use < strong > Subspace< / strong > ‘ s methods to track the contract state, events and balances. These functions return RxJS Observables which you can subscribe to, and obtain and transform the observed data via operators.< / p >
< div class = "c-notification" >
< h3 > What is an Observable?< / h3 >
The < code > Observable< / code > type can be used to model push-based data sources such as DOM events, timer intervals, and sockets. In addition, observables are:
- Compositional: Observables can be composed with higher-order combinators.
- Lazy: Observables do not start emitting data until an observer has subscribed.
< / div >
< h4 id = "Further-read" > < a href = "#Further-read" class = "headerlink" title = "Further read" > < / a > Further read< / h4 > < ul >
< li > < a href = "https://rxjs-dev.firebaseapp.com/guide/observable" target = "_blank" rel = "noopener" > RxJS Observables< / a > < / li >
< / ul >
< h2 id = "Tracking-state" > < a href = "#Tracking-state" class = "headerlink" title = "Tracking state" > < / a > Tracking state< / h2 > < p > You can track changes to a contract state variable, by specifying the view function and arguments to call and query the contract. < / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > const stateObservable$ = Contract.methods.functionName(functionArgs).track();
< / code > < / pre >
< div class = "c-notification" >
< h3 > Tracking the public variables of a contract< / h3 >
State variables implicity create a < code > view< / code > function when they're defined as < code > public< / code > . The < code > functionName< / code > would be the same as the variable name, and < code > functionArgs< / code > will have a value when the type is a < code > mapping< / code > or array (since these require an index value to query them).
< / div >
< p > Example:< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > const productTitle$ = ProductList.methods.products(0).track().map(" title" );
productTitle$.subscribe((title) => console.log(" product title is " + title));
/ / Alternative using Subspace low level API
const producTitle$ = subspace.trackProperty(ProductList, " products" , [0], {from: web3.eth.defaultAccount});
...
< / code > < / pre >
< p > The subscription will be triggered whenever the title changes< / p >
< h2 id = "Tracking-events" > < a href = "#Tracking-events" class = "headerlink" title = "Tracking events" > < / a > Tracking events< / h2 > < p > You can track events and react to their returned values.< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > const eventObservable$ = Contract.event.eventName.track();
< / code > < / pre >
< p > Example:< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > const rating$ = Product.events.Rating.track().map(" rating" )).pipe(map(x => parseInt(x)));
rating$.subscribe((rating) => console.log(" rating received: " + rating));
/ / Alternative using Subspace low level API
const rating$ = subspace.trackEvent(Product, " Rating" , {fromBlock: 0});
...
< / code > < / pre >
< p > < strong > Event Sourcing< / strong > < / p >
< p > You can easily do event sourcing with subspace.< / p >
< p > For e.g: if you needed to get the average rating of the last 5 events:< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > import { $average, $latest } from " @embarklabs/ subspace" ;
const rating$ = Product.events.Rating.track().map(" rating" )).pipe(map(x => parseInt(x)));
rating$.pipe($latest(5), $average()).subscribe((rating) => {
console.log(" average rating of the last 5 events is " + rating)
});
< / code > < / pre >
< h2 id = "Tracking-balances" > < a href = "#Tracking-balances" class = "headerlink" title = "Tracking balances" > < / a > Tracking balances< / h2 > < p > You can also track changes in both ETH and ERC20 token balances for each mined block or time interval depending on the < code > callInterval< / code > configured. < / p >
< p > Tracking ETH balance in an address:< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > const address = " 0x0001020304050607080900010203040506070809" ;
subspace.trackBalance(address).subscribe((balance) => {
console.log(" ETH balance is " , balance)
});
< / code > < / pre >
< p > Tracking ETH balance in a Contract:< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > Contract.trackBalance().subscribe((balance) => {
console.log(" ETH balance is " , balance)
});
< / code > < / pre >
< p > Tracking an ERC20 balance in a Contract:< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > const tokenAddress = " 0x744d70fdbe2ba4cf95131626614a1763df805b9e" ; / / SNT Address
const myBalanceObservable$ = Contract.trackBalance(tokenAddress);
< / code > < / pre >
< div class = "c-notification c-notification--warning" >
Balances are returned as a string containing the value in < strong > wei< / strong > .
< / div >
< h2 id = "Getting-block-data-gas-prices-and-block-time" > < a href = "#Getting-block-data-gas-prices-and-block-time" class = "headerlink" title = "Getting block data, gas prices and block time" > < / a > Getting block data, gas prices and block time< / h2 > < p > Subspace also provides a way to always receive the latest block object: < / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > subspace.trackBlock().subscribe(block => {
console.log(" The latest block data: " , block);
});
< / code > < / pre >
< p > If you don’ t need all the block information, but just the block number, you can use instead:< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > subspace.trackBlockNumber().subscribe(blockNumber => {
console.log(" The latest block number: " , blockNumber);
});
< / code > < / pre >
< p > You can also access the average block time. This takes in account only the last 10 blocks:< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > subspace.trackAverageBlocktime().subscribe(blocktimeMS => {
console.log(" The average block time in milliseconds is: " , blocktimeMS);
});
< / code > < / pre >
< p > Finally, if you want to obtain the most up to date median gas price:< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > subspace.trackGasPrice().subscribe(gasPrice => {
console.log(" Gas price in wei" , gasPrice);
});
< / code > < / pre >
< h2 id = "Subscriptions" > < a href = "#Subscriptions" class = "headerlink" title = "Subscriptions" > < / a > Subscriptions< / h2 > < p > Once you have an < code > Observable< / code > , you may receive a stream of data by creating a subscription. Subscriptions are triggered each time an observable emits a new value. These subscription receive a callback that must have a parameter which represents the value received from the observable (a contract state variable, an event, or the balance of an address); and they return an object representing the subscription.< / p >
< p > Subscriptions can be disposed by executing the method < code > unsubscribe()< / code > liberating the resource held by it:< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-js" > const myBalanceObservable$ = subspace.trackBalance(address, tokenAddress);
const subscription = myBalanceObservable$.subscribe(value => {
console.log(" The balance is: " , value);
});
/ / ...
subscription.unsubscribe();
< / code > < / pre >
< h4 id = "Further-read-1" > < a href = "#Further-read-1" class = "headerlink" title = "Further read" > < / a > Further read< / h4 > < ul >
< li > < a href = "https://rxjs-dev.firebaseapp.com/guide/subscription" target = "_blank" rel = "noopener" > RxJS Subscriptions< / a > < / li >
< / ul >
< h2 id = "Cleanup" > < a href = "#Cleanup" class = "headerlink" title = "Cleanup" > < / a > Cleanup< / h2 > < p > If < strong > Subspace< / strong > is not needed anymore, you need can invoke < code > close()< / code > to dispose and perform the cleanup necessary to remove the internal subscriptions and interval timers created by < strong > Subspace< / strong > during its normal execution, thus avoiding any potential memory leak.< / p >
<!-- Has Prism -->
< pre class = "line-numbers" style = "" > < code class = "language-clike" > subspace.close();
< / code > < / pre >
< div class = "c-notification c-notification--warning" >
< h3 > What about subscriptions created with our observables?< / h3 >
< code > close()< / code > will dispose any web3 subscription created when using a Subspace tracking method, however any subscription to an observable must still be unsubscribed manually. The npm package < code > subsink< / code > can be used to clear all the observables' subscriptions at once.
< / div >
< / div >
< / div >
< / div >
< / main >
< / div >
< footer role = "contentinfo" class = "c-spotlightbox o-distance js-inviewport-item" >
< div class = "o-constrained" >
< div class = "o-banner o-center" >
< p class = "h3 o-banner__item" > You want to dive < strong > into the framework?< / strong > < / p >
< a href = "/getting-started.html" title = "" class = "a-button o-banner__item" > Getting
started< / a >
< / div >
< span class = "c-divider u-background-color-light o-distance-xl" > < / span >
< div class = "o-grid o-distance-xl" >
< div class = "o-grid__column-1-1 o-grid__column-xlarge-2-4" >
< div >
< a href = "./" class = "a-logo" > Subspace< / a >
< / div >
< div class = "c-box u-inline-block u-border-color-light o-distance-m" >
< div class = "o-media-short" >
< span class = "u-text-color-quiet" > We are part of EmbarkLabs< / span >
< img src = "/images/embark-logo.svg" class = "o-vertical-alignment__item" >
< / div >
< / div >
< / div >
< div class = "o-grid__column-1-2 o-grid__column-large-1-4 o-grid__column-xlarge-1-4" >
< p class = "h6 u-text-color-quiet u-text-uppercase" > Resources< / p >
< ul class = "o-list" >
< li > < a href = "/getting-started.html" title = "footer.resources.links.contact" > Getting started< / a > < / li >
< li > < a href = "/overview-integrations.html" title = "footer.resources.links.privacy" > Integrations< / a > < / li >
< li > < a href = "/api.html" target = "_blank" title = "footer.resources.links.privacy" target = "_blank" > API< / a > < / li >
< / ul >
< / div >
< div class = "o-grid__column-1-2 o-grid__column-large-1-4 o-grid__column-xlarge-1-4" >
< p class = "h6 u-text-color-quiet u-text-uppercase" > The Status Network< / p >
< ul class = "o-list" >
< li > < a href = "https://status.im/" title = "footer.status.links.status" target = "_blank" > Status< / a > < / li >
< li > < a href = "https://dap.ps/" title = "dap.ps" target = "_blank" > dap.ps< / a > < / li >
< li > < a href = "https://teller.exchange/" title = "Teller" target = "_blank" > Teller< / a > < / li >
< li > < a href = "https://assemble.fund/" title = "Assemble" target = "_blank" > Assemble< / a > < / li >
< li > < a href = "https://embark.status.im/" title = "Embark" target = "_blank" > Embark< / a > < / li >
< li > < a href = "https://subspace.status.im/" title = "Subspace" target = "_blank" > Subspace< / a > < / li >
< li > < a href = "https://vac.dev/" title = "Vac" target = "_blank" > Vac< / a > < / li >
< li > < a href = "https://nimbus.status.im/" title = "Nimbus" target = "_blank" > Nimbus< / a > < / li >
< / ul >
< / div >
< / div >
< div class = "o-distance" >
< p class = "o-vertical-alignment o-center u-text-color-quiet" >
MIT Licensed | Subspace is part of the Status Network
< / p >
< / div >
< / div >
< / footer >
< script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous">< / script >
<!-- Fathom - simple website analytics - https://github.com/usefathom/fathom -->
< script type = "text/javascript" >
2020-01-29 20:27:38 +00:00
(function(f, a, t, h, o, m){
a[h]=a[h]||function(){
(a[h].q=a[h].q||[]).push(arguments)
};
o=f.createElement('script'),
m=f.getElementsByTagName('script')[0];
o.async=1; o.src=t; o.id='fathom-script';
m.parentNode.insertBefore(o,m)
})(document, window, '//fathom.status.im/tracker.js', 'fathom');
fathom('set', 'siteId', 'DNONS');
fathom('trackPageview');
2020-03-20 18:02:02 +00:00
< / script >
<!-- / Fathom -->
< script >
/*!
* jQuery.anchorScroll jQuery Plugin v1.0
*
* Author: Virgiliu Diaconu
* http://www.virgiliu.com
* Licensed under the MIT license.
*/
!function(o){"use strict";o.anchorScroll=function(l,t){var n=this;n.$el=o(l),n.el=l,n.init=function(){n.options=o.extend({},o.anchorScroll.defaultOptions,t)},n.$el.click(function(t){if(t.preventDefault(),o(t.target).closest("a").length& & o(n.el.hash).length){var e=o(n.el.hash).offset().top-n.options.offsetTop,s="this"===n.$el.data("classTo")?n.el:n.$el.data("classTo"),c=n.$el.data("onScroll"),a=n.$el.data("scrollEnd");"function"==typeof n.options.scrollStart& & n.options.scrollStart.call(l),o(s).addClass(c).removeClass(a),o("html,body").animate({scrollTop:e},n.options.scrollSpeed).promise().done(function(){o(s).addClass(a).removeClass(c),"function"==typeof n.options.scrollEnd& & n.options.scrollEnd.call(l)})}}),n.init()},o.anchorScroll.defaultOptions={scrollSpeed:800,offsetTop:0},o.fn.anchorScroll=function(l){return this.each(function(){new o.anchorScroll(this,l)})}}(jQuery,window,document);
< / script >
< script > ( f u n c t i o n ( r o o t , f a c t o r y ) {
if (typeof define === "function" & & define.amd) {
define([], function() {
return factory();
});
} else if (typeof exports === "object") {
module.exports = factory();
} else {
root.Headhesive = factory();
}
})(this, function() {
"use strict";
var _mergeObj = function(to, from) {
for (var p in from) {
if (from.hasOwnProperty(p)) {
to[p] = typeof from[p] === "object" ? _mergeObj(to[p], from[p]) : from[p];
}
}
return to;
};
var _throttle = function(func, wait) {
var _now = Date.now || function() {
return new Date().getTime();
};
var context, args, result;
var timeout = null;
var previous = 0;
var later = function() {
previous = _now();
timeout = null;
result = func.apply(context, args);
context = args = null;
};
return function() {
var now = _now();
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining < = 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
context = args = null;
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
return result;
};
};
var _getScrollY = function() {
return window.pageYOffset !== undefined ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
};
var _getElemY = function(elem, side) {
var pos = 0;
var elemHeight = elem.offsetHeight;
while (elem) {
pos += elem.offsetTop;
elem = elem.offsetParent;
}
if (side === "bottom") {
pos = pos + elemHeight;
}
return pos;
};
var Headhesive = function(elem, options) {
if (!("querySelector" in document & & "addEventListener" in window)) {
return;
}
this.visible = false;
this.options = {
offset: 300,
offsetSide: "top",
classes: {
clone: "headhesive",
stick: "headhesive--stick",
unstick: "headhesive--unstick"
},
throttle: 250,
onInit: function() {},
onStick: function() {},
onUnstick: function() {},
onDestroy: function() {}
};
this.elem = typeof elem === "string" ? document.querySelector(elem) : elem;
this.options = _mergeObj(this.options, options);
this.init();
};
Headhesive.prototype = {
constructor: Headhesive,
init: function() {
this.clonedElem = this.elem.cloneNode(true);
this.clonedElem.className += " " + this.options.classes.clone;
document.body.insertBefore(this.clonedElem, document.body.firstChild);
if (typeof this.options.offset === "number") {
this.scrollOffset = this.options.offset;
} else if (typeof this.options.offset === "string") {
this._setScrollOffset();
} else {
throw new Error("Invalid offset: " + this.options.offset);
}
this._throttleUpdate = _throttle(this.update.bind(this), this.options.throttle);
this._throttleScrollOffset = _throttle(this._setScrollOffset.bind(this), this.options.throttle);
window.addEventListener("scroll", this._throttleUpdate, false);
window.addEventListener("resize", this._throttleScrollOffset, false);
this.options.onInit.call(this);
},
_setScrollOffset: function() {
if (typeof this.options.offset === "string") {
this.scrollOffset = _getElemY(document.querySelector(this.options.offset), this.options.offsetSide);
}
},
destroy: function() {
document.body.removeChild(this.clonedElem);
window.removeEventListener("scroll", this._throttleUpdate);
window.removeEventListener("resize", this._throttleScrollOffset);
this.options.onDestroy.call(this);
},
stick: function() {
if (!this.visible) {
this.clonedElem.className = this.clonedElem.className.replace(new RegExp("(^|\\s)*" + this.options.classes.unstick + "(\\s|$)*", "g"), "");
this.clonedElem.className += " " + this.options.classes.stick;
this.visible = true;
this.options.onStick.call(this);
}
},
unstick: function() {
if (this.visible) {
this.clonedElem.className = this.clonedElem.className.replace(new RegExp("(^|\\s)*" + this.options.classes.stick + "(\\s|$)*", "g"), "");
this.clonedElem.className += " " + this.options.classes.unstick;
this.visible = false;
this.options.onUnstick.call(this);
}
},
update: function() {
if (_getScrollY() > this.scrollOffset) {
this.stick();
} else {
this.unstick();
}
}
};
return Headhesive;
});< / script >
< script >
!function(a){"function"==typeof define& & define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):window.jQuery||window.Zepto)}(function(a){var b,c,d,e,f,g,h="Close",i="BeforeClose",j="AfterClose",k="BeforeAppend",l="MarkupParse",m="Open",n="Change",o="mfp",p="."+o,q="mfp-ready",r="mfp-removing",s="mfp-prevent-close",t=function(){},u=!!window.jQuery,v=a(window),w=function(a,c){b.ev.on(o+a+p,c)},x=function(b,c,d,e){var f=document.createElement("div");return f.className="mfp-"+b,d& & (f.innerHTML=d),e?c& & c.appendChild(f):(f=a(f),c& & f.appendTo(c)),f},y=function(c,d){b.ev.triggerHandler(o+c,d),b.st.callbacks& & (c=c.charAt(0).toLowerCase()+c.slice(1),b.st.callbacks[c]& & b.st.callbacks[c].apply(b,a.isArray(d)?d:[d]))},z=function(c){return c===g& & b.currTemplate.closeBtn||(b.currTemplate.closeBtn=a(b.st.closeMarkup.replace("%title%",b.st.tClose)),g=c),b.currTemplate.closeBtn},A=function(){a.magnificPopup.instance||(b=new t,b.init(),a.magnificPopup.instance=b)},B=function(){var a=document.createElement("p").style,b=["ms","O","Moz","Webkit"];if(void 0!==a.transition)return!0;for(;b.length;)if(b.pop()+"Transition"in a)return!0;return!1};t.prototype={constructor:t,init:function(){var c=navigator.appVersion;b.isLowIE=b.isIE8=document.all& & !document.addEventListener,b.isAndroid=/android/gi.test(c),b.isIOS=/iphone|ipad|ipod/gi.test(c),b.supportsTransition=B(),b.probablyMobile=b.isAndroid||b.isIOS||/(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent),d=a(document),b.popupsCache={}},open:function(c){var e;if(c.isObj===!1){b.items=c.items.toArray(),b.index=0;var g,h=c.items;for(e=0;e< h.length ; e + + ) if ( g = h[e],g.parsed&&(g=g.el[0]),g===c.el[0]){b.index=e;break}}else b . items = a.isArray(c.items)?c.items:[c.items],b.index=c.index||0;if(b.isOpen)return void b . updateItemHTML ( ) ; b . types = [],f="",c.mainEl&&c.mainEl.length?b.ev=c.mainEl.eq(0):b.ev=d,c.key?(b.popupsCache[c.key]||(b.popupsCache[c.key]={}),b.currTemplate=b.popupsCache[c.key]):b.currTemplate={},b.st=a.extend(!0,{},a.magnificPopup.defaults,c),b.fixedContentPos="auto"===b.st.fixedContentPos?!b.probablyMobile:b.st.fixedContentPos,b.st.modal&&(b.st.closeOnContentClick=!1,b.st.closeOnBgClick=!1,b.st.showCloseBtn=!1,b.st.enableEscapeKey=!1),b.bgOverlay||(b.bgOverlay=x("bg").on("click"+p,function(){b.close()}),b.wrap=x("wrap").attr("tabindex",-1).on("click"+p,function(a){b._checkIfClose(a.target)&&b.close()}),b.container=x("container",b.wrap)),b.contentContainer=x("content"),b.st.preloader&&(b.preloader=x("preloader",b.container,b.st.tLoading));var i = a.magnificPopup.modules;for(e=0;e<i.length;e++){var j = i[e];j=j.charAt(0).toUpperCase()+j.slice(1),b["init"+j].call(b)}y("BeforeOpen"),b.st.showCloseBtn&&(b.st.closeBtnInside?(w(l,function(a,b,c,d){c.close_replaceWith=z(d.type)}),f+=" mfp-close-btn-in " ) :b . wrap . append ( z ( ) ) ) , b . st . alignTop & & ( f + = " mfp-align-top " ) , b . fixedContentPos ? b . wrap . css ( { overflow:b . st . overflowY , overflowX: " hidden " , overflowY:b . st . overflowY } ) :b . wrap . css ( { top:v . scrollTop ( ) , position: " absolute " } ) , ( b . st . fixedBgPos = ==!1||"auto"===b.st.fixedBgPos&&!b.fixedContentPos)&&b.bgOverlay.css({height:d.height(),position:"absolute"}),b.st.enableEscapeKey&&d.on("keyup"+p,function(a){27===a.keyCode&&b.close()}),v.on("resize"+p,function(){b.updateSize()}),b.st.closeOnContentClick||(f+=" mfp-auto-cursor " ) , f & & b . wrap . addClass ( f ) ; var k = b.wH=v.height(),n={};if(b.fixedContentPos&&b._hasScrollBar(k)){var o = b._getScrollbarSize();o&&(n.marginRight=o)}b.fixedContentPos&&(b.isIE7?a("body, html " ) . css ( " overflow " , " hidden " ) :n . overflow = "hidden" ) ; var r = b.st.mainClass;return b . isIE7 & & ( r + = " mfp-ie7 " ) , r & & b . _addClassToMFP ( r ) , b . updateItemHTML ( ) , y ( " BuildControls " ) , a ( " html " ) . css ( n ) , b . bgOverlay . add ( b . wrap ) . prependTo ( b . st . prependTo | | a ( document . body ) ) , b . _lastFocusedEl = document.activeElement,setTimeout(function(){b.content?(b._addClassToMFP(q),b._setFocus()):b.bgOverlay.addClass(q),d.on("focusin"+p,b._onFocusIn)},16),b.isOpen=!0,b.updateSize(k),y(m),c},close:function(){b.isOpen&&(y(i),b.isOpen=!1,b.st.removalDelay&&!b.isLowIE&&b.supportsTransition?(b._addClassToMFP(r)
< / script >
< script >
(function (global, factory) {
typeof exports === 'object' & & typeof module !== 'undefined' ? factory(require('jquery')) :
typeof define === 'function' & & define.amd ? define(['jquery'], factory) :
(factory(global.jQuery));
}(this, (function ($) { 'use strict';
$ = $ & & $.hasOwnProperty('default') ? $['default'] : $;
/**
* @author Mudit Ameta
* @license https://github.com/zeusdeux/isInViewport/blob/master/license.md MIT
*/
// expose isInViewport as a custom pseudo-selector
$.extend($.expr.pseudos || $.expr[':'], {
// if $.expr.createPseudo is available, use it
'in-viewport': $.expr.createPseudo
? $.expr.createPseudo(function (argsString) { return function (currElement) { return isInViewport(currElement, getSelectorArgs(argsString)); }; })
: function (currObj, index, meta) { return isInViewport(currObj, getSelectorArgs(meta[3])); }
});
// expose isInViewport as a function too
// this lets folks pass around actual objects as options (like custom viewport)
// and doesn't tie 'em down to strings. It also prevents isInViewport from
// having to look up and wrap the dom element corresponding to the viewport selector
$.fn.isInViewport = function(options) {
return this.filter(function (i, el) { return isInViewport(el, options); })
};
$.fn.run = run;
// lets you chain any arbitrary function or an array of functions and returns a jquery object
function run(args) {
var this$1 = this;
if (arguments.length === 1 & & typeof args === 'function') {
args = [args];
}
if (!(args instanceof Array)) {
throw new SyntaxError('isInViewport: Argument(s) passed to .do/.run should be a function or an array of functions')
}
args.forEach(function (arg) {
if (typeof arg !== 'function') {
console.warn('isInViewport: Argument(s) passed to .do/.run should be a function or an array of functions');
console.warn('isInViewport: Ignoring non-function values in array and moving on');
} else {
[].slice.call(this$1).forEach(function (t) { return arg.call($(t)); });
}
});
return this
}
// gets the width of the scrollbar
function getScrollbarWidth(viewport) {
// append a div that has 100% width to get true width of viewport
var el = $('< div > < / div > ').css({
width: '100%'
});
viewport.append(el);
// subtract true width from the viewport width which is inclusive
// of scrollbar by default
var scrollBarWidth = viewport.width() - el.width();
// remove our element from DOM
el.remove();
return scrollBarWidth
}
// Returns true if DOM element `element` is in viewport
function isInViewport(element, options) {
var ref = element.getBoundingClientRect();
var top = ref.top;
var bottom = ref.bottom;
var left = ref.left;
var right = ref.right;
var settings = $.extend({
tolerance: 0,
viewport: window
}, options);
var isVisibleFlag = false;
var $viewport = settings.viewport.jquery ? settings.viewport : $(settings.viewport);
if (!$viewport.length) {
console.warn('isInViewport: The viewport selector you have provided matches no element on page.');
console.warn('isInViewport: Defaulting to viewport as window');
$viewport = $(window);
}
var $viewportHeight = $viewport.height();
var $viewportWidth = $viewport.width();
var typeofViewport = $viewport[0].toString();
// if the viewport is other than window recalculate the top,
// bottom,left and right wrt the new viewport
// the [object DOMWindow] check is for window object type in PhantomJS
if ($viewport[0] !== window & & typeofViewport !== '[object Window]' & & typeofViewport !== '[object DOMWindow]') {
// use getBoundingClientRect() instead of $.Offset()
// since the original top/bottom positions are calculated relative to browser viewport and not document
var viewportRect = $viewport[0].getBoundingClientRect();
// recalculate these relative to viewport
top = top - viewportRect.top;
bottom = bottom - viewportRect.top;
left = left - viewportRect.left;
right = right - viewportRect.left;
// get the scrollbar width from cache or calculate it
isInViewport.scrollBarWidth = isInViewport.scrollBarWidth || getScrollbarWidth($viewport);
// remove the width of the scrollbar from the viewport width
$viewportWidth -= isInViewport.scrollBarWidth;
}
// handle falsy, non-number and non-integer tolerance value
// same as checking using isNaN and then setting to 0
// bitwise operators deserve some love too you know
settings.tolerance = ~~Math.round(parseFloat(settings.tolerance));
if (settings.tolerance < 0 ) {
settings.tolerance = $viewportHeight + settings.tolerance; // viewport height - tol
}
// the element is NOT in viewport iff it is completely out of
// viewport laterally or if it is completely out of the tolerance
// region. Therefore, if it is partially in view then it is considered
// to be in the viewport and hence true is returned. Because we have adjusted
// the left/right positions relative to the viewport, we should check the
// element's right against the viewport's 0 (left side), and the element's
// left against the viewport's width to see if it is outside of the viewport.
if (right < = 0 || left >= $viewportWidth) {
return isVisibleFlag
}
// if the element is bound to some tolerance
isVisibleFlag = settings.tolerance ? top < = settings.tolerance & & bottom >= settings.tolerance : bottom > 0 & & top < = $viewportHeight;
return isVisibleFlag
}
// get the selector args from the args string proved by Sizzle
function getSelectorArgs(argsString) {
if (argsString) {
var args = argsString.split(',');
// when user only gives viewport and no tolerance
if (args.length === 1 & & isNaN(args[0])) {
args[1] = args[0];
args[0] = void 0;
}
return {
tolerance: args[0] ? args[0].trim() : void 0,
viewport: args[1] ? $(args[1].trim()) : void 0
}
}
return {}
}
})));
//# sourceMappingURL=isInViewport.js.map
< / script >
< script src = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.19.0/prism.min.js" > < / script >
< script >
$(document).ready(function() {
$(document).on('scroll',function(e)
{
$('h2[id]').each(function()
{
if ( $(this).offset().top < window.pageYOffset + 100
& & $(this).offset().top +
$(this).height() > window.pageYOffset + 100
)
{
$(this).addClass('is-active');
var data = $(this).attr('id');
window.location.hash = '#!' + data;
var $location = window.location.hash.replace(/^#!/, '');
$('.js-docs-sidebar a').each(function() {
if($(this).attr('href').indexOf($location) > -1) {
$(this).addClass('is-active');
} else {
$(this).removeClass('is-active');
}
});
}
});
});
$(window).scroll(function() {
$('.js-inviewport-item').isInViewport({
tolerance: 0
})
.addClass('is-active');
});
var options = {
classes: {
clone: 'o-header__top--clone',
stick: 'is-sticky',
unstick: 'is-unsticky'
}
};
if($(window).width() >= 767) {
var header = new Headhesive('.js-header', options);
}
$('.js-navigation-open').on('click', function(event) {
event.preventDefault();
$('.js-navigation-list').addClass('is-active');
});
$('.js-navigation-close').on('click', function(event) {
event.preventDefault();
$('.js-navigation-list').removeClass('is-active');
});
$('.js-header-keyvisual').addClass('is-active');
// Popup
$('.js-popup').magnificPopup({
type: 'inline',
preloader: false,
focus: '#name',
// When elemened is focused, some mobile browsers in some cases zoom in
// It looks not nice, so we disable it:
callbacks: {
beforeOpen: function() {
if($(window).width() < 700 ) {
this.st.focus = false;
} else {
this.st.focus = '#name';
}
}
}
});
// Scroll
$('.js-anchor-scroll').anchorScroll({
scrollSpeed: 800, // scroll speed
offsetTop: 100, // offset for fixed top bars (defaults to 0)
onScroll: function () {
// callback on scroll start
},
scrollEnd: function () {
// callback on scroll end
}
});
});
$('.js-docs-trigger').on('click', function(event) {
event.preventDefault();
$('.js-docs-sidebar').addClass('is-active');
});
$('.js-docs-trigger-close').on('click', function(event) {
event.preventDefault();
$('.js-docs-sidebar').removeClass('is-active');
});
< / script >
< / body >
2020-01-17 22:18:10 +00:00
< / body >
< / html >
2020-03-20 18:02:02 +00:00