Features
------------
-* **Clean, intuitive design** — with Slate, the description of your API is on the left side of your documentation, and all the code examples are on the right side. Inspired by [Stripe's](https://stripe.com/docs/api) and [Paypal's](https://developer.paypal.com/webapps/developer/docs/api/) API docs. Slate is responsive, so it looks great on tablets, phones, and even print.
+* **Clean, intuitive design** — With Slate, the description of your API is on the left side of your documentation, and all the code examples are on the right side. Inspired by [Stripe's](https://stripe.com/docs/api) and [PayPal's](https://developer.paypal.com/webapps/developer/docs/api/) API docs. Slate is responsive, so it looks great on tablets, phones, and even in print.
-* **Everything on a single page** — gone are the days where your users had to search through a million pages to find what they wanted. Slate puts the entire documentation on a single page. We haven't sacrificed linkability, though. As you scroll, your browser's hash will update to the nearest header, so linking to a particular point in the documentation is still natural and easy.
+* **Everything on a single page** — Gone are the days when your users had to search through a million pages to find what they wanted. Slate puts the entire documentation on a single page. We haven't sacrificed linkability, though. As you scroll, your browser's hash will update to the nearest header, so linking to a particular point in the documentation is still natural and easy.
-* **Slate is just Markdown** — when you write docs with Slate, you're just writing Markdown, which makes it simple to edit and understand. Everything is written in Markdown — even the code samples are just Markdown code blocks!
+* **Slate is just Markdown** — When you write docs with Slate, you're just writing Markdown, which makes it simple to edit and understand. Everything is written in Markdown — even the code samples are just Markdown code blocks.
-* **Write code samples in multiple languages** — if your API has bindings in multiple programming languages, you easily put in tabs to switch between them. In your document, you'll distinguish different languages by specifying the language name at the top of each code block, just like with Github Flavored Markdown!
+* **Write code samples in multiple languages** — If your API has bindings in multiple programming languages, you can easily put in tabs to switch between them. In your document, you'll distinguish different languages by specifying the language name at the top of each code block, just like with GitHub Flavored Markdown.
-* **Out-of-the-box syntax highlighting** for [almost 60 languages](http://rouge.jayferd.us/demo), no configuration required.
+* **Out-of-the-box syntax highlighting** for [over 100 languages](https://github.com/jneen/rouge/wiki/List-of-supported-languages-and-lexers), no configuration required.
* **Automatic, smoothly scrolling table of contents** on the far left of the page. As you scroll, it displays your current position in the document. It's fast, too. We're using Slate at TripIt to build documentation for our new API, where our table of contents has over 180 entries. We've made sure that the performance remains excellent, even for larger documents.
-* **Let your users update your documentation for you** — by default, your Slate-generated documentation is hosted in a public Github repository. Not only does this mean you get free hosting for your docs with Github Pages, but it also makes it's simple for other developers to make pull requests to your docs if they find typos or other problems. Of course, if you don't want to, you're welcome to not use Github and host your docs elsewhere!
+* **Let your users update your documentation for you** — By default, your Slate-generated documentation is hosted in a public GitHub repository. Not only does this mean you get free hosting for your docs with GitHub Pages, but it also makes it simple for other developers to make pull requests to your docs if they find typos or other problems. Of course, if you don't want to use GitHub, you're also welcome to host your docs elsewhere.
-Getting starting with Slate is super easy! Simply fork this repository, and then follow the instructions below. Or, if you'd like to check out what Slate is capable of, take a look at the [sample docs](http://tripit.github.io/slate).
+* **RTL Support** Full right-to-left layout for RTL languages such as Arabic, Persian (Farsi), Hebrew etc.
-
+Getting started with Slate is super easy! Simply fork this repository and follow the instructions below. Or, if you'd like to check out what Slate is capable of, take a look at the [sample docs](http://lord.github.io/slate).
Getting Started with Slate
------------------------------
@@ -37,86 +38,78 @@ Getting Started with Slate
You're going to need:
- - **Linux or OS X** — Windows may work, but is unsupported.
- - **Ruby, version 1.9.3 or newer**
+ - **Linux or macOS** — Windows may work, but is unsupported.
+ - **Ruby, version 2.3.1 or newer**
- **Bundler** — If Ruby is already installed, but the `bundle` command doesn't work, just run `gem install bundler` in a terminal.
### Getting Set Up
- 1. Fork this repository on Github.
- 2. Clone *your forked repository* (not our original one) to your hard drive with `git clone https://github.com/YOURUSERNAME/slate.git`
- 3. `cd slate`
- 4. Install all dependencies: `bundle install`
- 5. Start the test server: `bundle exec middleman server`
-
-Or use the included Dockerfile! (must install Docker first)
+1. Fork this repository on GitHub.
+2. Clone *your forked repository* (not our original one) to your hard drive with `git clone https://github.com/YOURUSERNAME/slate.git`
+3. `cd slate`
+4. Initialize and start Slate. You can either do this locally, or with Vagrant:
```shell
-docker build -t slate .
-docker run -d -p 4567:4567 slate
+# either run this to run locally
+bundle install
+bundle exec middleman server
+
+# OR run this to run with vagrant
+vagrant up
```
-You can now see the docs at . Whoa! That was fast!
+You can now see the docs at http://localhost:4567. Whoa! That was fast!
+
+Now that Slate is all set up on your machine, you'll probably want to learn more about [editing Slate markdown](https://github.com/lord/slate/wiki/Markdown-Syntax), or [how to publish your docs](https://github.com/lord/slate/wiki/Deploying-Slate).
+
+If you'd prefer to use Docker, instructions are available [in the wiki](https://github.com/lord/slate/wiki/Docker).
-*Note: if you're using the Docker setup on OSX, the docs will be
-availalable at the output of `boot2docker ip` instead of `localhost:4567`.*
+### Note on JavaScript Runtime
-Now that Slate is all set up your machine, you'll probably want to learn more about [editing Slate markdown](https://github.com/tripit/slate/wiki/Markdown-Syntax), or [how to publish your docs](https://github.com/tripit/slate/wiki/Deploying-Slate).
+For those who don't have JavaScript runtime or are experiencing JavaScript runtime issues with ExecJS, it is recommended to add the [rubyracer gem](https://github.com/cowboyd/therubyracer) to your gemfile and run `bundle` again.
-Examples of Slate in the Wild
+Companies Using Slate
---------------------------------
-* [Travis-CI's API docs](http://docs.travis-ci.com/api/)
-* [Mozilla's localForage docs](http://mozilla.github.io/localForage/)
-* [Mozilla Recroom](http://mozilla.github.io/recroom/)
-* [ChaiOne Gameplan API docs](http://chaione.github.io/gameplanb2b/#introduction)
-* [Drcaban's Build a Quine tutorial](http://drcabana.github.io/build-a-quine/#introduction)
-* [PricePlow API docs](https://www.priceplow.com/api/documentation)
-* [Emerging Threats API docs](http://apidocs.emergingthreats.net/)
-* [Appium docs](http://appium.io/slate/en/master)
-* [Golazon Developer](http://developer.golazon.com)
-* [Dwolla API docs](https://docs.dwolla.com/)
-* [RozpisyZapasu API docs](http://www.rozpisyzapasu.cz/dev/api/)
-* [Codestar Framework Docs](http://codestarframework.com/documentation/)
-* [Buddycloud API](http://buddycloud.com/api)
-* [Crafty Clicks API](https://craftyclicks.co.uk/api/)
-* [Paracel API Reference](http://paracel.io/docs/api_reference.html)
-* [Switch Payments Documentation](http://switchpayments.com/docs/) & [API](http://switchpayments.com/developers/)
-* [Coinbase API Reference](https://developers.coinbase.com/api)
-* [Whispir.io API](https://whispir.github.io/api)
-* [NASA API](https://data.nasa.gov/developer/external/planetary/)
-* [CardPay API](https://developers.cardpay.com/)
-* [IBM Cloudant](https://docs-testb.cloudant.com/content-review/_design/couchapp/index.html)
-* [Bitrix basis components](http://bbc.bitrix.expert/)
-* [viagogo API Documentation](http://developer.viagogo.net/)
-* [Fidor Bank API Documentation](http://docs.fidor.de/)
-* [Market Prophit API Documentation](http://developer.marketprophit.com/)
-
-(Feel free to add your site to this list in a pull request!)
-
-Need Help? Found a bug?
+* [NASA](https://api.nasa.gov)
+* [Sony](http://developers.cimediacloud.com)
+* [Best Buy](https://bestbuyapis.github.io/api-documentation/)
+* [Travis-CI](https://docs.travis-ci.com/api/)
+* [Greenhouse](https://developers.greenhouse.io/harvest.html)
+* [Woocommerce](http://woocommerce.github.io/woocommerce-rest-api-docs/)
+* [Dwolla](https://docs.dwolla.com/)
+* [Clearbit](https://clearbit.com/docs)
+* [Coinbase](https://developers.coinbase.com/api)
+* [Parrot Drones](http://developer.parrot.com/docs/bebop/)
+* [Scale](https://docs.scaleapi.com/)
+
+You can view more in [the list on the wiki](https://github.com/lord/slate/wiki/Slate-in-the-Wild).
+
+Questions? Need Help? Found a bug?
--------------------
-Just [submit a issue](https://github.com/tripit/slate/issues) to the Slate Github if you need any help. And, of course, feel free to submit pull requests with bug fixes or changes.
+If you've got questions about setup, deploying, special feature implementation in your fork, or just want to chat with the developer, please feel free to [start a thread in our Spectrum community](https://spectrum.chat/slate)!
+Found a bug with upstream Slate? Go ahead and [submit an issue](https://github.com/lord/slate/issues). And, of course, feel free to submit pull requests with bug fixes or changes to the `dev` branch.
Contributors
--------------------
-Slate was built by [Robert Lord](https://lord.io) while at [TripIt](http://tripit.com).
+Slate was built by [Robert Lord](https://lord.io) while interning at [TripIt](https://www.tripit.com/).
Thanks to the following people who have submitted major pull requests:
- [@chrissrogers](https://github.com/chrissrogers)
- [@bootstraponline](https://github.com/bootstraponline)
- [@realityking](https://github.com/realityking)
+- [@cvkef](https://github.com/cvkef)
-Also, thanks to [Sauce Labs](http://saucelabs.com) for helping sponsor the project.
+Also, thanks to [Sauce Labs](http://saucelabs.com) for sponsoring the development of the responsive styles.
Special Thanks
--------------------
- [Middleman](https://github.com/middleman/middleman)
- [jquery.tocify.js](https://github.com/gfranko/jquery.tocify.js)
- [middleman-syntax](https://github.com/middleman/middleman-syntax)
-- [middleman-gh-pages](https://github.com/neo/middleman-gh-pages)
+- [middleman-gh-pages](https://github.com/edgecase/middleman-gh-pages)
- [Font Awesome](http://fortawesome.github.io/Font-Awesome/)
diff --git a/Rakefile b/Rakefile
deleted file mode 100644
index 6a952e1e914..00000000000
--- a/Rakefile
+++ /dev/null
@@ -1,6 +0,0 @@
-require 'middleman-gh-pages'
-require 'rake/clean'
-
-CLOBBER.include('build')
-
-task :default => [:build]
diff --git a/Vagrantfile b/Vagrantfile
new file mode 100644
index 00000000000..6f2444e2133
--- /dev/null
+++ b/Vagrantfile
@@ -0,0 +1,44 @@
+Vagrant.configure(2) do |config|
+ config.vm.box = "ubuntu/trusty64"
+ config.vm.network :forwarded_port, guest: 4567, host: 4567
+ config.vm.provider "virtualbox" do |vb|
+ vb.memory = "2048"
+ end
+
+ config.vm.provision "bootstrap",
+ type: "shell",
+ inline: <<-SHELL
+ sudo apt-add-repository ppa:brightbox/ruby-ng
+ sudo apt-get update
+ sudo apt-get install -yq ruby2.4 ruby2.4-dev
+ sudo apt-get install -yq pkg-config build-essential nodejs git libxml2-dev libxslt-dev
+ sudo apt-get autoremove -yq
+ gem2.4 install --no-ri --no-rdoc bundler
+ SHELL
+
+ # add the local user git config to the vm
+ config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig"
+
+ config.vm.provision "install",
+ type: "shell",
+ privileged: false,
+ inline: <<-SHELL
+ echo "=============================================="
+ echo "Installing app dependencies"
+ cd /vagrant
+ bundle config build.nokogiri --use-system-libraries
+ bundle install
+ SHELL
+
+ config.vm.provision "run",
+ type: "shell",
+ privileged: false,
+ run: "always",
+ inline: <<-SHELL
+ echo "=============================================="
+ echo "Starting up middleman at http://localhost:4567"
+ echo "If it does not come up, check the ~/middleman.log file for any error messages"
+ cd /vagrant
+ bundle exec middleman server --watcher-force-polling --watcher-latency=1 &> ~/middleman.log &
+ SHELL
+end
diff --git a/config.rb b/config.rb
index 43bceaa5a43..24887979fea 100644
--- a/config.rb
+++ b/config.rb
@@ -1,3 +1,6 @@
+# Unique header generation
+require './lib/unique_head.rb'
+
# Markdown
set :markdown_engine, :redcarpet
set :markdown,
@@ -5,9 +8,11 @@
smartypants: true,
disable_indented_code_blocks: true,
prettify: true,
+ strikethrough: true,
tables: true,
with_toc_data: true,
- no_intra_emphasis: true
+ no_intra_emphasis: true,
+ renderer: UniqueHeadCounter
# Assets
set :css_dir, 'stylesheets'
@@ -17,6 +22,11 @@
# Activate the syntax highlighter
activate :syntax
+ready do
+ require './lib/multilang.rb'
+end
+
+activate :sprockets
activate :autoprefixer do |config|
config.browsers = ['last 2 version', 'Firefox ESR']
@@ -30,9 +40,19 @@
# Build Configuration
configure :build do
+ # If you're having trouble with Middleman hanging, commenting
+ # out the following two lines has been known to help
activate :minify_css
activate :minify_javascript
# activate :relative_assets
# activate :asset_hash
# activate :gzip
end
+
+# Deploy Configuration
+# If you want Middleman to listen on a different port, you can set that below
+set :port, 4567
+
+helpers do
+ require './lib/toc_data.rb'
+end
diff --git a/deploy.sh b/deploy.sh
new file mode 100755
index 00000000000..f122ba7ce62
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,215 @@
+#!/usr/bin/env bash
+set -o errexit #abort if any command fails
+me=$(basename "$0")
+
+help_message="\
+Usage: $me [-c FILE] []
+Deploy generated files to a git branch.
+
+Options:
+
+ -h, --help Show this help information.
+ -v, --verbose Increase verbosity. Useful for debugging.
+ -e, --allow-empty Allow deployment of an empty directory.
+ -m, --message MESSAGE Specify the message used when committing on the
+ deploy branch.
+ -n, --no-hash Don't append the source commit's hash to the deploy
+ commit's message.
+ --source-only Only build but not push
+ --push-only Only push but not build
+"
+
+
+run_build() {
+ bundle exec middleman build --clean
+}
+
+parse_args() {
+ # Set args from a local environment file.
+ if [ -e ".env" ]; then
+ source .env
+ fi
+
+ # Parse arg flags
+ # If something is exposed as an environment variable, set/overwrite it
+ # here. Otherwise, set/overwrite the internal variable instead.
+ while : ; do
+ if [[ $1 = "-h" || $1 = "--help" ]]; then
+ echo "$help_message"
+ return 0
+ elif [[ $1 = "-v" || $1 = "--verbose" ]]; then
+ verbose=true
+ shift
+ elif [[ $1 = "-e" || $1 = "--allow-empty" ]]; then
+ allow_empty=true
+ shift
+ elif [[ ( $1 = "-m" || $1 = "--message" ) && -n $2 ]]; then
+ commit_message=$2
+ shift 2
+ elif [[ $1 = "-n" || $1 = "--no-hash" ]]; then
+ GIT_DEPLOY_APPEND_HASH=false
+ shift
+ else
+ break
+ fi
+ done
+
+ # Set internal option vars from the environment and arg flags. All internal
+ # vars should be declared here, with sane defaults if applicable.
+
+ # Source directory & target branch.
+ deploy_directory=build
+ deploy_branch=gh-pages
+
+ #if no user identity is already set in the current git environment, use this:
+ default_username=${GIT_DEPLOY_USERNAME:-deploy.sh}
+ default_email=${GIT_DEPLOY_EMAIL:-}
+
+ #repository to deploy to. must be readable and writable.
+ repo=origin
+
+ #append commit hash to the end of message by default
+ append_hash=${GIT_DEPLOY_APPEND_HASH:-true}
+}
+
+main() {
+ parse_args "$@"
+
+ enable_expanded_output
+
+ if ! git diff --exit-code --quiet --cached; then
+ echo Aborting due to uncommitted changes in the index >&2
+ return 1
+ fi
+
+ commit_title=`git log -n 1 --format="%s" HEAD`
+ commit_hash=` git log -n 1 --format="%H" HEAD`
+
+ #default commit message uses last title if a custom one is not supplied
+ if [[ -z $commit_message ]]; then
+ commit_message="publish: $commit_title"
+ fi
+
+ #append hash to commit message unless no hash flag was found
+ if [ $append_hash = true ]; then
+ commit_message="$commit_message"$'\n\n'"generated from commit $commit_hash"
+ fi
+
+ previous_branch=`git rev-parse --abbrev-ref HEAD`
+
+ if [ ! -d "$deploy_directory" ]; then
+ echo "Deploy directory '$deploy_directory' does not exist. Aborting." >&2
+ return 1
+ fi
+
+ # must use short form of flag in ls for compatibility with macOS and BSD
+ if [[ -z `ls -A "$deploy_directory" 2> /dev/null` && -z $allow_empty ]]; then
+ echo "Deploy directory '$deploy_directory' is empty. Aborting. If you're sure you want to deploy an empty tree, use the --allow-empty / -e flag." >&2
+ return 1
+ fi
+
+ if git ls-remote --exit-code $repo "refs/heads/$deploy_branch" ; then
+ # deploy_branch exists in $repo; make sure we have the latest version
+
+ disable_expanded_output
+ git fetch --force $repo $deploy_branch:$deploy_branch
+ enable_expanded_output
+ fi
+
+ # check if deploy_branch exists locally
+ if git show-ref --verify --quiet "refs/heads/$deploy_branch"
+ then incremental_deploy
+ else initial_deploy
+ fi
+
+ restore_head
+}
+
+initial_deploy() {
+ git --work-tree "$deploy_directory" checkout --orphan $deploy_branch
+ git --work-tree "$deploy_directory" add --all
+ commit+push
+}
+
+incremental_deploy() {
+ #make deploy_branch the current branch
+ git symbolic-ref HEAD refs/heads/$deploy_branch
+ #put the previously committed contents of deploy_branch into the index
+ git --work-tree "$deploy_directory" reset --mixed --quiet
+ git --work-tree "$deploy_directory" add --all
+
+ set +o errexit
+ diff=$(git --work-tree "$deploy_directory" diff --exit-code --quiet HEAD --)$?
+ set -o errexit
+ case $diff in
+ 0) echo No changes to files in $deploy_directory. Skipping commit.;;
+ 1) commit+push;;
+ *)
+ echo git diff exited with code $diff. Aborting. Staying on branch $deploy_branch so you can debug. To switch back to master, use: git symbolic-ref HEAD refs/heads/master && git reset --mixed >&2
+ return $diff
+ ;;
+ esac
+}
+
+commit+push() {
+ set_user_id
+ git --work-tree "$deploy_directory" commit -m "$commit_message"
+
+ disable_expanded_output
+ #--quiet is important here to avoid outputting the repo URL, which may contain a secret token
+ git push --quiet $repo $deploy_branch
+ enable_expanded_output
+}
+
+#echo expanded commands as they are executed (for debugging)
+enable_expanded_output() {
+ if [ $verbose ]; then
+ set -o xtrace
+ set +o verbose
+ fi
+}
+
+#this is used to avoid outputting the repo URL, which may contain a secret token
+disable_expanded_output() {
+ if [ $verbose ]; then
+ set +o xtrace
+ set -o verbose
+ fi
+}
+
+set_user_id() {
+ if [[ -z `git config user.name` ]]; then
+ git config user.name "$default_username"
+ fi
+ if [[ -z `git config user.email` ]]; then
+ git config user.email "$default_email"
+ fi
+}
+
+restore_head() {
+ if [[ $previous_branch = "HEAD" ]]; then
+ #we weren't on any branch before, so just set HEAD back to the commit it was on
+ git update-ref --no-deref HEAD $commit_hash $deploy_branch
+ else
+ git symbolic-ref HEAD refs/heads/$previous_branch
+ fi
+
+ git reset --mixed
+}
+
+filter() {
+ sed -e "s|$repo|\$repo|g"
+}
+
+sanitize() {
+ "$@" 2> >(filter 1>&2) | filter
+}
+
+if [[ $1 = --source-only ]]; then
+ run_build
+elif [[ $1 = --push-only ]]; then
+ main "$@"
+else
+ run_build
+ main "$@"
+fi
diff --git a/lib/multilang.rb b/lib/multilang.rb
new file mode 100644
index 00000000000..36fbe5b1f07
--- /dev/null
+++ b/lib/multilang.rb
@@ -0,0 +1,16 @@
+module Multilang
+ def block_code(code, full_lang_name)
+ if full_lang_name
+ parts = full_lang_name.split('--')
+ rouge_lang_name = (parts) ? parts[0] : "" # just parts[0] here causes null ref exception when no language specified
+ super(code, rouge_lang_name).sub("highlight #{rouge_lang_name}") do |match|
+ match + " tab-" + full_lang_name
+ end
+ else
+ super(code, full_lang_name)
+ end
+ end
+end
+
+require 'middleman-core/renderers/redcarpet'
+Middleman::Renderers::MiddlemanRedcarpetHTML.send :include, Multilang
diff --git a/lib/nesting_unique_head.rb b/lib/nesting_unique_head.rb
new file mode 100644
index 00000000000..01278371c17
--- /dev/null
+++ b/lib/nesting_unique_head.rb
@@ -0,0 +1,22 @@
+# Nested unique header generation
+require 'middleman-core/renderers/redcarpet'
+
+class NestingUniqueHeadCounter < Middleman::Renderers::MiddlemanRedcarpetHTML
+ def initialize
+ super
+ @@headers_history = {} if !defined?(@@headers_history)
+ end
+
+ def header(text, header_level)
+ friendly_text = text.gsub(/<[^>]*>/,"").parameterize
+ @@headers_history[header_level] = text.parameterize
+
+ if header_level > 1
+ for i in (header_level - 1).downto(1)
+ friendly_text.prepend("#{@@headers_history[i]}-") if @@headers_history.key?(i)
+ end
+ end
+
+ return "#{text}"
+ end
+end
diff --git a/lib/toc_data.rb b/lib/toc_data.rb
new file mode 100644
index 00000000000..4a04efee26f
--- /dev/null
+++ b/lib/toc_data.rb
@@ -0,0 +1,31 @@
+require 'nokogiri'
+
+def toc_data(page_content)
+ html_doc = Nokogiri::HTML::DocumentFragment.parse(page_content)
+
+ # get a flat list of headers
+ headers = []
+ html_doc.css('h1, h2, h3').each do |header|
+ headers.push({
+ id: header.attribute('id').to_s,
+ content: header.children,
+ title: header.children.to_s.gsub(/<[^>]*>/, ''),
+ level: header.name[1].to_i,
+ children: []
+ })
+ end
+
+ [3,2].each do |header_level|
+ header_to_nest = nil
+ headers = headers.reject do |header|
+ if header[:level] == header_level
+ header_to_nest[:children].push header if header_to_nest
+ true
+ else
+ header_to_nest = header if header[:level] < header_level
+ false
+ end
+ end
+ end
+ headers
+end
diff --git a/lib/unique_head.rb b/lib/unique_head.rb
new file mode 100644
index 00000000000..d42bab2aa9d
--- /dev/null
+++ b/lib/unique_head.rb
@@ -0,0 +1,24 @@
+# Unique header generation
+require 'middleman-core/renderers/redcarpet'
+require 'digest'
+class UniqueHeadCounter < Middleman::Renderers::MiddlemanRedcarpetHTML
+ def initialize
+ super
+ @head_count = {}
+ end
+ def header(text, header_level)
+ friendly_text = text.gsub(/<[^>]*>/,"").parameterize
+ if friendly_text.strip.length == 0
+ # Looks like parameterize removed the whole thing! It removes many unicode
+ # characters like Chinese and Russian. To get a unique URL, let's just
+ # URI escape the whole header
+ friendly_text = Digest::SHA1.hexdigest(text)[0,10]
+ end
+ @head_count[friendly_text] ||= 0
+ @head_count[friendly_text] += 1
+ if @head_count[friendly_text] > 1
+ friendly_text += "-#{@head_count[friendly_text]}"
+ end
+ return "#{text}"
+ end
+end
diff --git a/source/fonts/slate.eot b/source/fonts/slate.eot
old mode 100755
new mode 100644
diff --git a/source/fonts/slate.svg b/source/fonts/slate.svg
old mode 100755
new mode 100644
diff --git a/source/fonts/slate.ttf b/source/fonts/slate.ttf
old mode 100755
new mode 100644
diff --git a/source/fonts/slate.woff b/source/fonts/slate.woff
old mode 100755
new mode 100644
diff --git a/source/fonts/slate.woff2 b/source/fonts/slate.woff2
old mode 100755
new mode 100644
diff --git a/source/images/logo.png b/source/images/logo.png
index fa1f13da819..f2195a692db 100644
Binary files a/source/images/logo.png and b/source/images/logo.png differ
diff --git a/source/includes/_errors.md b/source/includes/_errors.md
index 56cffb34d22..c06d04ca097 100644
--- a/source/includes/_errors.md
+++ b/source/includes/_errors.md
@@ -1,20 +1,140 @@
# Errors
-
-
-The Kittn API uses the following error codes:
-
-
-Error Code | Meaning
----------- | -------
-400 | Bad Request -- Your request sucks
-401 | Unauthorized -- Your API key is wrong
-403 | Forbidden -- The kitten requested is hidden for administrators only
-404 | Not Found -- The specified kitten could not be found
-405 | Method Not Allowed -- You tried to access a kitten with an invalid method
-406 | Not Acceptable -- You requested a format that isn't json
-410 | Gone -- The kitten requested has been removed from our servers
-418 | I'm a teapot
-429 | Too Many Requests -- You're requesting too many kittens! Slow down!
-500 | Internal Server Error -- We had a problem with our server. Try again later.
-503 | Service Unavailable -- We're temporarially offline for maintanance. Please try again later.
+
+
+
+## Error Handling
+
+In case of an error, the resulting answer will carry an error attribute and the code of an error together with other parameters listed below. When you receive an error code, you can look the error up in the [table](#exception-codes).
+
+All errors have the following format:
+
+see the right panel
+
+```json
+{"id":0,
+"error":
+{
+"code": ..error code number.. ,
+"name": ..error name.. ,
+"message": ..description.. ,
+"trace":
+[{"level":"error","file": ..file name.. ,"line": ..line number.. ,"method": ..name of the function from the source code which carries the error.. ,"timestamp": ..exact time of the error.. },
+{"level":"error","file":..file name.. ,"line": ..line number.. ,"method": ..name of the function from the source code which carries the error.. ,"timestamp": ..exact time of the error.. }]
+}}
+```
+
+```javascript
+//go to json
+```
+
+## Exception codes
+
+### KeyChain exceptions
+
+**Error code**|**Error name**|**Comment**
+---|---|---
+1|json_parse_error_code|error in json format of rpc command
+2|rpc_command_parse_code|error while parsing the structure of rpc command (invalid set of fields or invalid value type)|
+3|command_not_implemented_code|command is not implemented
+4|command_depreciated_code|command is depreciated
+5|invalid_arg_exception_code|invalid command arguments: occurs in case a parameter's type or value was passed incorrectly
+6|privkey_not_found_code|private key could not be found by this keyname
+7|privkey_invalid_unlock_code|cannot unlock private key: occurs when the user provided a wrong password from a private key
+8|password_input_error_code|error while getting password: all the other errors that occur while receiving a password from a user
+9|internal_error_code|some unspecified internal error: might occur if there are errors in the program. If you get this error, it is advisable to contact support.
+
+### Internal errors
+
+**Error code**|**Error name**
+---|---
+10|parse_error_exception_code
+11|timeout_exception_code
+12|file_not_found_exception_code
+13|key_not_found_exception_code
+14|bad_cast_exception_code
+15|assert_exception_code
+16|encryption_error_code
+17|null_optional_code
+18|overflow_code
+19|underflow_code
+20|divide_by_zero_code
+21|out_of_range_exception_code
+22|eof_exception_code
+
+### Third party exceptions
+
+**Error code**|**Error name**|**Comments**
+---|---|---
+23|std_exception_code|for std::exceptions (3rd party): standard exceptions that occurred in the third party components
+24|unhandled_exception_code|for unhandled 3rd party exceptions: all the other exceptions that occurred in the third patry components
+
+
+## Error example
+
+### Parsing error - the command was incorrect.
+
+Error format
+
+**Field name**|**Type**|**Description**
+---|---|---
+error|`json object`|error specifics|
+code|`integer`|error code number
+name|`string`|error name
+message|`string`|description of the error starting at the lower level where it occured and showing the higher level
+trace|`list of arrays`|trace of the error, starting from the lower level and moving to the higher levels
+
+JSON example
+
+```json
+{"id":0,
+"error":
+{
+"code":9,
+"name":"parse_error_exception_code",
+"message":"Parse Error: invalid index '123' in enum 'keychain_app::command_te' => cannot parse command",
+"trace":
+[{"level":"error","file":"exception.cpp","line":230,"method":"fc_light::throw_bad_enum_cast","timestamp":"2018-12-25T16:38:41"},
+{"level":"error","file":"keychain.cpp","line":90,"method":"keychain_app::keychain::operator ()","timestamp":"2018-12-25T16:38:41"}]
+}}
+```
+
+```javscript
+//go to json
+```
+
+## Log files
+
+If you are experiencing any trouble working with KeyChain, here you can find locations of the log files:
+
+**For macOS and Linux:**
+
+/var/keychain/logs
+
+**For Windows:**
+
+
+Win10: %USERPROFILE%\AppData\Local\Keychain\Logs
+
+where **%USERPROFILE%** is a user's folder, e.g. **C:\Users\Alice**
+
+
+Win7: %SystemRoot%\Logs\Keychain\Logs
+
+where **%SystemRoot%** — the folder where the system is installed; usually it is **C:\Windows**
+
+## Developer Debug Information
+
+If you need help fixing the bugs, please do not hesitate to contact us:
+
+- [Telegram](https://t.me/arrayio)
+- [Stackoverflow](https://stackoverflow.com/users/10429540/array-io)
+- [Twitter](https://twitter.com/ProjectArray)
+
+- Or you can write us an email to support@array.io.
+
+If you want to report a security issue, include the word "security" in the subject line.
+
+We take security issues very seriously and we'll be looking forward to hearing from you. Still, we hope you enjoy using KeyChain and the integration goes smooth!
\ No newline at end of file
diff --git a/source/index.html.md b/source/index.html.md
new file mode 100644
index 00000000000..5bd9850d130
--- /dev/null
+++ b/source/index.html.md
@@ -0,0 +1,905 @@
+---
+title: KeyChain Documentation
+
+language_tabs: # must be one of https://git.io/vQNgJ
+
+ - json
+ - javascript
+
+toc_footers:
+ - KeyChain official website
+
+includes:
+ - errors
+
+search: true
+---
+
+# KeyChain Documentation
+## Introduction
+
+**KeyChain** is a standalone app for signing transactions and generating key pairs. It stores private keys in an isolated environment where no logger, debugger or spyware can intercept them because of the [three-layer security](#three-security-layers-of-keychain) protecting each action of the system.
+**KeyChain** supports transactions from and to various blockchains, including Ethereum and Ethereum classic, Litecoin, Bitcoin, Bitcoin Cash, and Bitshares.
+
+## Installation
+
+Download and install KeyChain for [macOS](https://github.com/arrayio/array-io-keychain/releases/). Windows and Linux installers are coming soon.
+
+*Try out KeyChain on the [demo page](https://arrayio.github.io/array-io-keychain/demo/).*
+
+After installation, connect to the demo-page: [http://localhost:16384/](http://localhost:16384/) to check if the installation was successful and to test the KeyChain commands. In case everything went well, you will see the following page and you will be able to see responses to the commands in the "Response" box when you click on them.
+
+
+
+
+
+
+If you are having trouble connecting to the page, [contact us](#contact) and we will do our best to help you.
+
+Below you can find comprehensive installation guides for [macOS](#macos), [Windows](#windows), [Linux](#linux).
+
+## Getting started
+
+After you have installed KeyChain for [macOS](https://github.com/arrayio/array-io-keychain/releases/download/), you can start using it with web3. Just install the `keychain.js` library from this [source](https://www.npmjs.com/package/keychain.js) and follow these simple steps (see the right panel in javascript).
+
+If you launch KeyChain for the first time, you need to get a public key with the "select_key" command.
+You can save the public key to local storage. Then you will not need to use "select_key" command again.
+
+NB: Do not forget to install the library and require it:
+
+1) Install the library
+`npm install keychain.js`
+
+2) Use an overridden web3 function
+
+Now you can turn to the right panel where you use an overridden web3 function.
+
+```json
+//go to javascript
+```
+
+```javascript
+const { Keychain, KeychainWeb3 } = require('keychain.js');
+const Web3 = require('web3');
+const web3 = new Web3('YOUR_API_URL'); // https://ropsten.infura.io/v3/046804e3dd3240b09834531326f310cf
+const tx = {
+ to: '0xE8899BA12578d60e4D0683a596EDaCbC85eC18CC',
+ value: 100,
+ gas: 21000
+};
+const keychain = new Keychain();
+const keychainWeb3 = new KeychainWeb3(keychain, web3);
+keychain.selectKey()
+ .then(publicKey => keychainWeb3.signTransaction(tx, publicKey))
+ .then(result => web3.eth.sendSignedTransaction(result.rawTransaction));
+```
+
+* `keychain.js` - Keychain class for working with the KeyChain WebSocket
+* `keychainWeb3.js` - KeychainWeb3 class with methods `sign` and `singTransaction` for substituting `web3.eth.accounts.sign` and `web3.eth.accounts.signTransaction` methods
+
+**Run tests**
+
+If you wish to see KeyChain in action, install KeyChain, then install the library from this [source](https://www.npmjs.com/package/keychain.js) and import key to the `key_data` folder.
+
+
+1) Add key to your `key_data`:
+
+- public_key: `a7aea4bd112706655cb7014282d2a54658924e69c68f5a54f2cd5f35c6fcba9b610d6ae8549f960ae96e23ffc017f305c1d8664978c8ba8a1cc656fd9d068ef5`
+- password: `qwe`
+
+2) `npm run test`
+
+
+
+
+
+
+## Contact
+
+If you have questions or enquiries about KeyChain, please do not hesitate to contact us:
+
+- [Telegram](https://t.me/arrayio)
+- [Stackoverflow](https://stackoverflow.com/users/10429540/array-io)
+- [Twitter](https://twitter.com/ProjectArray)
+- Or you can write us an email to [support@array.io](mailto:support@array.io).
+
+If you want to report a security issue, include the word "security" in the subject field.
+
+We take security issues very seriously and we'll be looking forward to hearing from you. We hope you enjoy using KeyChain!
+
+## License
+
+MIT License
+
+Copyright (c) 2018 KeyChain
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+# Protocol
+
+## Sign a transaction
+
+### Command
+sign_trx
+
+> JSON Request
+
+```json
+{
+ "command": "sign_trx",
+ "params":
+ {
+ "transaction": "871689d060721b5cec5a010080841e00000000000011130065cd1d0000000000000000",
+ "blockchain_type": "array",
+ "public_key": "62826b9c7b6bbfcd89456c1e8068e141d6a46b2c1c0166ed25ba8ad6ede320f4454ff116d13f4e679e8224fcca49f49d50c279ed88513a1db7185946e26811ab01",
+ "unlock_time": 45
+ }
+}
+```
+```javascript
+//go to json
+```
+### Query parameters
+**Parameter**|**Type**|**Description**
+---|---|---
+transaction | ```hex string```|Hex representation of the transaction.
+blockchain_type | ```enumeration string```|Inserts the name of blockchain you’re using. Possible options: array, bitcoin, ethereum, bitshares.
+public_key | ```hex string```|64-byte public key in hex format
+unlock_time | ```integer```|This parameter is experimental and optional! If this parameter is defined and if it is greater than zero, it unlocks the key for a set number of seconds. While the key is unlocked, the transactions will be signed without the user's approval.
+
+### Response format
+
+> Response example
+
+```json
+{
+ "result":"6cc47faa3778d15efeb470cd4154fdceb80633beaed15f0816d93951ffd7629a5fae3fe83c030f5f8a0cea82c1907f85418b93e820ea3b043c116053afae20a300"
+}
+```
+```javascript
+//go to json
+```
+**Field name**|**Type**|**Description**
+---|---|---
+result|`hex string`|65-byte signature in hex string format.
+
+## Sign hash of transaction
+
+This request is suited best for advanced users who are eager to work on a low level. You should have knowledge of how to calculate hash of transaction, given the type of blockchain, and know the type of signature and its packaging.
+
+### Command
+sign_hash
+
+> JSON Request
+
+```json
+{
+ "command": "sign_hash",
+ "params":
+ {
+ "sign_type": "VRS_canonical",
+ "hash": "fe5e4a8974715e20f47c8bb609547c9e66b0b9e31d521199b3d8d6af6da74cb1",
+ "public_key": "a7aea4bd112706655cb7014282d2a54658924e69c68f5a54f2cd5f35c6fcba9b610d6ae8549f960ae96e23ffc017f305c1d8664978c8ba8a1cc656fd9d068ef5"
+ }
+}
+```
+```javascript
+//go to json
+```
+### Query parameters
+**Parameter**|**Type**|**Description**
+---|---|---
+sign-type |```enumeration string```|Customizes the way secp256 library is used by choosing one of its arguments through sign-type parameter. It has two possible value options: RSV_noncanonical and VRS_canonical. Default value is RSV_noncanonical. Prefix RSV/VRS means signature struct: [R, S, v] or [v, R, S].
+hash |```hex string```|Hash calculated from the transaction. It can be the result of the first calculation or the second - depending on the type of the blockchain. For example, Bitcoin uses two calculations: to sign a bitcoin transaction you need to transmit the final (second) hash. Ethereum and Array make only one calculation to get the hash.
+public_key|```hex string```|64-byte public key in hex format
+
+### Response format
+
+> Response example
+
+```json
+{
+ "result":"62826b9c7b6bbfcd89456c1e8068e141d6a46b2c1c0166ed25ba8ad6ede320f4454ff116d13f4e679e8224fcca49f49d50c279ed88513a1db7185946e26811ab01"
+}
+```
+```javascript
+//go to json
+```
+**Field name**|**Type**|**Description**
+---|---|---
+result|`hex string`|65-byte signature in hex format.
+
+
+## Select a key
+
+### Command
+select_key
+
+> JSON Request
+
+```json
+{
+ "command": "select_key"
+}
+```
+```javascript
+//go to json
+```
+### Query parameters
+No
+
+### Response format
+
+64-byte public key. The length of the public key depends on the type of the blockchain. For example, in Ethereum, the length of a public key is 64 bytes.
+
+> Response example
+
+```json
+{
+ "result":"a7aea4bd112706655cb7014282d2a54658924e69c68f5a54f2cd5f35c6fcba9b610d6ae8549f960ae96e23ffc017f305c1d8664978c8ba8a1cc656fd9d068ef5"
+}
+```
+```javascript
+//go to json
+```
+**Field name**|**Type**|**Description**
+---|---|---
+result|`hex string`| 64-byte public key.
+
+## Lock all unlocked keys
+This command protects you from any hostile intervention into the KeyChain while you have left your computer without supervision.
+
+### Command
+lock
+
+> JSON Request
+
+```json
+{
+ "command": "lock"
+}
+```
+```javascript
+//go to json
+```
+### Query parameters
+No
+### Response format
+
+> Response example
+
+```json
+{
+ "result":"true"
+}
+```
+```javascript
+//go to json
+```
+**Field name**|**Type**|**Description**
+---|---|---
+result|`bool`|bool result.
+
+## Unlock private key
+
+Unlock your key when you are ready to use it.
+
+
+
+
+
+
+### Command
+unlock
+
+> JSON Request
+
+```json
+{
+ "command": "unlock"
+ "params":
+ {
+ "public_key": "a7aea4bd112706655cb7014282d2a54658924e69c68f5a54f2cd5f35c6fcba9b610d6ae8549f960ae96e23ffc017f305c1d8664978c8ba8a1cc656fd9d068ef5",
+ "unlock_time": 45
+ }
+}
+```
+```javascript
+//go to json
+```
+### Query parameters
+
+**Parameter**|**Type**|**Description**
+---|---|---
+public_key|```hex string```|64-byte public key in hex format
+unlock_time|```integer```|When this parameter is specified, it unlocks the key for a set number of seconds. While the key is unlocked, the pass entry window will not appear and the transactions will be signed without the user's approval.
+
+### Response format
+
+> Response example
+
+```json
+{
+ "result":"true"
+}
+```
+```javascript
+//go to json
+```
+**Field name**|**Type**|**Description**
+---|---|---
+result|`bool`|bool result.
+
+## KeyChain version details
+
+You can request the details of the current KeyChain version you are using.
+
+### Command
+about
+
+> JSON Request
+
+```json
+{
+ "command":"about"
+}
+```
+```javascript
+//go to json
+```
+### Query parameters
+No
+
+### Response format
+
+> Response example
+
+```json
+{
+ "result":
+ {
+ "version":"0.9.114","git_revision_sha":"59861769dca634d08d5442cb0074d40d8f544e66","git_revision_age":"9 minutes ago","compile_date":"compiled on Dec 12 2018 at 08:11:44","boost_version":"1.66","openssl_version":"OpenSSL 1.1.1 11 Sep 2018","build":"linux 64-bit"
+ }
+}
+```
+```javascript
+//go to json
+```
+**Field name**|**Type**|**Description**
+---|---|---
+result|`json object`|version details as a json object with the following parameters.
+version|`string`|number of the current version.
+git_revision_sha|`string`|hash of the commit.
+git_revision_age|`string`|age of the commit.
+compile_date|`string`|time of compilation.
+boost_version|`string`|required version of the boost library.
+openssl_version|`string`|required openssl version.
+build|`string`|required operating system.
+
+## KeyChain version number
+
+You can request the number of the current version you are using.
+
+### Command
+version
+
+> JSON Request
+
+```json
+{
+ "command":"version"
+}
+```
+```javascript
+//go to json
+```
+### Query parameters
+No
+
+### Response format
+
+> Response example
+
+```json
+{
+ "result":"0.9.114"
+}
+```
+```javascript
+//go to json
+```
+**Field name**|**Type**|**Description**
+---|---|---
+result|`string`|current version number which has the form of "[major].[minor].[build number]".
+
+# Installation guides
+
+## macOS
+
+### System requirements
+
+- macOS [10.12](#http://support.apple.com/downloads/DL1917/en_US/macosupd10.12.5.dmg) or [newer](https://support.apple.com/downloads/macos).
+
+### How to install
+
+Download [KeyChain](https://github.com/arrayio/array-io-keychain/releases/) and follow the steps of the graphic installer.
+
+
+
+1 Click "next" to start installation
+
+
+
+2 Accept the terms of the License Agreement
+
+
+
+3 Choose a folder and click "next"
+
+
+
+4 Click "install" for installation to start
+
+
+
+5 You will need to authorize the installation
+
+
+
+6 Wait until the setup is complete
+
+
+
+7 Congratulations! You have installed KeyChain
+
+
+
+
+
+### Check if KeyChain is installed
+
+After installation, connect to the demo-page: [http://localhost:16384/](http://localhost:16384/) to check if the installation was successful and to test the KeyChain commands. In case everything went well, you will see the following page and you will be able to see responses to the commands in the "Response" box when you click on them.
+
+
+
+
+
+
+## Windows
+
+Windows installer will be accessible at v.1.0.
+
+We are passionate about KeyChain and seek to make it as soon as possible, so that you could enjoy its wonderful features on any operating system you like.
+
+### System requirements
+
+- Windows 7 or newer.
+
+
+
+
+
+
+## Linux
+
+Linux installer will be accessible soon.
+
+We are passionate about KeyChain and seek to make it as soon as possible, so that you could enjoy its wonderful features on any operating system you like.
+
+### System requirements
+
+- Ubuntu 16.04 or newer
+
+- Debian 9 or newer
+
+- Linux Mint 18.3 or newer
+
+
+
+# Sign an Ethereum transaction
+
+Here you can find an instruction on how to sign an Ethereum transaction with KeyChain.
+
+On this [demo page](https://arrayio.github.io/array-io-keychain/eth_signer/) you can try out signing Ethereum transactions with KeyChain.
+
+## Step-by-step guide
+
+### 1. Install KeyChain for macOS
+
+Download KeyChain installer for [macOS](https://github.com/arrayio/array-io-keychain/releases/).
+
+### 2. Request public key
+
+Start with the command `wscat -c ws://localhost:16384/`
+
+
+> Select a key
+
+```json
+{
+ "command": "select_key"
+}
+```
+```javascript
+//go to json
+```
+
+### 3. Get the address
+
+The address can be calculated from the public key.
+
+> Get address from public key
+
+```json
+//go to javascript
+```
+```javascript
+const ethUtil = require('ethereumjs-util');
+const publicKey = 'YOUR_PUBLIC_KEY';
+const fromAdd = ethUtil.publicToAddress(publicKey).toString('hex');
+```
+
+### 4. Make a transaction
+
+Now you can transfer money to the address corresponding to the public key.
+
+In case you work with ropsten -
+https://faucet.ropsten.be/
+
+### 5. Check the balance
+
+Check the balance of the address - it should have enough ether for a successful transfer.
+
+> Check balance
+
+```json
+//go to javascript
+```
+```javascript
+web3.eth.getBalance(fromAdd)
+.then(console.log);
+```
+
+### 6. Sign transaction
+
+You can now use the key that you have generated to sign a transaction.
+
+You can find an example of the code [here](https://github.com/arrayio/keychain.js/blob/master/example/signETH.js).
+
+### 7. Check Etherscan
+
+[Etherscan](https://ropsten.etherscan.io/address/0x1ba05dad1abe91fdea3afffe9676b59076ce0ece)
+
+
+
+# WebSocket API
+
+**KeyChain** contains a WebSocket API. This API can be used to stream information from a KeyChain instance to any client that implements WebSockets. Implementations in different languages:
+
+- NodeJS
+- Python
+- JavaScript/HTML
+- etc
+
+Install KeyChain for [macOS](https://github.com/arrayio/array-io-keychain/releases/).
+
+## Demo pages in JavaScript
+
+### For testing the KeyChain commands
+
+- On this [demo page](https://arrayio.github.io/array-io-keychain/demo/) you can test all the KeyChain commands.
+
+- [Here](https://github.com/arrayio/array-io-keychain/tree/master/docs/demo) you will find the **code** of the KeyChain demo page. It will be automatically installed together with the KeyChain.
+
+### For signing transactions
+
+- [Here](https://arrayio.github.io/array-io-keychain/eth_signer/) you can try out signing Ethereum transactions with KeyChain.
+
+- You can find an example of the code [here](https://github.com/arrayio/keychain.js/blob/master/example/signETH.js).
+
+## Message format
+
+> For example, here is a request for requesting the current version:
+
+```json
+{"command":"version"}
+```
+
+```javascript
+//go to json
+```
+
+Each WebSocket API message is a json serialized object containing a command with the corresponding parameters.
+
+For full comprehensive descriptions of the commands, acceptable parameters and values, go to the [Protocol](#generate-a-key-pair).
+
+## WebSocket integration guide
+
+### NodeJS integration example
+
+The following will show the usage of websocket connections.
+
+> We make use of the `wscat` application available via npm:
+
+```json
+//go to javascript
+```
+
+```javascript
+$ npm install -g wscat
+```
+
+> A simple call would take the form:
+
+```json
+//go to javascript
+```
+
+```javascript
+$ wscat -c ws://127.0.0.1:16384
+>{"command":"version"}
+```
+
+#### Successful Calls
+The API will return a properly JSON formated response carrying the same id as the request to distinguish subsequent calls.
+
+```json
+{"result": ..data..}
+```
+
+```javascript
+//go to json
+```
+
+#### Error
+
+In case of an error, the resulting answer will carry an error attribute and a description in human-readable format:
+
+```json
+{"error":"Error: keyfile could not be found by public_key"}
+```
+
+```javascript
+//go to json
+```
+
+### JavaScript integration example
+
+Before you proceed with the integration, you need to install KeyChain for [macOS](https://github.com/arrayio/array-io-keychain/releases/).
+
+#### Test in the web
+
+After installing KeyChain, open the browser and connect to the demo page via [http://localhost:16384/](http://localhost:16384/).
+
+When the connection is established, you will see the following KeyChain page. If you click on one of the commands on the left panel, you will see its json request and response in the white boxes below:
+
+
+
+#### Test from a terminal application:
+
+You can see if the installation was successful by going to the terminal app, opening KeyChain and trying one of the commands that you can take from the [Protocol](#generate-a-key-pair):
+
+
+c:\Users\User-1>cd "C: Program Files\WsKeychain"
+c:\Program Files\WSKeychain>keychain
+{"command":"version"}
+
+A successful response will take the following format:
+
+#### Response format
+
+> Response example
+
+```json
+{
+ "result":"0.9.114"
+}
+```
+```javascript
+//go to json
+```
+
+**Field name**|**Type**|**Description**
+---|---|---
+result|`string`|current version number which has the form of "[major].[minor].[build number]".
+
+
+### Build a page that connects to WebSocket
+
+[Here](https://github.com/arrayio/array-io-keychain/blob/master/docs/demo/index.html) you will find an example of how to build a web-page that connects to WebSocket.
+
+Save this to the folder where you will be running the `websocket` command.
+
+Open this page via localhost: [http://localhost:16384/](http://localhost:16384/)
+
+You can find the code for the demo page and the page itself up in the [Demo pages in JavaScript](#demo-pages-in-javascript) section.
+
+# Pipe API
+
+**KeyChain** has an I/O stream (pipe) integration option. Interaction with **KeyChain** through pipes requires a terminal application as an input module that sends requests (STDIN) to the security layer that shows you the details of your request. When you approve the request through an interative gialogue window, the request goes back through the signing module and gives you an answer (STDOUT).
+
+For detailed description of the process, see [Three security layers](#three-security-layers-of-keychain) section.
+
+## Message format
+
+Each Pipe API message is a json serialized object containing a command with the corresponding parameters.
+
+For full comprehensive descriptions of the commands, acceptable parameters and values, go to the [Protocol](#generate-a-key-pair).
+
+## Pipe integration guide
+
+Before you proceed with the integration, you need to install KeyChain for [macOS](https://github.com/arrayio/array-io-keychain/releases/).
+
+When the installation is complete, you can open stream input to start sending json requests through STDIN - STDOUT pipes.
+
+###### Sign transaction
+
+```javascript
+const { spawn } = require('child_process');
+const path = require('path');
+const keychain = spawn(path.join(__dirname, 'keychain'));
+const queue = [];
+
+const sendCommand = (command, callback) => {
+ keychain.stdin.write(JSON.stringify(command));
+ queue.push(callback)
+};
+
+keychain.stdout.on('data', data => {
+ queue.shift()(JSON.parse(data))
+});
+
+sendCommand({command: 'select_key'}, response => {
+ const selectedKey = response.result;
+ console.log('Selected key: ', selectedKey);
+ const signCommand = {
+ command: 'sign_trx',
+ params: {
+ transaction: 'eb0885098bca5a00825208948ec6977b1255854169e5f9f8f163f371bcf1ffd287038d7ea4c6800080038080',
+ blockchain_type: 'ethereum',
+ public_key: selectedKey,
+ }
+ };
+
+ sendCommand(signCommand, response => console.log('Sign result: ', response));
+});
+```
+For descriptions of all the commands and parameters, see [KeyChain Protocol](https://github.com/arrayio/array-io-keychain/wiki/KeyChain-Protocol).
+
+# Restore your keys with master key
+
+KeyChain allows its users to restore their keys using a master key.
+
+If you have lost or forgotten one of your public keys, you can restore it in the Key Manager by entering your seed phrase that you get when creating your first key. KeyChain will derive your keys from the master key.
+
+
+
+
+
NB: restoring keys is impossible without a seed phrase
+
Memorize your seed when you create your first key
+
+
+
+
+The seed is created after KeyChain collects entropy data to protect the master key.
+
+KeyChain starts collecting entropy data when it has checked whether the user who downloaded KeyChain has any keys at their disposal. The process which leads to launching the Entropy window can be described as the following UML diagram:
+
+
+
+## Step 1: Collecting entropy data
+
+
+
+## Step 2: Displaying the seed
+
+
+
+# KeyChain security
+
+## About KeyChain security
+
+Choosing a means of storing keys is an important and responsible task that everybody needs to address when considering making **transactions** - irrespective of the type of blockchain they use. That is why here we show you the **detailed structure** of our KeyChain - so that your choice of key storage is an informed decision based on real knowledge of what stands behind the system.
+
+## How KeyChain ensures safe key storage
+
+KeyChain encrypts private keys using AES256 algorithm and stores the keys in an isolated environment that is protected with [three security layers](#three-security-layers-of-keychain).
+
+**AES256** was first adopted by the U.S. government and now is used worldwide as a secure and reliable way of protecting information. It stands for **Advanced Encryption Standard** which handles **256-bit keys**. This is a symmetric encryption algorithm that creates an output (ciphertext) from the input (plaintext) in 14 rounds which involve several steps of encryption. These steps combine the procedures of other symmetric encryption algorithms: substitution cipher with a reference table, adding round key, shifting rows, and mixing columns - all performed multiple times.
+
+## OS-specific KeyChain security
+
+### Unix-like operating systems
+
+For Linux, we use a unique mechanism created by our team.
+
+Typically, Linux offers the following algorithm of interacting with the user:
+
+
+**1)** Client app = > connects => to X-server
+
+**2)** Client app => sends some X-proto specified commands => X-server receives the commands => X-server renders an interface window
+
+**3)** User enters a passphrase into the window => X-server catch the passphrase from the user => sends X-proto commands to the client => client app processes the passphrase from the user
+
+
+However, around 1984, at the time when X11 was created, there existed no such task as performing secure operations via the Internet. The developers of X11 did not set out to protect the user’s data from someone capturing it. Even now, there is still no real mechanism against this kind of attack.
+To solve the problem of protecting the data, we have decided to look past the standard solutions. Instead of receiving a passphrase from a user through the X-server, we have chosen to receive the passphrase from the keyboard driver. This serves as a shortcut that allows KeyChain to work without connecting to the X-server, thus minimizing the risk of someone stealing the passphrase.
+
+Therefore, now instead of the following sequence…
+
+
+- The user enters the passphrase into the window => X-server catches the passphrase => it sends the passphrase through X-proto => Client app processes the request
+
+
+… we have:
+
+
+- The user enters the passphrase => KeyChain catches the keyboard’s events directly
+
+
+The shorter the path, the fewer weak points can be found. We exclude the weakest link (X-server) from the process of entering the passphrase. Thus, for the third party to compromise the passphrase, they will need to intercept it right at the keyboard level, which requires to have root access and hence makes it almost impossible.
+
+You might be asking yourself: if KeyChain functions without connecting to the X-server, why does the user see the dialogue window?
+
+The answer is simple and is motivated by our concern with the user experience. Working through the command line is rather inconvenient for most people. That is why we use an emulator program that imitates the process of inputting the passphrase by receiving events from the KeyChain daemon. Note that no secret data goes into the emulator – like a mirror that only reflects light without absorbing any of it. This allows KeyChain to minimize the risks to the minimum.
+
+### Windows
+
+Users of **Windows 10 Enterprise Edition** can benefit from a new security feature - **Isolated User Mode (IUM)**. It employs a set of modes called **Virtual Trusted Levels (VTL)** to run processes separately, without accessing each other’s memory.
+We launch KeyChain on VTL1 (SecureMode). Any malware that is launched on VTL0 (NormalMode) does not have access to KeyChain. The mechanism of isolating the kernels is executed as a Windows OS process. Learn more about IUM processes [here](https://docs.microsoft.com/en-us/windows/desktop/procthread/isolated-user-mode--ium--processes).
+
+To ensure secure passphrase entry, **Windows Vista/7/8** and **Windows 10 (not Enterprise Edition)** use a mechanism similar to the one used for **User Access Control (UAC)**. In particular, UAC is used at a program setup to avoid giving a malware access to the system context.
+
+KeyChain gets access to the system environment when it is being installed.
+A malware can only access the user context (unless it is installed in the system as a service). The processes that are launched in the user context do not have access to the applications that are launched in the isolated context.Hence, a malware cannot get access to KeyChain data because of the mechanism of separating the access between the levels of the OS. For more information, please refer to [Microsoft documentation](https://docs.microsoft.com/en-us/windows/security/identity-protection/user-account-control/how-user-account-control-works).
+
+### macOS
+
+macOS has in innate security mechanism that does not allow any other program to interfere with a process if it was not the one that started it. Therefore, all we needed to do was to take care of the intermediary step that takes place at the passphrase entry on the keyboard. That is why we incorporated the `EnableSecureEventInput` function that provides a means for a process to protect sensitive data from being intercepted by other processes. Learn more on the [Apple developers portal](https://developer.apple.com/library/archive/technotes/tn2150/_index.html#//apple_ref/doc/uid/DTS10004249).
+
+## Three security layers of KeyChain
+
+
+
+Apps or websites send requests to the KeyChain through two types of communication - standard I/O streams (mostly called pipes), and the WebSocket.
+The architecture of the KeyChain software consists of the three independent layers:
+
+1. **API layer** which integrates with your app, website or any external application. It is language-neutral. The protocol for the terminal application operates with the JSON format in synchronous request/response way. The main function of the **API layer** is to transmit and parse commands for given API.
+Each request carries information about commands, the type of key user wants to use to sign transactions and other relevant parameters which you can find in the [Protocol](#generate-a-key-pair).
+
+2. **Security layer** receives the commands from the API layer and acts as an OS-specific protection mechanism for the **interface window** (third layer). It serves as a shield from potential attacks at sensitive data and information. **Security layer** is tailored for the Mac OS, Linux, and Windows OS and operates only with permitted files (through admin access).
+The request, transmitted to the **Signing module** which holds the private keys, works simultaneously with the Secured input module that uses OS-specific mechanism. The **Secured input module** protects the passphrase from key grabbers and malware.
+
+3. **Representation layer** is the **UI window** which notifies the user about the details of transactions and necessary actions. The **interface window** is initiated from **Security layer**. Once the user inputs the correct passphrase, it sends the permission to the **Signing module** to unlock the demanded key. Passphrase input field is protected by the secured input module. **Security layer** decrypts the given key with the correct passphrase entered by the user. In this instance **Signing module** can operate with the open private key, for example it can extract information, sign transactions, therefore responding to given requests.
+
+# Useful links
+
+This page contains links to the scholarly and publicistic sources that we find useful concerning KeyChain.
+
+- Building a Transaction By Hand: [guide](https://klmoney.wordpress.com/bitcoin-dissecting-transactions-part-2-building-a-transaction-by-hand/)
+
+- Cryptography Docs: [documentation of cryptographic algorithms used in banking](https://www.adjoint.io/docs/cryptography.html)
+
+- Default nonce function: [blog thread on the topic "What is RFC6979?"](https://www.reddit.com/r/Bitcoin/comments/1mwzo2/what_is_rfc6979/)
+
+- Guide to Elliptic Curve Digital Signatures: [guide with thorough explanations of technical aspects](http://royalforkblog.github.io/2014/09/04/ecc/)
+
+- Доступно о криптографии на эллиптических кривых: [about elliptic curves in Russian](https://habr.com/post/335906/)
diff --git a/source/index.md b/source/index.md
deleted file mode 100644
index 4c1fa8c9f7d..00000000000
--- a/source/index.md
+++ /dev/null
@@ -1,168 +0,0 @@
----
-title: API Reference
-
-language_tabs:
- - shell
- - ruby
- - python
-
-toc_footers:
- - Sign Up for a Developer Key
- - Documentation Powered by Slate
-
-includes:
- - errors
-
-search: true
----
-
-# Introduction
-
-Welcome to the Kittn API! You can use our API to access Kittn API endpoints, which can get information on various cats, kittens, and breeds in our database.
-
-We have language bindings in Shell, Ruby, and Python! You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.
-
-This example API documentation page was created with [Slate](http://github.com/tripit/slate). Feel free to edit it and use it as a base for your own API's documentation.
-
-# Authentication
-
-> To authorize, use this code:
-
-```ruby
-require 'kittn'
-
-api = Kittn::APIClient.authorize!('meowmeowmeow')
-```
-
-```python
-import kittn
-
-api = kittn.authorize('meowmeowmeow')
-```
-
-```shell
-# With shell, you can just pass the correct header with each request
-curl "api_endpoint_here"
- -H "Authorization: meowmeowmeow"
-```
-
-> Make sure to replace `meowmeowmeow` with your API key.
-
-Kittn uses API keys to allow access to the API. You can register a new Kittn API key at our [developer portal](http://example.com/developers).
-
-Kittn expects for the API key to be included in all API requests to the server in a header that looks like the following:
-
-`Authorization: meowmeowmeow`
-
-
-
-# Kittens
-
-## Get All Kittens
-
-```ruby
-require 'kittn'
-
-api = Kittn::APIClient.authorize!('meowmeowmeow')
-api.kittens.get
-```
-
-```python
-import kittn
-
-api = kittn.authorize('meowmeowmeow')
-api.kittens.get()
-```
-
-```shell
-curl "http://example.com/api/kittens"
- -H "Authorization: meowmeowmeow"
-```
-
-> The above command returns JSON structured like this:
-
-```json
-[
- {
- "id": 1,
- "name": "Fluffums",
- "breed": "calico",
- "fluffiness": 6,
- "cuteness": 7
- },
- {
- "id": 2,
- "name": "Isis",
- "breed": "unknown",
- "fluffiness": 5,
- "cuteness": 10
- }
-]
-```
-
-This endpoint retrieves all kittens.
-
-### HTTP Request
-
-`GET http://example.com/api/kittens`
-
-### Query Parameters
-
-Parameter | Default | Description
---------- | ------- | -----------
-include_cats | false | If set to true, the result will also include cats.
-available | true | If set to false, the result will include kittens that have already been adopted.
-
-
-
-## Get a Specific Kitten
-
-```ruby
-require 'kittn'
-
-api = Kittn::APIClient.authorize!('meowmeowmeow')
-api.kittens.get(2)
-```
-
-```python
-import kittn
-
-api = kittn.authorize('meowmeowmeow')
-api.kittens.get(2)
-```
-
-```shell
-curl "http://example.com/api/kittens/2"
- -H "Authorization: meowmeowmeow"
-```
-
-> The above command returns JSON structured like this:
-
-```json
-{
- "id": 2,
- "name": "Isis",
- "breed": "unknown",
- "fluffiness": 5,
- "cuteness": 10
-}
-```
-
-This endpoint retrieves a specific kitten.
-
-
-
-### HTTP Request
-
-`GET http://example.com/kittens/`
-
-### URL Parameters
-
-Parameter | Description
---------- | -----------
-ID | The ID of the kitten to retrieve
-
diff --git a/source/javascripts/all.js b/source/javascripts/all.js
index ffaa9b01307..5f5d4067ba6 100644
--- a/source/javascripts/all.js
+++ b/source/javascripts/all.js
@@ -1,4 +1,2 @@
-//= require ./lib/_energize
-//= require ./app/_lang
+//= require ./all_nosearch
//= require ./app/_search
-//= require ./app/_toc
diff --git a/source/javascripts/all_nosearch.js b/source/javascripts/all_nosearch.js
index 818bc4e5095..b18c1d833d4 100644
--- a/source/javascripts/all_nosearch.js
+++ b/source/javascripts/all_nosearch.js
@@ -1,3 +1,16 @@
//= require ./lib/_energize
-//= require ./app/_lang
//= require ./app/_toc
+//= require ./app/_lang
+
+$(function() {
+ loadToc($('#toc'), '.toc-link', '.toc-list-h2', 10);
+ setupLanguages($('body').data('languages'));
+ $('.content').imagesLoaded( function() {
+ window.recacheHeights();
+ window.refreshToc();
+ });
+});
+
+window.onpopstate = function() {
+ activateLanguage(getLanguageFromQueryString());
+};
diff --git a/source/javascripts/app/_lang.js b/source/javascripts/app/_lang.js
index 1a124bb68ae..0fbaaef7046 100644
--- a/source/javascripts/app/_lang.js
+++ b/source/javascripts/app/_lang.js
@@ -1,3 +1,5 @@
+//= require ../lib/_jquery
+
/*
Copyright 2008-2013 Concur Technologies, Inc.
@@ -13,13 +15,14 @@ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
*/
-(function (global) {
+;(function () {
'use strict';
var languages = [];
- global.setupLanguages = setupLanguages;
- global.activateLanguage = activateLanguage;
+ window.setupLanguages = setupLanguages;
+ window.activateLanguage = activateLanguage;
+ window.getLanguageFromQueryString = getLanguageFromQueryString;
function activateLanguage(language) {
if (!language) return;
@@ -28,11 +31,13 @@ under the License.
$(".lang-selector a").removeClass('active');
$(".lang-selector a[data-language-name='" + language + "']").addClass('active');
for (var i=0; i < languages.length; i++) {
- $(".highlight." + languages[i]).hide();
+ $(".highlight.tab-" + languages[i]).hide();
+ $(".lang-specific." + languages[i]).hide();
}
- $(".highlight." + language).show();
+ $(".highlight.tab-" + language).show();
+ $(".lang-specific." + language).show();
- global.toc.calculateHeights();
+ window.recacheHeights();
// scroll to the new location of the position
if ($(window.location.hash).get(0)) {
@@ -93,7 +98,7 @@ under the License.
// gets the language set in the query string
function getLanguageFromQueryString() {
if (location.search.length >= 1) {
- var language = parseURL(location.search).language
+ var language = parseURL(location.search).language;
if (language) {
return language;
} else if (jQuery.inArray(location.search.substr(1), languages) != -1) {
@@ -155,8 +160,5 @@ under the License.
activateLanguage(language);
return false;
});
- window.onpopstate = function() {
- activateLanguage(getLanguageFromQueryString());
- };
});
-})(window);
+})();
diff --git a/source/javascripts/app/_search.js b/source/javascripts/app/_search.js
index 91f38a04edf..9ff4233c18a 100644
--- a/source/javascripts/app/_search.js
+++ b/source/javascripts/app/_search.js
@@ -1,10 +1,13 @@
//= require ../lib/_lunr
+//= require ../lib/_jquery
//= require ../lib/_jquery.highlight
-(function () {
+;(function () {
'use strict';
var content, searchResults;
var highlightOpts = { element: 'span', className: 'search-highlight' };
+ var searchDelay = 0;
+ var timeoutHandle = 0;
var index = new lunr.Index();
@@ -26,24 +29,44 @@
body: body.text()
});
});
+
+ determineSearchDelay();
+ }
+ function determineSearchDelay() {
+ if(index.tokenStore.length>5000) {
+ searchDelay = 300;
+ }
}
function bind() {
content = $('.content');
searchResults = $('.search-results');
- $('#input-search').on('keyup', search);
+ $('#input-search').on('keyup',function(e) {
+ var wait = function() {
+ return function(executingFunction, waitTime){
+ clearTimeout(timeoutHandle);
+ timeoutHandle = setTimeout(executingFunction, waitTime);
+ };
+ }();
+ wait(function(){
+ search(e);
+ }, searchDelay );
+ });
}
function search(event) {
+
+ var searchInput = $('#input-search')[0];
+
unhighlight();
searchResults.addClass('visible');
// ESC clears the field
- if (event.keyCode === 27) this.value = '';
+ if (event.keyCode === 27) searchInput.value = '';
- if (this.value) {
- var results = index.search(this.value).filter(function(r) {
+ if (searchInput.value) {
+ var results = index.search(searchInput.value).filter(function(r) {
return r.score > 0.0001;
});
@@ -53,10 +76,10 @@
var elem = document.getElementById(result.ref);
searchResults.append("