├── lib ├── stackprof-webnav.rb └── stackprof-webnav │ ├── version.rb │ ├── views │ ├── invalid_dump.haml │ ├── graph.haml │ ├── error.haml │ ├── file.haml │ ├── layout.haml │ ├── index.haml │ ├── method.haml │ ├── overview.haml │ └── flamegraph.haml │ ├── public │ ├── css │ │ ├── application.css │ │ ├── code.css │ │ ├── normalize.css │ │ └── foundation.min.css │ ├── overview.js │ ├── lib │ │ └── string_score.js │ └── flamegraph.js │ ├── dump.rb │ ├── presenter.rb │ └── server.rb ├── Procfile ├── screenshots ├── file.png ├── method.png ├── overview.png ├── callgraph.png ├── directory.png └── flamegraph.png ├── spec ├── fixtures │ ├── test.dump │ └── test-raw.dump ├── spec_helper.rb ├── helpers.rb └── integration_spec.rb ├── Gemfile ├── .gitignore ├── Rakefile ├── bin └── stackprof-webnav ├── LICENSE.txt ├── .github └── workflows │ └── ruby.yml ├── stackprof-webnav.gemspec ├── README.md └── Gemfile.lock /lib/stackprof-webnav.rb: -------------------------------------------------------------------------------- 1 | require_relative 'stackprof-webnav/server' 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec stackprof-webnav -b -p $PORT 2 | -------------------------------------------------------------------------------- /screenshots/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alisnic/stackprof-webnav/HEAD/screenshots/file.png -------------------------------------------------------------------------------- /screenshots/method.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alisnic/stackprof-webnav/HEAD/screenshots/method.png -------------------------------------------------------------------------------- /screenshots/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alisnic/stackprof-webnav/HEAD/screenshots/overview.png -------------------------------------------------------------------------------- /spec/fixtures/test.dump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alisnic/stackprof-webnav/HEAD/spec/fixtures/test.dump -------------------------------------------------------------------------------- /screenshots/callgraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alisnic/stackprof-webnav/HEAD/screenshots/callgraph.png -------------------------------------------------------------------------------- /screenshots/directory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alisnic/stackprof-webnav/HEAD/screenshots/directory.png -------------------------------------------------------------------------------- /screenshots/flamegraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alisnic/stackprof-webnav/HEAD/screenshots/flamegraph.png -------------------------------------------------------------------------------- /lib/stackprof-webnav/version.rb: -------------------------------------------------------------------------------- 1 | module StackProf 2 | module Webnav 3 | VERSION = '1.0.4' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/fixtures/test-raw.dump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alisnic/stackprof-webnav/HEAD/spec/fixtures/test-raw.dump -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in stackprof-webnav.gemspec 4 | gemspec 5 | 6 | gem 'pry' 7 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/views/invalid_dump.haml: -------------------------------------------------------------------------------- 1 | %h3 StackProf Navigator - #{@action} 2 | %hr 3 | 4 | %h4 Unable to open the specified file as a dump 5 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/views/graph.haml: -------------------------------------------------------------------------------- 1 | #graph 2 | %a{:href => url_for("/overview")} 3 | %button Back to overview 4 | %div 5 | %img{src: url_for("/graph.svg")} 6 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/views/error.haml: -------------------------------------------------------------------------------- 1 | %h1 Oops, something went wrong 2 | 3 | %h2 4 | = @error.class 5 | \- 6 | = @error.message 7 | 8 | %h3 Are you looking at a valid stackprof dump? 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | tags 19 | .DS_Store 20 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/views/file.haml: -------------------------------------------------------------------------------- 1 | %h3 StackProf Navigator - #{@path} 2 | %hr 3 | 4 | %a{:href => '/'} ← Back to overview 5 | %br 6 | %br 7 | 8 | - if @data 9 | .code_linenums 10 | - @data[:lineinfo].each do |l| 11 | %span= l 12 | != @data[:code] 13 | - else 14 | %h3 Unable to load file, was the dump captured on another machine? 15 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/views/layout.haml: -------------------------------------------------------------------------------- 1 | %html 2 | %head 3 | %title Stackprof navigator 4 | %link(rel="stylesheet" href="/css/normalize.css") 5 | %link(rel="stylesheet" href="/css/foundation.min.css") 6 | %link(rel="stylesheet" href="/css/code.css") 7 | %link(rel="stylesheet" href="/css/application.css") 8 | 9 | %body 10 | = yield 11 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/views/index.haml: -------------------------------------------------------------------------------- 1 | %h3 StackProf Navigator 2 | %hr 3 | 4 | %p 5 | Listing dumps in 6 | %b= @directory 7 | 8 | %table.centered 9 | %thead 10 | %th Filename 11 | %th Modified 12 | 13 | %tbody 14 | - @files.each do |file| 15 | %tr 16 | %td 17 | %a{href: url_for("/overview", dump: file.path)}= file.name 18 | %td= file.modified 19 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['APP_ENV'] = 'test' 2 | $LOAD_PATH.unshift File.join(Dir.pwd, "lib") 3 | 4 | require "stackprof-webnav" 5 | require "rspec" 6 | require_relative 'helpers' 7 | 8 | RSpec.configure do |config| 9 | config.include Helpers 10 | config.backtrace_exclusion_patterns << /.gem/ 11 | 12 | config.after(:each) do 13 | StackProf::Webnav::Server.cmd_options = {} 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/public/css/application.css: -------------------------------------------------------------------------------- 1 | table { 2 | width: 100%; 3 | } 4 | 5 | pre code { 6 | color: black; 7 | } 8 | 9 | input.filter { 10 | max-width: 68%; 11 | margin: 0 0 0 1rem; 12 | display: inline; 13 | } 14 | 15 | #graph > a { 16 | position: absolute; 17 | } 18 | #graph > div { 19 | overflow: scroll; 20 | height: 100%; 21 | } 22 | #graph > div > img { 23 | max-width: initial; 24 | } 25 | -------------------------------------------------------------------------------- /spec/helpers.rb: -------------------------------------------------------------------------------- 1 | require 'rack/test' 2 | 3 | module Helpers 4 | def build_app(options={}) 5 | StackProf::Webnav::Server.cmd_options = options 6 | Rack::Test::Session.new(Rack::MockSession.new(StackProf::Webnav::Server)) 7 | end 8 | 9 | def fixture_path(name) 10 | File.join(File.dirname(__FILE__), "fixtures", name) 11 | end 12 | 13 | def build_presenter(path) 14 | report = StackProf::Report.new(Marshal.load(File.read(path))) 15 | StackProf::Webnav::Presenter.new(report) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "stackprof" 3 | require "rack/test" 4 | 5 | task :make_dump do 6 | class DummyA 7 | def work 8 | sleep 0.01 9 | end 10 | end 11 | 12 | class DummyB 13 | def work 14 | DummyA.new.work 15 | end 16 | end 17 | 18 | StackProf.run(mode: :cpu, out: 'spec/fixtures/test.dump') do 19 | 1000.times { DummyB.new.work } 20 | end 21 | 22 | StackProf.run(mode: :cpu, raw: true, out: 'spec/fixtures/test-raw.dump') do 23 | 1000.times { DummyB.new.work } 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/dump.rb: -------------------------------------------------------------------------------- 1 | class Dump 2 | attr_reader :path 3 | attr_accessor :flamegraph_json, :graph_data 4 | def initialize(path) 5 | @path = path 6 | end 7 | 8 | def checksum 9 | @checksum ||= Digest::SHA1.file(@path) 10 | end 11 | 12 | def content 13 | @content ||= File.open(@path).read 14 | end 15 | 16 | def path=(new_path) 17 | @path = new_path 18 | check_checksum! 19 | end 20 | 21 | def check_checksum! 22 | return unless @checksum 23 | 24 | if Digest::SHA1.file(@path) != checksum 25 | puts "\n\nFile reloaded" 26 | @checksum, @content = nil, nil 27 | end 28 | end 29 | 30 | def flame_graph_path 31 | @path + ".#{checksum}.flames.json" 32 | end 33 | 34 | def graph_path 35 | @path + ".#{checksum}.digraph.dot" 36 | end 37 | 38 | def graph_image_path 39 | @path + ".#{checksum}.graph.svg" 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /bin/stackprof-webnav: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'optparse' 3 | require 'rack' 4 | 5 | require_relative '../lib/stackprof-webnav' 6 | 7 | options = { 8 | :addr => "127.0.0.1", 9 | :port => 9292 10 | } 11 | 12 | parser = OptionParser.new(ARGV) do |o| 13 | o.banner = "Usage: stackprof-webnav [-f localfile.dump]|[-d directory]|[-o ADDR]|[-p NUMBER]" 14 | o.on('-f [LOCALFILE]', 'Local file path to dump') {|filepath| options[:filepath] = filepath } 15 | o.on('-d [DIRECTORY]', 'path to a directory with dumps') {|directory| options[:directory] = directory} 16 | o.on('-o [ADDR]', 'Server addr bind') {|addr| options[:addr] = addr } 17 | o.on('-p [PORT]', 'Server port') {|port| options[:port] = port } 18 | end 19 | 20 | parser.parse! 21 | 22 | server = StackProf::Webnav::Server 23 | server.cmd_options = options 24 | 25 | Rack::Handler.pick(['thin', 'webrick']).run server.new, :Host => options[:addr], :Port => options[:port] 26 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/public/overview.js: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | const table = document.getElementsByTagName('table')[0] 3 | const input = document.getElementsByClassName('filter')[0] 4 | const rows = {} 5 | const filterKeys = Array.from(document.querySelectorAll('tr[data-filter-key]')).map(el => { 6 | key = el.dataset.filterKey 7 | rows[key] = el 8 | return key 9 | }) 10 | const scores = {} 11 | const computeScores = () => { 12 | const searchString = input.value 13 | filterKeys.forEach(filterKey => scores[filterKey] = stringScore(filterKey, searchString)) 14 | } 15 | 16 | const redrawTable = (sorted) => { 17 | const newBody = document.createElement('tbody') 18 | sorted.forEach(key => newBody.appendChild(rows[key])) 19 | table.removeChild(table.getElementsByTagName('tbody')[0]) 20 | table.appendChild(newBody) 21 | } 22 | 23 | input.addEventListener('input', () => { 24 | computeScores() 25 | redrawTable( 26 | filterKeys.filter(k => scores[k] !== 0).sort((a, b) => scores[b] - scores[a]) 27 | ) 28 | }) 29 | })() 30 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/public/css/code.css: -------------------------------------------------------------------------------- 1 | pre, code, .repl input, .repl .prompt span, textarea, .code_linenums { 2 | font-family: menlo, lucida console, monospace; 3 | font-size: 8pt; 4 | } 5 | 6 | .code_linenums { 7 | background:#f1f1f1; 8 | padding-top:10px; 9 | padding-bottom:9px; 10 | float:left; 11 | } 12 | 13 | .code_linenums span{ 14 | display:block; 15 | padding:0 12px; 16 | height: 16px; 17 | } 18 | 19 | .code .highlight, .code_linenums .highlight { 20 | background: rgba(220, 30, 30, 0.1); 21 | -webkit-animation: highlight 400ms linear 1; 22 | -moz-animation: highlight 400ms linear 1; 23 | animation: highlight 400ms linear 1; 24 | } 25 | 26 | .code, .console, .unavailable { 27 | background: #fff; 28 | padding: 5px; 29 | box-shadow: inset 3px 3px 3px rgba(0, 0, 0, 0.1), inset 0 0 0 1px rgba(0, 0, 0, 0.1); 30 | } 31 | 32 | .code { 33 | margin-bottom: -1px; 34 | border-top-left-radius:2px; 35 | padding: 10px 0; 36 | overflow: auto; 37 | } 38 | 39 | .code pre{ 40 | padding-left:12px; 41 | min-height:16px; 42 | margin: 0; 43 | height: 16px; 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Andrei Lisnic 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/views/method.haml: -------------------------------------------------------------------------------- 1 | %h3 StackProf Navigator - #{@action} (#{@frames.count} frames) 2 | %hr 3 | 4 | %p 5 | %a{:href => url_for("/overview")} ← Back to overview 6 | 7 | - @frames.each do |frame| 8 | %a{:href => url_for("/file", path: frame[:location])} 9 | %button.btn 10 | Browse 11 | = frame[:location] 12 | 13 | - if frame[:callers].any? 14 | %table 15 | %thead 16 | %th Callers 17 | %th 18 | %th 19 | %th 20 | %tbody 21 | - frame[:callers].each do |caller| 22 | %tr 23 | %td 24 | %td= caller[:weight] 25 | %td= caller[:pct] 26 | %td 27 | %a{:href => url_for("/method", name: caller[:method])} 28 | &= caller[:method] 29 | 30 | - if frame[:callees].any? 31 | %table 32 | %thead 33 | %th Callees 34 | %th 35 | %th 36 | %th 37 | %tbody 38 | - frame[:callees].each do |caller| 39 | %tr 40 | %td 41 | %td= caller[:weight] 42 | %td= caller[:pct] 43 | %td 44 | %a{:href => url_for("/method", name: caller[:method])} 45 | &= caller[:method] 46 | 47 | %h4 Code 48 | != frame[:source] 49 | %hr 50 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/views/overview.haml: -------------------------------------------------------------------------------- 1 | %h3 StackProf Navigator - #{@action} (#{@frames.count} frames) 2 | %hr 3 | 4 | %p 5 | Viewing dump 6 | %b= current_dump.path 7 | 8 | %a{href: "/"} 9 | %button.btn Browse directory 10 | 11 | 12 | %a{href: url_for("/graph")} 13 | %button.btn View call graph 14 | 15 | - if current_report.data[:raw] 16 | %a{href: url_for("/flamegraph")} 17 | %button View flamegraph 18 | - else 19 | %a{href: "https://github.com/tmm1/stackprof#all-options"} 20 | %button.secondary Flamegraph is not available 21 | 22 | %table.centered 23 | %thead 24 | %th Total 25 | %th % 26 | %th Samples 27 | %th % 28 | %th 29 | Method 30 | %input.filter{:placeholder => "filter methods", :type => "text"}/ 31 | 32 | %tbody 33 | - @frames.each do |frame| 34 | - # Filter keys are preprocessed to better match acronyms. 35 | %tr{"data-filter-key" => frame[:method].gsub(/[A-Z]/, " \\0").gsub(/[\W_]+/, " ").strip} 36 | %td= frame[:total] 37 | %td= frame[:total_pct] 38 | %td= frame[:samples] 39 | %td= frame[:samples_pct] 40 | %td 41 | %a{:href => url_for("/method", name: frame[:method])} 42 | &= frame[:method] 43 | 44 | %script{src: "lib/string_score.js"} 45 | %script{src: "overview.js"} 46 | -------------------------------------------------------------------------------- /.github/workflows/ruby.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake 6 | # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby 7 | 8 | name: Ruby 9 | 10 | on: 11 | push: 12 | branches: [ master ] 13 | pull_request: 14 | branches: [ master ] 15 | 16 | jobs: 17 | test: 18 | 19 | runs-on: ubuntu-latest 20 | strategy: 21 | matrix: 22 | ruby-version: 23 | - '2.7' 24 | - '3.0' 25 | 26 | steps: 27 | - uses: actions/checkout@v2 28 | - name: Set up Ruby 29 | # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, 30 | # change this to (see https://github.com/ruby/setup-ruby#versioning): 31 | # uses: ruby/setup-ruby@v1 32 | uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e 33 | with: 34 | ruby-version: ${{ matrix.ruby-version }} 35 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically 36 | - name: Install graphviz 37 | run: sudo apt-get install -y graphviz 38 | - name: Run tests 39 | run: bundle exec rspec 40 | -------------------------------------------------------------------------------- /stackprof-webnav.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'stackprof-webnav/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "stackprof-webnav" 8 | spec.version = StackProf::Webnav::VERSION 9 | spec.authors = ["Andrei Lisnic"] 10 | spec.email = ["andrei.lisnic@gmail.com"] 11 | spec.summary = %q{View stackprof dumps in a web UI} 12 | spec.description = %q{Provides the ability to analyze StackProf dumps} 13 | spec.homepage = "https://github.com/alisnic/stackprof-webnav" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files -z`.split("\x0") 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib"] 20 | 21 | spec.bindir = 'bin' 22 | spec.executables << 'stackprof-webnav' 23 | 24 | spec.add_dependency "sinatra", ">= 2.1.0", "< 5.0.0" 25 | spec.add_dependency "haml", "~> 5.1.2" 26 | spec.add_dependency "stackprof", ">= 0.2.13" 27 | spec.add_dependency "better_errors", "~> 1.1.0" 28 | spec.add_dependency "rackup", ">= 1.0.0", "< 3.0.0" 29 | spec.add_dependency "ruby-graphviz", "~> 1.2.4" 30 | spec.add_dependency "sinatra-contrib", ">= 2.1.0", "< 5.0.0" 31 | spec.add_dependency "webrick", "~> 1.7" 32 | spec.add_development_dependency "bundler", "~> 2.2" 33 | spec.add_development_dependency "rake", "~> 10.1" 34 | spec.add_development_dependency "rspec", "~> 3.9.0" 35 | spec.add_development_dependency "rack-test", ">= 1.1.0", "< 3.0.0" 36 | end 37 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/views/flamegraph.haml: -------------------------------------------------------------------------------- 1 | %a{:href => url_for("/overview")} 2 | %button Back to overview 3 | 4 | %br 5 | %br 6 | 7 | :css 8 | body { 9 | margin: 0; 10 | padding: 0; 11 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; 12 | font-size: 10pt; 13 | } 14 | .overview-container { 15 | position: relative; 16 | } 17 | .overview { 18 | cursor: col-resize; 19 | } 20 | .overview-viewport-overlay { 21 | position: absolute; 22 | top: 0; 23 | left: 0; 24 | width: 1; 25 | height: 1; 26 | background-color: rgba(0, 0, 0, 0.25); 27 | transform-origin: top left; 28 | cursor: -moz-grab; 29 | cursor: -webkit-grab; 30 | cursor: grab; 31 | } 32 | .moving { 33 | cursor: -moz-grabbing; 34 | cursor: -webkit-grabbing; 35 | cursor: grabbing; 36 | } 37 | .info { 38 | display: block; 39 | height: 40px; 40 | margin: 3px 6px; 41 | margin-right: 206px; 42 | padding: 3px 6px; 43 | line-height: 18px; 44 | } 45 | .legend { 46 | display: block; 47 | float: right; 48 | width: 195px; 49 | max-height: 100%; 50 | overflow-y: scroll; 51 | } 52 | .legend > div { 53 | padding: 6px; 54 | clear: right; 55 | } 56 | .legend > div span { 57 | opacity: 0.75; 58 | display: block; 59 | text-align: right; 60 | } 61 | .legend > div .name { 62 | max-width: 70%; 63 | word-wrap: break-word; 64 | } 65 | %script{:src => "flamegraph.js"} 66 | .legend 67 | .overview-container 68 | %canvas.overview 69 | .overview-viewport-overlay 70 | .info 71 | %div{:style => "float: right; text-align: right"} 72 | .samples 73 | .exclusive 74 | .frame 75 | .file 76 | %canvas.flamegraph 77 | %script{:src => "/flames.json?dump=#{params[:dump]}"} 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StackProf Web navigator 2 | 3 | Provides a web ui to inspect stackprof dumps. 4 | 5 | ## Screenshots 6 | 7 | ![](https://github.com/alisnic/stackprof-webnav/blob/master/screenshots/directory.png?raw=true) 8 | 9 | ![](https://github.com/alisnic/stackprof-webnav/blob/master/screenshots/overview.png?raw=true) 10 | 11 | ![](https://github.com/alisnic/stackprof-webnav/blob/master/screenshots/method.png?raw=true) 12 | 13 | ![](https://github.com/alisnic/stackprof-webnav/blob/master/screenshots/file.png?raw=true) 14 | 15 | ![](https://github.com/alisnic/stackprof-webnav/blob/master/screenshots/callgraph.png?raw=true) 16 | 17 | ![](https://github.com/alisnic/stackprof-webnav/blob/master/screenshots/flamegraph.png?raw=true) 18 | 19 | ## Usage 20 | 21 | ### Install the gem 22 | ```bash 23 | $ gem install stackprof-webnav 24 | ``` 25 | 26 | ### Run the server 27 | 28 | ```bash 29 | $ stackprof-webnav 30 | ``` 31 | 32 | By default it will list all files in the current directory. You can click in the 33 | web interface to try to open any file as a dump. 34 | 35 | Additionally, you can list another directory by passing the `-d` flag: 36 | 37 | ```bash 38 | $ stackprof-webnav -d /my/folder/with/dumps 39 | ``` 40 | 41 | Or launch it with a dump preselected: 42 | 43 | ```bash 44 | $ stackprof-webnav -f /path/to/stackprof.dump 45 | ``` 46 | 47 | See [stackprof gem][create-dump] homepage to learn how to create dumps. 48 | 49 | ### Profit 50 | Open the browser at localhost:9292 51 | 52 | ## Contributing 53 | 54 | 1. Fork it ( http://github.com//stackprof-webnav/fork ) 55 | 2. Create your feature branch (`git checkout -b my-new-feature`) 56 | 3. Commit your changes (`git commit -am 'Add some feature'`) 57 | 4. Push to the branch (`git push origin my-new-feature`) 58 | 5. Create new Pull Request 59 | 60 | [create-dump]: https://github.com/tmm1/stackprof#getting-started 61 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | stackprof-webnav (1.0.3) 5 | better_errors (~> 1.1.0) 6 | haml (~> 5.1.2) 7 | rackup (>= 1.0.0, < 3.0.0) 8 | ruby-graphviz (~> 1.2.4) 9 | sinatra (>= 2.1.0, < 5.0.0) 10 | sinatra-contrib (>= 2.1.0, < 5.0.0) 11 | stackprof (>= 0.2.13) 12 | webrick (~> 1.7) 13 | 14 | GEM 15 | remote: https://rubygems.org/ 16 | specs: 17 | better_errors (1.1.0) 18 | coderay (>= 1.0.0) 19 | erubis (>= 2.6.6) 20 | coderay (1.1.2) 21 | diff-lcs (1.3) 22 | erubis (2.7.0) 23 | haml (5.1.2) 24 | temple (>= 0.8.0) 25 | tilt 26 | method_source (0.9.2) 27 | multi_json (1.15.0) 28 | mustermann (1.1.1) 29 | ruby2_keywords (~> 0.0.1) 30 | pry (0.12.2) 31 | coderay (~> 1.1.0) 32 | method_source (~> 0.9.0) 33 | rack (2.2.3) 34 | rack-protection (2.1.0) 35 | rack 36 | rack-test (1.1.0) 37 | rack (>= 1.0, < 3) 38 | rackup (1.0.0) 39 | rack (< 3) 40 | webrick 41 | rake (10.5.0) 42 | rexml (3.2.5) 43 | rspec (3.9.0) 44 | rspec-core (~> 3.9.0) 45 | rspec-expectations (~> 3.9.0) 46 | rspec-mocks (~> 3.9.0) 47 | rspec-core (3.9.0) 48 | rspec-support (~> 3.9.0) 49 | rspec-expectations (3.9.0) 50 | diff-lcs (>= 1.2.0, < 2.0) 51 | rspec-support (~> 3.9.0) 52 | rspec-mocks (3.9.0) 53 | diff-lcs (>= 1.2.0, < 2.0) 54 | rspec-support (~> 3.9.0) 55 | rspec-support (3.9.0) 56 | ruby-graphviz (1.2.5) 57 | rexml 58 | ruby2_keywords (0.0.5) 59 | sinatra (2.1.0) 60 | mustermann (~> 1.0) 61 | rack (~> 2.2) 62 | rack-protection (= 2.1.0) 63 | tilt (~> 2.0) 64 | sinatra-contrib (2.1.0) 65 | multi_json 66 | mustermann (~> 1.0) 67 | rack-protection (= 2.1.0) 68 | sinatra (= 2.1.0) 69 | tilt (~> 2.0) 70 | stackprof (0.2.17) 71 | temple (0.8.2) 72 | tilt (2.0.10) 73 | webrick (1.7.0) 74 | 75 | PLATFORMS 76 | ruby 77 | 78 | DEPENDENCIES 79 | bundler (~> 2.2) 80 | pry 81 | rack-test (>= 1.1.0, < 3.0.0) 82 | rake (~> 10.1) 83 | rspec (~> 3.9.0) 84 | stackprof-webnav! 85 | 86 | BUNDLED WITH 87 | 2.2.18 88 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/presenter.rb: -------------------------------------------------------------------------------- 1 | require 'better_errors' 2 | require 'stringio' 3 | require 'rexml/document' 4 | 5 | module StackProf 6 | module Webnav 7 | class Presenter 8 | attr_reader :report 9 | def initialize report 10 | @report = report 11 | end 12 | 13 | def file_overview path 14 | buffer = StringIO.new 15 | report.print_file(path, buffer) 16 | data = buffer.string.split("\n").map {|l| l.split('|').first} 17 | 18 | { 19 | :lineinfo => data, 20 | :code => BetterErrors::CodeFormatter::HTML.new(path, 0, 9999).output 21 | } 22 | end 23 | 24 | def overview_frames 25 | report.frames.map do |frame, info| 26 | call, total = info.values_at(:samples, :total_samples) 27 | { 28 | :total => total, 29 | :total_pct => percent(total.to_f/report.overall_samples), 30 | :samples => call, 31 | :samples_pct => percent(call.to_f/report.overall_samples), 32 | :method => info[:name] 33 | } 34 | end 35 | end 36 | 37 | def method_info name 38 | name = /#{Regexp.escape name}/ unless Regexp === name 39 | frames = report.frames.select do |frame, info| 40 | info[:name] =~ name 41 | end.map do |frame, info| 42 | file, line = info.values_at(:file, :line) 43 | 44 | { 45 | :callers => callers(frame, info), 46 | :callees => callees(frame, info), 47 | :location => file, 48 | :source => BetterErrors::CodeFormatter::HTML.new(file, line).output 49 | } 50 | end 51 | end 52 | 53 | private 54 | 55 | def percent value 56 | "%2.2f%%" % (value*100) 57 | end 58 | 59 | def callers frame, info 60 | report.data[:frames].select do |id, other| 61 | other[:edges] && other[:edges].include?(frame) 62 | end.map do |id, other| 63 | [other[:name], other[:edges][frame]] 64 | end.sort_by(&:last).reverse.map do |name, weight| 65 | { 66 | :weight => weight, 67 | :pct => percent(weight.to_f/info[:total_samples]), 68 | :method => name 69 | } 70 | end 71 | end 72 | 73 | def callees frame, info 74 | (info[:edges] || []).map do |k, weight| 75 | [report.data[:frames][k][:name], weight] 76 | end.sort_by { |k,v| -v }.map do |name, weight| 77 | { 78 | :weight => weight, 79 | :pct => percent(weight.to_f/(info[:total_samples]-info[:samples])), 80 | :method => name 81 | } 82 | end 83 | end 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /spec/integration_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | RSpec.describe "integration tests" do 4 | let(:app) { build_app } 5 | let(:presenter) { build_presenter(fixture_path("test.dump")) } 6 | let(:response) { app.last_response } 7 | 8 | after(:each) do 9 | Dir.glob("spec/fixtures/*.flames.json").each {|file| File.delete(file) } 10 | Dir.glob("spec/fixtures/*.digraph.dot").each {|file| File.delete(file) } 11 | Dir.glob("spec/fixtures/*.graph.svg").each {|file| File.delete(file) } 12 | end 13 | 14 | describe "index" do 15 | it "lists the files in a folder" do 16 | app = build_app(directory: "spec/fixtures") 17 | app.get "/" 18 | expect(app.last_response.body).to include("test.dump") 19 | end 20 | 21 | it "redirects to overview if path is specified" do 22 | app = build_app(filepath: fixture_path("test.dump")) 23 | app.get "/" 24 | app.follow_redirect! 25 | expect(app.last_response.body).to include("Dummy") 26 | end 27 | end 28 | 29 | it "does not crash on unknown requests" do 30 | app.get "/favicon.ico" 31 | expect(response.status).to eq(404) 32 | end 33 | 34 | describe "overview" do 35 | it "works with a valid dump" do 36 | app.get "/overview", dump: fixture_path("test.dump") 37 | 38 | frame = presenter.overview_frames.first 39 | expect(response.body).to include(frame[:method]) 40 | end 41 | 42 | it "communicates that flamegraph is not available for dump" do 43 | app.get "/overview", dump: fixture_path("test.dump") 44 | end 45 | 46 | it "does not crash if selected file is not a dump" do 47 | app.get "/overview", dump: "Rakefile" 48 | expect(response.body).to include("Unable to open") 49 | end 50 | end 51 | 52 | it "is able to generate flames.json file" do 53 | app.get "/flames.json", dump: fixture_path("test-raw.dump") 54 | expect(response.body).to include("flamegraph") 55 | end 56 | 57 | it "is able to render graph" do 58 | app.get "/graph.svg", dump: fixture_path("test.dump") 59 | expect(response.get_header("Content-Type")).to eq("image/svg+xml") 60 | expect(response.body.size).to_not eq(0) 61 | end 62 | 63 | it "method page renders information about a method" do 64 | frame = presenter.overview_frames.first 65 | app.get "/method", dump: fixture_path("test.dump"), name: frame[:method] 66 | expect(response.body).to include("Callers") 67 | end 68 | 69 | it "does not crash on a file page" do 70 | frame = presenter.overview_frames.first 71 | method_info = presenter.method_info frame[:method] 72 | 73 | expect { 74 | app.get "/file", 75 | dump: fixture_path("test.dump"), 76 | path: method_info.first[:location] 77 | }.to_not raise_error 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/public/lib/string_score.js: -------------------------------------------------------------------------------- 1 | /* 2 | * string_score.js: Quicksilver-like string scoring algorithm. 3 | * 4 | * Copyright (C) 2009-2011 Joshaven Potter 5 | * Copyright (C) 2010-2011 Yesudeep Mangalapilly 6 | * MIT license: http://www.opensource.org/licenses/mit-license.php 7 | * 8 | * This is a javascript port of the above mentionned, with a tiny change: 9 | * it avoids string monkey patch. 10 | */ 11 | ;(function(global) { 12 | global.stringScore = (string, abbreviation) => { 13 | // Perfect match if the spring equals the abbreviation 14 | if (string == abbreviation) return 1.0 15 | 16 | // Initializing variables. 17 | let string_length = string.length 18 | let abbreviation_length = abbreviation.length 19 | let total_character_score = 0 20 | 21 | // Awarded only if the string and the abbreviation have a common prefix. 22 | let should_award_common_prefix_bonus = 0 23 | 24 | // # Sum character scores 25 | 26 | // Add up scores for each character in the abbreviation. 27 | for (let i = 0, c = abbreviation[i]; i < abbreviation_length; c = abbreviation[++i]) { 28 | // Find the index of current character (case-insensitive) in remaining part of string. 29 | let index_c_lowercase = string.indexOf(c.toLowerCase()) 30 | let index_c_uppercase = string.indexOf(c.toUpperCase()) 31 | let min_index = Math.min(index_c_lowercase, index_c_uppercase) 32 | let index_in_string = min_index > -1 ? min_index : Math.max(index_c_lowercase, index_c_uppercase) 33 | 34 | // # Identical Strings 35 | // Bail out if current character is not found (case-insensitive) in remaining part of string. 36 | if (index_in_string == -1) return 0 37 | 38 | // Set base score for current character. 39 | let character_score = 0.1 40 | 41 | 42 | // # Case-match bonus 43 | // If the current abbreviation character has the same case 44 | // as that of the character in the string, we add a bonus. 45 | if (string[index_in_string] == c) character_score += 0.1 46 | 47 | // # Consecutive character match and common prefix bonuses 48 | // Increase the score when each consecutive character of 49 | // the abbreviation matches the first character of the 50 | // remaining string. 51 | if (index_in_string == 0) { 52 | character_score += 0.8 53 | // String and abbreviation have common prefix, so award bonus. 54 | if (i == 0) should_award_common_prefix_bonus = 1 55 | } 56 | 57 | // # Acronym bonus 58 | // Typing the first character of an acronym is as 59 | // though you preceded it with two perfect character 60 | // matches. 61 | if (string.charAt(index_in_string - 1) == ' ') // TODO: better accro 62 | character_score += 0.8 // * Math.min(index_in_string, 5) # Cap bonus at 0.4 * 5 63 | 64 | // Left trim the matched part of the string 65 | // (forces sequential matching). 66 | string = string.substring(index_in_string + 1, string_length) 67 | 68 | // Add to total character score. 69 | total_character_score += character_score 70 | } 71 | 72 | let abbreviation_score = total_character_score / abbreviation_length 73 | 74 | let final_score = ((abbreviation_score * (abbreviation_length / string_length)) + abbreviation_score) / 2 75 | if (should_award_common_prefix_bonus && final_score + 0.1 < 1) final_score += 0.1 76 | return final_score 77 | } 78 | })(window) 79 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/server.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | require 'haml' 3 | require 'stackprof' 4 | require_relative 'presenter' 5 | require_relative 'dump' 6 | require 'sinatra/reloader' if development? 7 | require 'ruby-graphviz' 8 | 9 | module StackProf 10 | module Webnav 11 | class Server < Sinatra::Application 12 | class << self 13 | attr_accessor :cmd_options 14 | end 15 | 16 | configure :development do 17 | register Sinatra::Reloader 18 | end 19 | 20 | configure :production do 21 | set :show_exceptions, false 22 | end 23 | 24 | error do 25 | @error = env['sinatra.error'] 26 | haml :error 27 | end 28 | 29 | before do 30 | unless request.path_info == '/' 31 | if params[:dump] && params[:dump] != current_dump.path 32 | current_dump.path = params[:dump] 33 | end 34 | end 35 | end 36 | 37 | helpers do 38 | def current_dump 39 | Thread.current[:cache] = {} 40 | Thread.current[params[:dump]] ||= Dump.new(params[:dump]) 41 | end 42 | 43 | def current_report 44 | StackProf::Report.new( 45 | Marshal.load(current_dump.content) 46 | ) 47 | end 48 | 49 | def presenter 50 | Presenter.new(current_report) 51 | end 52 | 53 | def ensure_file_generated(path, &block) 54 | return if File.exist?(path) 55 | File.open(path, 'wb', &block) 56 | end 57 | 58 | def url_for(path, options={}) 59 | query = URI.encode_www_form({dump: params[:dump]}.merge(options)) 60 | path + "?" + query 61 | end 62 | end 63 | 64 | get '/' do 65 | if Server.cmd_options[:filepath] 66 | query = URI.encode_www_form(dump: Server.cmd_options[:filepath]) 67 | next redirect to("/overview?#{query}") 68 | end 69 | 70 | @directory = File.expand_path(Server.cmd_options[:directory] || '.') 71 | @files = Dir.entries(@directory).sort.map do |file| 72 | path = File.expand_path(file, @directory) 73 | 74 | OpenStruct.new( 75 | name: file, 76 | path: path, 77 | modified: File.mtime(path) 78 | ) 79 | end.select do |file| 80 | File.file?(file.path) 81 | end 82 | 83 | haml :index 84 | end 85 | 86 | get '/overview' do 87 | @action = "overview" 88 | 89 | begin 90 | @frames = presenter.overview_frames 91 | haml :overview 92 | rescue => error 93 | haml :invalid_dump 94 | end 95 | end 96 | 97 | get '/flames.json' do 98 | ensure_file_generated(current_dump.flame_graph_path) do |file| 99 | current_report.print_flamegraph(file, true, true) 100 | end 101 | 102 | send_file(current_dump.flame_graph_path, type: 'text/javascript') 103 | end 104 | 105 | get '/graph.svg' do 106 | ensure_file_generated(current_dump.graph_path) do |file| 107 | current_report.print_graphviz({}, file) 108 | end 109 | 110 | ensure_file_generated(current_dump.graph_image_path) do |file| 111 | GraphViz 112 | .parse(current_dump.graph_path) 113 | .output(svg: current_dump.graph_image_path) 114 | end 115 | 116 | send_file(current_dump.graph_image_path, type: 'image/svg+xml') 117 | end 118 | 119 | get '/graph' do 120 | haml :graph 121 | end 122 | 123 | get '/flamegraph' do 124 | haml :flamegraph 125 | end 126 | 127 | get '/method' do 128 | @action = params[:name] 129 | @frames = presenter.method_info(params[:name]) 130 | haml :method 131 | end 132 | 133 | get '/file' do 134 | path = params[:path] 135 | 136 | if File.exist?(path) 137 | @path = path 138 | @data = presenter.file_overview(path) 139 | else 140 | @data = nil 141 | end 142 | 143 | haml :file 144 | end 145 | end 146 | end 147 | end 148 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/public/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.1.2 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /** 8 | * Correct `block` display not defined in IE 8/9. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | main, 20 | nav, 21 | section, 22 | summary { 23 | display: block; 24 | } 25 | 26 | /** 27 | * Correct `inline-block` display not defined in IE 8/9. 28 | */ 29 | 30 | audio, 31 | canvas, 32 | video { 33 | display: inline-block; 34 | } 35 | 36 | /** 37 | * Prevent modern browsers from displaying `audio` without controls. 38 | * Remove excess height in iOS 5 devices. 39 | */ 40 | 41 | audio:not([controls]) { 42 | display: none; 43 | height: 0; 44 | } 45 | 46 | /** 47 | * Address `[hidden]` styling not present in IE 8/9. 48 | * Hide the `template` element in IE, Safari, and Firefox < 22. 49 | */ 50 | 51 | [hidden], 52 | template { 53 | display: none; 54 | } 55 | 56 | script { 57 | display: none !important; 58 | } 59 | 60 | /* ========================================================================== 61 | Base 62 | ========================================================================== */ 63 | 64 | /** 65 | * 1. Set default font family to sans-serif. 66 | * 2. Prevent iOS text size adjust after orientation change, without disabling 67 | * user zoom. 68 | */ 69 | 70 | html { 71 | font-family: sans-serif; /* 1 */ 72 | -ms-text-size-adjust: 100%; /* 2 */ 73 | -webkit-text-size-adjust: 100%; /* 2 */ 74 | } 75 | 76 | /** 77 | * Remove default margin. 78 | */ 79 | 80 | body { 81 | margin: 0; 82 | } 83 | 84 | /* ========================================================================== 85 | Links 86 | ========================================================================== */ 87 | 88 | /** 89 | * Remove the gray background color from active links in IE 10. 90 | */ 91 | 92 | a { 93 | background: transparent; 94 | } 95 | 96 | /** 97 | * Address `outline` inconsistency between Chrome and other browsers. 98 | */ 99 | 100 | a:focus { 101 | outline: thin dotted; 102 | } 103 | 104 | /** 105 | * Improve readability when focused and also mouse hovered in all browsers. 106 | */ 107 | 108 | a:active, 109 | a:hover { 110 | outline: 0; 111 | } 112 | 113 | /* ========================================================================== 114 | Typography 115 | ========================================================================== */ 116 | 117 | /** 118 | * Address variable `h1` font-size and margin within `section` and `article` 119 | * contexts in Firefox 4+, Safari 5, and Chrome. 120 | */ 121 | 122 | h1 { 123 | font-size: 2em; 124 | margin: 0.67em 0; 125 | } 126 | 127 | /** 128 | * Address styling not present in IE 8/9, Safari 5, and Chrome. 129 | */ 130 | 131 | abbr[title] { 132 | border-bottom: 1px dotted; 133 | } 134 | 135 | /** 136 | * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. 137 | */ 138 | 139 | b, 140 | strong { 141 | font-weight: bold; 142 | } 143 | 144 | /** 145 | * Address styling not present in Safari 5 and Chrome. 146 | */ 147 | 148 | dfn { 149 | font-style: italic; 150 | } 151 | 152 | /** 153 | * Address differences between Firefox and other browsers. 154 | */ 155 | 156 | hr { 157 | -moz-box-sizing: content-box; 158 | box-sizing: content-box; 159 | height: 0; 160 | } 161 | 162 | /** 163 | * Address styling not present in IE 8/9. 164 | */ 165 | 166 | mark { 167 | background: #ff0; 168 | color: #000; 169 | } 170 | 171 | /** 172 | * Correct font family set oddly in Safari 5 and Chrome. 173 | */ 174 | 175 | code, 176 | kbd, 177 | pre, 178 | samp { 179 | font-family: monospace, serif; 180 | font-size: 1em; 181 | } 182 | 183 | /** 184 | * Improve readability of pre-formatted text in all browsers. 185 | */ 186 | 187 | pre { 188 | white-space: pre-wrap; 189 | } 190 | 191 | /** 192 | * Set consistent quote types. 193 | */ 194 | 195 | q { 196 | quotes: "\201C" "\201D" "\2018" "\2019"; 197 | } 198 | 199 | /** 200 | * Address inconsistent and variable font size in all browsers. 201 | */ 202 | 203 | small { 204 | font-size: 80%; 205 | } 206 | 207 | /** 208 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 209 | */ 210 | 211 | sub, 212 | sup { 213 | font-size: 75%; 214 | line-height: 0; 215 | position: relative; 216 | vertical-align: baseline; 217 | } 218 | 219 | sup { 220 | top: -0.5em; 221 | } 222 | 223 | sub { 224 | bottom: -0.25em; 225 | } 226 | 227 | /* ========================================================================== 228 | Embedded content 229 | ========================================================================== */ 230 | 231 | /** 232 | * Remove border when inside `a` element in IE 8/9. 233 | */ 234 | 235 | img { 236 | border: 0; 237 | } 238 | 239 | /** 240 | * Correct overflow displayed oddly in IE 9. 241 | */ 242 | 243 | svg:not(:root) { 244 | overflow: hidden; 245 | } 246 | 247 | /* ========================================================================== 248 | Figures 249 | ========================================================================== */ 250 | 251 | /** 252 | * Address margin not present in IE 8/9 and Safari 5. 253 | */ 254 | 255 | figure { 256 | margin: 0; 257 | } 258 | 259 | /* ========================================================================== 260 | Forms 261 | ========================================================================== */ 262 | 263 | /** 264 | * Define consistent border, margin, and padding. 265 | */ 266 | 267 | fieldset { 268 | border: 1px solid #c0c0c0; 269 | margin: 0 2px; 270 | padding: 0.35em 0.625em 0.75em; 271 | } 272 | 273 | /** 274 | * 1. Correct `color` not being inherited in IE 8/9. 275 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 276 | */ 277 | 278 | legend { 279 | border: 0; /* 1 */ 280 | padding: 0; /* 2 */ 281 | } 282 | 283 | /** 284 | * 1. Correct font family not being inherited in all browsers. 285 | * 2. Correct font size not being inherited in all browsers. 286 | * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. 287 | */ 288 | 289 | button, 290 | input, 291 | select, 292 | textarea { 293 | font-family: inherit; /* 1 */ 294 | font-size: 100%; /* 2 */ 295 | margin: 0; /* 3 */ 296 | } 297 | 298 | /** 299 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 300 | * the UA stylesheet. 301 | */ 302 | 303 | button, 304 | input { 305 | line-height: normal; 306 | } 307 | 308 | /** 309 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 310 | * All other form control elements do not inherit `text-transform` values. 311 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. 312 | * Correct `select` style inheritance in Firefox 4+ and Opera. 313 | */ 314 | 315 | button, 316 | select { 317 | text-transform: none; 318 | } 319 | 320 | /** 321 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 322 | * and `video` controls. 323 | * 2. Correct inability to style clickable `input` types in iOS. 324 | * 3. Improve usability and consistency of cursor style between image-type 325 | * `input` and others. 326 | */ 327 | 328 | button, 329 | html input[type="button"], /* 1 */ 330 | input[type="reset"], 331 | input[type="submit"] { 332 | -webkit-appearance: button; /* 2 */ 333 | cursor: pointer; /* 3 */ 334 | } 335 | 336 | /** 337 | * Re-set default cursor for disabled elements. 338 | */ 339 | 340 | button[disabled], 341 | html input[disabled] { 342 | cursor: default; 343 | } 344 | 345 | /** 346 | * 1. Address box sizing set to `content-box` in IE 8/9. 347 | * 2. Remove excess padding in IE 8/9. 348 | */ 349 | 350 | input[type="checkbox"], 351 | input[type="radio"] { 352 | box-sizing: border-box; /* 1 */ 353 | padding: 0; /* 2 */ 354 | } 355 | 356 | /** 357 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 358 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 359 | * (include `-moz` to future-proof). 360 | */ 361 | 362 | input[type="search"] { 363 | -webkit-appearance: textfield; /* 1 */ 364 | -moz-box-sizing: content-box; 365 | -webkit-box-sizing: content-box; /* 2 */ 366 | box-sizing: content-box; 367 | } 368 | 369 | /** 370 | * Remove inner padding and search cancel button in Safari 5 and Chrome 371 | * on OS X. 372 | */ 373 | 374 | input[type="search"]::-webkit-search-cancel-button, 375 | input[type="search"]::-webkit-search-decoration { 376 | -webkit-appearance: none; 377 | } 378 | 379 | /** 380 | * Remove inner padding and border in Firefox 4+. 381 | */ 382 | 383 | button::-moz-focus-inner, 384 | input::-moz-focus-inner { 385 | border: 0; 386 | padding: 0; 387 | } 388 | 389 | /** 390 | * 1. Remove default vertical scrollbar in IE 8/9. 391 | * 2. Improve readability and alignment in all browsers. 392 | */ 393 | 394 | textarea { 395 | overflow: auto; /* 1 */ 396 | vertical-align: top; /* 2 */ 397 | } 398 | 399 | /* ========================================================================== 400 | Tables 401 | ========================================================================== */ 402 | 403 | /** 404 | * Remove most spacing between table cells. 405 | */ 406 | 407 | table { 408 | border-collapse: collapse; 409 | border-spacing: 0; 410 | } 411 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/public/flamegraph.js: -------------------------------------------------------------------------------- 1 | if (typeof Element.prototype.matches !== 'function') { 2 | Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.webkitMatchesSelector || function matches(selector) { 3 | var element = this 4 | var elements = (element.document || element.ownerDocument).querySelectorAll(selector) 5 | var index = 0 6 | 7 | while (elements[index] && elements[index] !== element) { 8 | ++index 9 | } 10 | 11 | return Boolean(elements[index]) 12 | } 13 | } 14 | 15 | if (typeof Element.prototype.closest !== 'function') { 16 | Element.prototype.closest = function closest(selector) { 17 | var element = this 18 | 19 | while (element && element.nodeType === 1) { 20 | if (element.matches(selector)) { 21 | return element 22 | } 23 | 24 | element = element.parentNode 25 | } 26 | 27 | return null 28 | } 29 | } 30 | 31 | if (typeof Object.assign !== 'function') { 32 | (function() { 33 | Object.assign = function(target) { 34 | 'use strict' 35 | // We must check against these specific cases. 36 | if (target === undefined || target === null) { 37 | throw new TypeError('Cannot convert undefined or null to object') 38 | } 39 | 40 | var output = Object(target) 41 | for (var index = 1; index < arguments.length; index++) { 42 | var source = arguments[index] 43 | if (source !== undefined && source !== null) { 44 | for (var nextKey in source) { 45 | if (source.hasOwnProperty(nextKey)) { 46 | output[nextKey] = source[nextKey] 47 | } 48 | } 49 | } 50 | } 51 | return output 52 | } 53 | })() 54 | } 55 | 56 | function EventSource() { 57 | var self = this 58 | 59 | self.eventListeners = {} 60 | } 61 | 62 | EventSource.prototype.on = function(name, callback) { 63 | var self = this 64 | 65 | var listeners = self.eventListeners[name] 66 | if (!listeners) 67 | listeners = self.eventListeners[name] = [] 68 | listeners.push(callback) 69 | } 70 | 71 | EventSource.prototype.dispatch = function(name, data) { 72 | var self = this 73 | 74 | var listeners = self.eventListeners[name] || [] 75 | listeners.forEach(function(c) { 76 | requestAnimationFrame(function() { c(data) }) 77 | }) 78 | } 79 | 80 | function CanvasView(canvas) { 81 | var self = this 82 | 83 | self.canvas = canvas 84 | } 85 | 86 | CanvasView.prototype.setDimensions = function(width, height) { 87 | var self = this 88 | 89 | if (self.resizeRequestID) 90 | cancelAnimationFrame(self.resizeRequestID) 91 | 92 | self.resizeRequestID = requestAnimationFrame(self.setDimensionsNow.bind(self, width, height)) 93 | } 94 | 95 | CanvasView.prototype.setDimensionsNow = function(width, height) { 96 | var self = this 97 | 98 | if (width === self.width && height === self.height) 99 | return 100 | 101 | self.width = width 102 | self.height = height 103 | 104 | self.canvas.style.width = width 105 | self.canvas.style.height = height 106 | 107 | var ratio = window.devicePixelRatio || 1 108 | self.canvas.width = width * ratio 109 | self.canvas.height = height * ratio 110 | 111 | var ctx = self.canvas.getContext('2d') 112 | ctx.setTransform(1, 0, 0, 1, 0, 0) 113 | ctx.scale(ratio, ratio) 114 | 115 | self.repaintNow() 116 | } 117 | 118 | CanvasView.prototype.paint = function() { 119 | } 120 | 121 | CanvasView.prototype.scheduleRepaint = function() { 122 | var self = this 123 | 124 | if (self.repaintRequestID) 125 | return 126 | 127 | self.repaintRequestID = requestAnimationFrame(function() { 128 | self.repaintRequestID = null 129 | self.repaintNow() 130 | }) 131 | } 132 | 133 | CanvasView.prototype.repaintNow = function() { 134 | var self = this 135 | 136 | self.canvas.getContext('2d').clearRect(0, 0, self.width, self.height) 137 | self.paint() 138 | 139 | if (self.repaintRequestID) { 140 | cancelAnimationFrame(self.repaintRequestID) 141 | self.repaintRequestID = null 142 | } 143 | } 144 | 145 | function Flamechart(canvas, data, dataRange, info) { 146 | var self = this 147 | 148 | CanvasView.call(self, canvas) 149 | EventSource.call(self) 150 | 151 | self.canvas = canvas 152 | self.data = data 153 | self.dataRange = dataRange 154 | self.info = info 155 | 156 | self.viewport = { 157 | x: dataRange.minX, 158 | y: dataRange.minY, 159 | width: dataRange.maxX - dataRange.minX, 160 | height: dataRange.maxY - dataRange.minY, 161 | } 162 | } 163 | 164 | Flamechart.prototype = Object.create(CanvasView.prototype) 165 | Flamechart.prototype.constructor = Flamechart 166 | Object.assign(Flamechart.prototype, EventSource.prototype) 167 | 168 | Flamechart.prototype.xScale = function(x) { 169 | var self = this 170 | return self.widthScale(x - self.viewport.x) 171 | } 172 | 173 | Flamechart.prototype.yScale = function(y) { 174 | var self = this 175 | return self.heightScale(y - self.viewport.y) 176 | } 177 | 178 | Flamechart.prototype.widthScale = function(width) { 179 | var self = this 180 | return width * self.width / self.viewport.width 181 | } 182 | 183 | Flamechart.prototype.heightScale = function(height) { 184 | var self = this 185 | return height * self.height / self.viewport.height 186 | } 187 | 188 | Flamechart.prototype.frameRect = function(f) { 189 | return { 190 | x: f.x, 191 | y: f.y, 192 | width: f.width, 193 | height: 1, 194 | } 195 | } 196 | 197 | Flamechart.prototype.dataToCanvas = function(r) { 198 | var self = this 199 | 200 | return { 201 | x: self.xScale(r.x), 202 | y: self.yScale(r.y), 203 | width: self.widthScale(r.width), 204 | height: self.heightScale(r.height), 205 | } 206 | } 207 | 208 | Flamechart.prototype.setViewport = function(viewport) { 209 | var self = this 210 | 211 | if (self.viewport.x === viewport.x && 212 | self.viewport.y === viewport.y && 213 | self.viewport.width === viewport.width && 214 | self.viewport.height === viewport.height) 215 | return 216 | 217 | self.viewport = viewport 218 | 219 | self.scheduleRepaint() 220 | 221 | self.dispatch('viewportchanged', { current: viewport }) 222 | } 223 | 224 | Flamechart.prototype.paint = function(opacity, frames, gemName) { 225 | var self = this 226 | 227 | var ctx = self.canvas.getContext('2d') 228 | 229 | ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)' 230 | 231 | if (self.showLabels) { 232 | ctx.textBaseline = 'middle' 233 | ctx.font = '11px ' + getComputedStyle(this.canvas).fontFamily 234 | // W tends to be one of the widest characters (and if the font is truly 235 | // fixed-width then any character will do). 236 | var characterWidth = ctx.measureText('WWWW').width / 4 237 | } 238 | 239 | if (typeof opacity === 'undefined') 240 | opacity = 1 241 | 242 | frames = frames || self.data 243 | 244 | var blocksByColor = {} 245 | 246 | frames.forEach(function(f) { 247 | if (gemName && f.gemName !== gemName) 248 | return 249 | 250 | var r = self.dataToCanvas(self.frameRect(f)) 251 | 252 | if (r.x >= self.width || 253 | r.y >= self.height || 254 | (r.x + r.width) <= 0 || 255 | (r.y + r.height) <= 0) { 256 | return 257 | } 258 | 259 | var i = self.info[f.frame_id] 260 | var color = colorString(i.color, opacity) 261 | var colorBlocks = blocksByColor[color] 262 | if (!colorBlocks) 263 | colorBlocks = blocksByColor[color] = [] 264 | colorBlocks.push({ rect: r, text: f.frame }) 265 | }) 266 | 267 | var textBlocks = [] 268 | 269 | Object.keys(blocksByColor).forEach(function(color) { 270 | ctx.fillStyle = color 271 | 272 | blocksByColor[color].forEach(function(block) { 273 | if (opacity < 1) 274 | ctx.clearRect(block.rect.x, block.rect.y, block.rect.width, block.rect.height) 275 | 276 | ctx.fillRect(block.rect.x, block.rect.y, block.rect.width, block.rect.height) 277 | 278 | if (block.rect.width > 4 && block.rect.height > 4) 279 | ctx.strokeRect(block.rect.x, block.rect.y, block.rect.width, block.rect.height) 280 | 281 | if (!self.showLabels || block.rect.width / characterWidth < 4) 282 | return 283 | 284 | textBlocks.push(block) 285 | }) 286 | }) 287 | 288 | ctx.fillStyle = '#000' 289 | textBlocks.forEach(function(block) { 290 | var text = block.text 291 | var textRect = Object.assign({}, block.rect) 292 | textRect.x += 1 293 | textRect.width -= 2 294 | if (textRect.width < text.length * characterWidth * 0.75) 295 | text = centerTruncate(block.text, Math.floor(textRect.width / characterWidth)) 296 | ctx.fillText(text, textRect.x, textRect.y + textRect.height / 2, textRect.width) 297 | }) 298 | } 299 | 300 | Flamechart.prototype.frameAtPoint = function(x, y) { 301 | var self = this 302 | 303 | return self.data.find(function(d) { 304 | var r = self.dataToCanvas(self.frameRect(d)) 305 | 306 | return r.x <= x 307 | && r.x + r.width >= x 308 | && r.y <= y 309 | && r.y + r.height >= y 310 | }) 311 | } 312 | 313 | function MainFlamechart(canvas, data, dataRange, info) { 314 | var self = this 315 | 316 | Flamechart.call(self, canvas, data, dataRange, info) 317 | 318 | self.showLabels = true 319 | 320 | self.canvas.addEventListener('mousedown', self.onMouseDown.bind(self)) 321 | self.canvas.addEventListener('mousemove', self.onMouseMove.bind(self)) 322 | self.canvas.addEventListener('mouseout', self.onMouseOut.bind(self)) 323 | self.canvas.addEventListener('wheel', self.onWheel.bind(self)) 324 | } 325 | 326 | MainFlamechart.prototype = Object.create(Flamechart.prototype) 327 | 328 | MainFlamechart.prototype.setDimensionsNow = function(width, height) { 329 | var self = this 330 | 331 | var viewport = Object.assign({}, self.viewport) 332 | viewport.height = height / 16 333 | self.setViewport(viewport) 334 | 335 | CanvasView.prototype.setDimensionsNow.call(self, width, height) 336 | } 337 | 338 | MainFlamechart.prototype.onMouseDown = function(e) { 339 | var self = this 340 | 341 | if (e.button !== 0) 342 | return 343 | 344 | captureMouse({ 345 | mouseup: self.onMouseUp.bind(self), 346 | mousemove: self.onMouseMove.bind(self), 347 | }) 348 | 349 | var clientRect = self.canvas.getBoundingClientRect() 350 | var currentX = e.clientX - clientRect.left 351 | var currentY = e.clientY - clientRect.top 352 | 353 | self.dragging = true 354 | self.dragInfo = { 355 | mouse: { x: currentX, y: currentY }, 356 | viewport: { x: self.viewport.x, y: self.viewport.y }, 357 | } 358 | 359 | e.preventDefault() 360 | } 361 | 362 | MainFlamechart.prototype.onMouseUp = function(e) { 363 | var self = this 364 | 365 | if (!self.dragging) 366 | return 367 | 368 | releaseCapture() 369 | 370 | self.dragging = false 371 | e.preventDefault() 372 | } 373 | 374 | MainFlamechart.prototype.onMouseMove = function(e) { 375 | var self = this 376 | 377 | var clientRect = self.canvas.getBoundingClientRect() 378 | var currentX = e.clientX - clientRect.left 379 | var currentY = e.clientY - clientRect.top 380 | 381 | if (self.dragging) { 382 | var viewport = Object.assign({}, self.viewport) 383 | viewport.x = self.dragInfo.viewport.x - (currentX - self.dragInfo.mouse.x) * viewport.width / self.width 384 | viewport.y = self.dragInfo.viewport.y - (currentY - self.dragInfo.mouse.y) * viewport.height / self.height 385 | viewport.x = Math.min(self.dataRange.maxX - viewport.width, Math.max(self.dataRange.minX, viewport.x)) 386 | viewport.y = Math.min(self.dataRange.maxY - viewport.height, Math.max(self.dataRange.minY, viewport.y)) 387 | self.setViewport(viewport) 388 | return 389 | } 390 | 391 | var frame = self.frameAtPoint(currentX, currentY) 392 | self.setHoveredFrame(frame) 393 | } 394 | 395 | MainFlamechart.prototype.onMouseOut = function() { 396 | var self = this 397 | 398 | if (self.dragging) 399 | return 400 | 401 | self.setHoveredFrame(null) 402 | } 403 | 404 | MainFlamechart.prototype.onWheel = function(e) { 405 | var self = this 406 | 407 | var deltaX = e.deltaX 408 | var deltaY = e.deltaY 409 | 410 | if (e.deltaMode == WheelEvent.prototype.DOM_DELTA_LINE) { 411 | deltaX *= 11 412 | deltaY *= 11 413 | } 414 | 415 | if (e.shiftKey) { 416 | if ('webkitDirectionInvertedFromDevice' in e) { 417 | if (e.webkitDirectionInvertedFromDevice) 418 | deltaY *= -1 419 | } else if (/Mac OS X/.test(navigator.userAgent)) { 420 | // Assume that most Mac users have "Scroll direction: Natural" enabled. 421 | deltaY *= -1 422 | } 423 | 424 | var mouseWheelZoomSpeed = 1 / 120 425 | self.handleZoomGesture(Math.pow(1.2, -(deltaY || deltaX) * mouseWheelZoomSpeed), e.offsetX) 426 | e.preventDefault() 427 | return 428 | } 429 | 430 | var viewport = Object.assign({}, self.viewport) 431 | viewport.x += deltaX * viewport.width / (self.dataRange.maxX - self.dataRange.minX) 432 | viewport.x = Math.min(self.dataRange.maxX - viewport.width, Math.max(self.dataRange.minX, viewport.x)) 433 | viewport.y += (deltaY / 8) * viewport.height / (self.dataRange.maxY - self.dataRange.minY) 434 | viewport.y = Math.min(self.dataRange.maxY - viewport.height, Math.max(self.dataRange.minY, viewport.y)) 435 | self.setViewport(viewport) 436 | e.preventDefault() 437 | } 438 | 439 | MainFlamechart.prototype.handleZoomGesture = function(zoom, originX) { 440 | var self = this 441 | 442 | var viewport = Object.assign({}, self.viewport) 443 | var ratioX = originX / self.width 444 | 445 | var newWidth = Math.min(viewport.width / zoom, self.dataRange.maxX - self.dataRange.minX) 446 | viewport.x = Math.max(self.dataRange.minX, viewport.x + (viewport.width - newWidth) * ratioX) 447 | viewport.width = Math.min(newWidth, self.dataRange.maxX - viewport.x) 448 | 449 | self.setViewport(viewport) 450 | } 451 | 452 | MainFlamechart.prototype.setHoveredFrame = function(frame) { 453 | var self = this 454 | 455 | if (frame === self.hoveredFrame) 456 | return 457 | 458 | var previous = self.hoveredFrame 459 | self.hoveredFrame = frame 460 | 461 | self.dispatch('hoveredframechanged', { previous: previous, current: self.hoveredFrame }) 462 | } 463 | 464 | function OverviewFlamechart(container, viewportOverlay, data, dataRange, info) { 465 | var self = this 466 | 467 | Flamechart.call(self, container.querySelector('.overview'), data, dataRange, info) 468 | 469 | self.container = container 470 | 471 | self.showLabels = false 472 | 473 | self.viewportOverlay = viewportOverlay 474 | 475 | self.canvas.addEventListener('mousedown', self.onMouseDown.bind(self)) 476 | self.viewportOverlay.addEventListener('mousedown', self.onOverlayMouseDown.bind(self)) 477 | } 478 | 479 | OverviewFlamechart.prototype = Object.create(Flamechart.prototype) 480 | 481 | OverviewFlamechart.prototype.setViewportOverlayRect = function(r) { 482 | var self = this 483 | 484 | self.viewportOverlayRect = r 485 | 486 | r = self.dataToCanvas(r) 487 | r.width = Math.max(2, r.width) 488 | r.height = Math.max(2, r.height) 489 | 490 | if ('transform' in self.viewportOverlay.style) { 491 | self.viewportOverlay.style.transform = 'translate(' + r.x + 'px, ' + r.y + 'px) scale(' + r.width + ', ' + r.height + ')' 492 | } else { 493 | self.viewportOverlay.style.left = r.x 494 | self.viewportOverlay.style.top = r.y 495 | self.viewportOverlay.style.width = r.width 496 | self.viewportOverlay.style.height = r.height 497 | } 498 | } 499 | 500 | OverviewFlamechart.prototype.onMouseDown = function(e) { 501 | var self = this 502 | 503 | captureMouse({ 504 | mouseup: self.onMouseUp.bind(self), 505 | mousemove: self.onMouseMove.bind(self), 506 | }) 507 | 508 | self.dragging = true 509 | self.dragStartX = e.clientX - self.canvas.getBoundingClientRect().left 510 | 511 | self.handleDragGesture(e) 512 | 513 | e.preventDefault() 514 | } 515 | 516 | OverviewFlamechart.prototype.onMouseUp = function(e) { 517 | var self = this 518 | 519 | if (!self.dragging) 520 | return 521 | 522 | releaseCapture() 523 | 524 | self.dragging = false 525 | 526 | self.handleDragGesture(e) 527 | 528 | e.preventDefault() 529 | } 530 | 531 | OverviewFlamechart.prototype.onMouseMove = function(e) { 532 | var self = this 533 | 534 | if (!self.dragging) 535 | return 536 | 537 | self.handleDragGesture(e) 538 | 539 | e.preventDefault() 540 | } 541 | 542 | OverviewFlamechart.prototype.handleDragGesture = function(e) { 543 | var self = this 544 | 545 | var clientRect = self.canvas.getBoundingClientRect() 546 | var currentX = e.clientX - clientRect.left 547 | var currentY = e.clientY - clientRect.top 548 | 549 | if (self.dragCurrentX === currentX) 550 | return 551 | 552 | self.dragCurrentX = currentX 553 | 554 | var minX = Math.min(self.dragStartX, self.dragCurrentX) 555 | var maxX = Math.max(self.dragStartX, self.dragCurrentX) 556 | 557 | var rect = Object.assign({}, self.viewportOverlayRect) 558 | rect.x = minX / self.width * self.viewport.width + self.viewport.x 559 | rect.width = Math.max(self.viewport.width / 1000, (maxX - minX) / self.width * self.viewport.width) 560 | 561 | rect.y = Math.max(self.viewport.y, Math.min(self.viewport.height - self.viewport.y, currentY / self.height * self.viewport.height + self.viewport.y - rect.height / 2)) 562 | 563 | self.setViewportOverlayRect(rect) 564 | self.dispatch('overlaychanged', { current: self.viewportOverlayRect }) 565 | } 566 | 567 | OverviewFlamechart.prototype.onOverlayMouseDown = function(e) { 568 | var self = this 569 | 570 | captureMouse({ 571 | mouseup: self.onOverlayMouseUp.bind(self), 572 | mousemove: self.onOverlayMouseMove.bind(self), 573 | }) 574 | 575 | self.overlayDragging = true 576 | self.overlayDragInfo = { 577 | mouse: { x: e.clientX, y: e.clientY }, 578 | rect: Object.assign({}, self.viewportOverlayRect), 579 | } 580 | self.viewportOverlay.classList.add('moving') 581 | 582 | self.handleOverlayDragGesture(e) 583 | 584 | e.preventDefault() 585 | } 586 | 587 | OverviewFlamechart.prototype.onOverlayMouseUp = function(e) { 588 | var self = this 589 | 590 | if (!self.overlayDragging) 591 | return 592 | 593 | releaseCapture() 594 | 595 | self.overlayDragging = false 596 | self.viewportOverlay.classList.remove('moving') 597 | 598 | self.handleOverlayDragGesture(e) 599 | 600 | e.preventDefault() 601 | } 602 | 603 | OverviewFlamechart.prototype.onOverlayMouseMove = function(e) { 604 | var self = this 605 | 606 | if (!self.overlayDragging) 607 | return 608 | 609 | self.handleOverlayDragGesture(e) 610 | 611 | e.preventDefault() 612 | } 613 | 614 | OverviewFlamechart.prototype.handleOverlayDragGesture = function(e) { 615 | var self = this 616 | 617 | var deltaX = (e.clientX - self.overlayDragInfo.mouse.x) / self.width * self.viewport.width 618 | var deltaY = (e.clientY - self.overlayDragInfo.mouse.y) / self.height * self.viewport.height 619 | 620 | var rect = Object.assign({}, self.overlayDragInfo.rect) 621 | rect.x += deltaX 622 | rect.y += deltaY 623 | rect.x = Math.max(self.viewport.x, Math.min(self.viewport.x + self.viewport.width - rect.width, rect.x)) 624 | rect.y = Math.max(self.viewport.y, Math.min(self.viewport.y + self.viewport.height - rect.height, rect.y)) 625 | 626 | self.setViewportOverlayRect(rect) 627 | self.dispatch('overlaychanged', { current: self.viewportOverlayRect }) 628 | } 629 | 630 | function FlamegraphView(data, info, sortedGems) { 631 | var self = this 632 | 633 | self.data = data 634 | self.info = info 635 | 636 | self.dataRange = self.computeDataRange() 637 | 638 | self.mainChart = new MainFlamechart(document.querySelector('.flamegraph'), data, self.dataRange, info) 639 | self.overview = new OverviewFlamechart(document.querySelector('.overview-container'), document.querySelector('.overview-viewport-overlay'), data, self.dataRange, info) 640 | self.infoElement = document.querySelector('.info') 641 | 642 | self.mainChart.on('hoveredframechanged', self.onHoveredFrameChanged.bind(self)) 643 | self.mainChart.on('viewportchanged', self.onViewportChanged.bind(self)) 644 | self.overview.on('overlaychanged', self.onOverlayChanged.bind(self)) 645 | 646 | var legend = document.querySelector('.legend') 647 | self.renderLegend(legend, sortedGems) 648 | 649 | legend.addEventListener('mousemove', self.onLegendMouseMove.bind(self)) 650 | legend.addEventListener('mouseout', self.onLegendMouseOut.bind(self)) 651 | 652 | window.addEventListener('resize', self.updateDimensions.bind(self)) 653 | 654 | self.updateDimensions() 655 | } 656 | 657 | FlamegraphView.prototype.updateDimensions = function() { 658 | var self = this 659 | 660 | var margin = {top: 10, right: 10, bottom: 10, left: 10} 661 | var width = window.innerWidth - 200 - margin.left - margin.right 662 | var mainChartHeight = Math.ceil(window.innerHeight * 0.80) - margin.top - margin.bottom 663 | var overviewHeight = Math.floor(window.innerHeight * 0.20) - 60 - margin.top - margin.bottom 664 | 665 | self.mainChart.setDimensions(width + margin.left + margin.right, mainChartHeight + margin.top + margin.bottom) 666 | self.overview.setDimensions(width + margin.left + margin.right, overviewHeight + margin.top + margin.bottom) 667 | self.overview.setViewportOverlayRect(self.mainChart.viewport) 668 | } 669 | 670 | FlamegraphView.prototype.computeDataRange = function() { 671 | var self = this 672 | 673 | var range = { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity } 674 | self.data.forEach(function(d) { 675 | range.minX = Math.min(range.minX, d.x) 676 | range.minY = Math.min(range.minY, d.y) 677 | range.maxX = Math.max(range.maxX, d.x + d.width) 678 | range.maxY = Math.max(range.maxY, d.y + 1) 679 | }) 680 | 681 | return range 682 | } 683 | 684 | FlamegraphView.prototype.onHoveredFrameChanged = function(data) { 685 | var self = this 686 | 687 | self.updateInfo(data.current) 688 | 689 | if (data.previous) 690 | self.repaintFrames(1, self.info[data.previous.frame_id].frames) 691 | 692 | if (data.current) 693 | self.repaintFrames(0.5, self.info[data.current.frame_id].frames) 694 | } 695 | 696 | FlamegraphView.prototype.repaintFrames = function(opacity, frames) { 697 | var self = this 698 | 699 | self.mainChart.paint(opacity, frames) 700 | self.overview.paint(opacity, frames) 701 | } 702 | 703 | FlamegraphView.prototype.updateInfo = function(frame) { 704 | var self = this 705 | 706 | if (!frame) { 707 | self.infoElement.style.backgroundColor = '' 708 | self.infoElement.querySelector('.frame').textContent = '' 709 | self.infoElement.querySelector('.file').textContent = '' 710 | self.infoElement.querySelector('.samples').textContent = '' 711 | self.infoElement.querySelector('.exclusive').textContent = '' 712 | return 713 | } 714 | 715 | var i = self.info[frame.frame_id] 716 | var shortFile = frame.file.replace(/^.+\/(gems|app|lib|config|jobs)/, '$1') 717 | var sData = self.samplePercentRaw(i.samples.length, frame.topFrame ? frame.topFrame.exclusiveCount : 0) 718 | 719 | self.infoElement.style.backgroundColor = colorString(i.color, 1) 720 | self.infoElement.querySelector('.frame').textContent = frame.frame 721 | self.infoElement.querySelector('.file').textContent = shortFile 722 | self.infoElement.querySelector('.samples').textContent = sData[0] + ' samples (' + sData[1] + '%)' 723 | if (sData[3]) 724 | self.infoElement.querySelector('.exclusive').textContent = sData[2] + ' exclusive (' + sData[3] + '%)' 725 | else 726 | self.infoElement.querySelector('.exclusive').textContent = '' 727 | } 728 | 729 | FlamegraphView.prototype.samplePercentRaw = function(samples, exclusive) { 730 | var self = this 731 | 732 | var ret = [samples, ((samples / self.dataRange.maxX) * 100).toFixed(2)] 733 | if (exclusive) 734 | ret = ret.concat([exclusive, ((exclusive / self.dataRange.maxX) * 100).toFixed(2)]) 735 | return ret 736 | } 737 | 738 | FlamegraphView.prototype.onViewportChanged = function(data) { 739 | var self = this 740 | 741 | self.overview.setViewportOverlayRect(data.current) 742 | } 743 | 744 | FlamegraphView.prototype.onOverlayChanged = function(data) { 745 | var self = this 746 | 747 | self.mainChart.setViewport(data.current) 748 | } 749 | 750 | FlamegraphView.prototype.renderLegend = function(element, sortedGems) { 751 | var self = this 752 | 753 | var fragment = document.createDocumentFragment() 754 | 755 | sortedGems.forEach(function(gem) { 756 | var sData = self.samplePercentRaw(gem.samples.length) 757 | var node = document.createElement('div') 758 | node.className = 'legend-gem' 759 | node.setAttribute('data-gem-name', gem.name) 760 | node.style.backgroundColor = colorString(gem.color, 1) 761 | 762 | var span = document.createElement('span') 763 | span.style.float = 'right' 764 | span.textContent = sData[0] + 'x' 765 | span.appendChild(document.createElement('br')) 766 | span.appendChild(document.createTextNode(sData[1] + '%')) 767 | node.appendChild(span) 768 | 769 | var name = document.createElement('div') 770 | name.className = 'name' 771 | name.textContent = gem.name 772 | name.appendChild(document.createElement('br')) 773 | name.appendChild(document.createTextNode('\u00a0')) 774 | node.appendChild(name) 775 | 776 | fragment.appendChild(node) 777 | }) 778 | 779 | element.appendChild(fragment) 780 | } 781 | 782 | FlamegraphView.prototype.onLegendMouseMove = function(e) { 783 | var self = this 784 | 785 | var gemElement = e.target.closest('.legend-gem') 786 | var gemName = gemElement.getAttribute('data-gem-name') 787 | 788 | if (self.hoveredGemName === gemName) 789 | return 790 | 791 | if (self.hoveredGemName) { 792 | self.mainChart.paint(1, null, self.hoveredGemName) 793 | self.overview.paint(1, null, self.hoveredGemName) 794 | } 795 | 796 | self.hoveredGemName = gemName 797 | 798 | self.mainChart.paint(0.5, null, self.hoveredGemName) 799 | self.overview.paint(0.5, null, self.hoveredGemName) 800 | } 801 | 802 | FlamegraphView.prototype.onLegendMouseOut = function() { 803 | var self = this 804 | 805 | if (!self.hoveredGemName) 806 | return 807 | 808 | self.mainChart.paint(1, null, self.hoveredGemName) 809 | self.overview.paint(1, null, self.hoveredGemName) 810 | self.hoveredGemName = null 811 | } 812 | 813 | var capturingListeners = null 814 | function captureMouse(listeners) { 815 | if (capturingListeners) 816 | releaseCapture() 817 | 818 | for (var name in listeners) 819 | document.addEventListener(name, listeners[name], true) 820 | capturingListeners = listeners 821 | } 822 | 823 | function releaseCapture() { 824 | if (!capturingListeners) 825 | return 826 | 827 | for (var name in capturingListeners) 828 | document.removeEventListener(name, capturingListeners[name], true) 829 | capturingListeners = null 830 | } 831 | 832 | function guessGem(frame) { 833 | var split = frame.split('/gems/') 834 | if (split.length === 1) { 835 | split = frame.split('/app/') 836 | if (split.length === 1) { 837 | split = frame.split('/lib/') 838 | } else { 839 | return split[split.length - 1].split('/')[0] 840 | } 841 | 842 | split = split[Math.max(split.length - 2, 0)].split('/') 843 | return split[split.length - 1].split(':')[0] 844 | } 845 | else 846 | { 847 | return split[split.length - 1].split('/')[0].split('-', 2)[0] 848 | } 849 | } 850 | 851 | function color() { 852 | var r = parseInt(205 + Math.random() * 50) 853 | var g = parseInt(Math.random() * 230) 854 | var b = parseInt(Math.random() * 55) 855 | return [r, g, b] 856 | } 857 | 858 | // http://stackoverflow.com/a/7419630 859 | function rainbow(numOfSteps, step) { 860 | // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distiguishable vibrant markers in Google Maps and other apps. 861 | // Adam Cole, 2011-Sept-14 862 | // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript 863 | var r, g, b 864 | var h = step / numOfSteps 865 | var i = ~~(h * 6) 866 | var f = h * 6 - i 867 | var q = 1 - f 868 | switch (i % 6) { 869 | case 0: r = 1, g = f, b = 0; break 870 | case 1: r = q, g = 1, b = 0; break 871 | case 2: r = 0, g = 1, b = f; break 872 | case 3: r = 0, g = q, b = 1; break 873 | case 4: r = f, g = 0, b = 1; break 874 | case 5: r = 1, g = 0, b = q; break 875 | } 876 | return [Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)] 877 | } 878 | 879 | function colorString(color, opacity) { 880 | if (typeof opacity === 'undefined') 881 | opacity = 1 882 | return 'rgba(' + color.join(',') + ',' + opacity + ')' 883 | } 884 | 885 | // http://stackoverflow.com/questions/1960473/unique-values-in-an-array 886 | function getUnique(orig) { 887 | var o = {} 888 | for (var i = 0; i < orig.length; i++) o[orig[i]] = 1 889 | return Object.keys(o) 890 | } 891 | 892 | function centerTruncate(text, maxLength) { 893 | var charactersToKeep = maxLength - 1 894 | if (charactersToKeep <= 0) 895 | return '' 896 | if (text.length <= charactersToKeep) 897 | return text 898 | 899 | var prefixLength = Math.ceil(charactersToKeep / 2) 900 | var suffixLength = charactersToKeep - prefixLength 901 | var prefix = text.substr(0, prefixLength) 902 | var suffix = suffixLength > 0 ? text.substr(-suffixLength) : '' 903 | 904 | return [prefix, '\u2026', suffix].join('') 905 | } 906 | 907 | function flamegraph(data) { 908 | var info = {} 909 | data.forEach(function(d) { 910 | var i = info[d.frame_id] 911 | if (!i) 912 | info[d.frame_id] = i = {frames: [], samples: [], color: color()} 913 | i.frames.push(d) 914 | for (var j = 0; j < d.width; j++) { 915 | i.samples.push(d.x + j) 916 | } 917 | }) 918 | 919 | // Samples may overlap on the same line 920 | for (var r in info) { 921 | if (info[r].samples) { 922 | info[r].samples = getUnique(info[r].samples) 923 | } 924 | } 925 | 926 | // assign some colors, analyze samples per gem 927 | var gemStats = {} 928 | var topFrames = {} 929 | var lastFrame = {frame: 'd52e04d-df28-41ed-a215-b6ec840a8ea5', x: -1} 930 | 931 | data.forEach(function(d) { 932 | var gem = guessGem(d.file) 933 | var stat = gemStats[gem] 934 | d.gemName = gem 935 | 936 | if (!stat) { 937 | gemStats[gem] = stat = {name: gem, samples: [], frames: []} 938 | } 939 | 940 | stat.frames.push(d.frame_id) 941 | for (var j = 0; j < d.width; j++) { 942 | stat.samples.push(d.x + j) 943 | } 944 | // This assumes the traversal is in order 945 | if (lastFrame.x !== d.x) { 946 | var topFrame = topFrames[lastFrame.frame_id] 947 | if (!topFrame) { 948 | topFrames[lastFrame.frame_id] = topFrame = {exclusiveCount: 0} 949 | } 950 | topFrame.exclusiveCount += 1 951 | lastFrame.topFrame = topFrame 952 | } 953 | lastFrame = d 954 | }) 955 | 956 | var topFrame = topFrames[lastFrame.frame_id] 957 | if (!topFrame) { 958 | topFrames[lastFrame.frame_id] = topFrame = {exclusiveCount: 0} 959 | } 960 | topFrame.exclusiveCount += 1 961 | lastFrame.topFrame = topFrame 962 | 963 | var totalGems = 0 964 | for (var k in gemStats) { 965 | totalGems++ 966 | gemStats[k].samples = getUnique(gemStats[k].samples) 967 | } 968 | 969 | var gemsSorted = Object.keys(gemStats).map(function(k) { return gemStats[k] }) 970 | gemsSorted.sort(function(a, b) { return b.samples.length - a.samples.length }) 971 | 972 | var currentIndex = 0 973 | gemsSorted.forEach(function(stat) { 974 | stat.color = rainbow(totalGems, currentIndex) 975 | currentIndex += 1 976 | 977 | for (var x = 0; x < stat.frames.length; x++) { 978 | info[stat.frames[x]].color = stat.color 979 | } 980 | }) 981 | 982 | new FlamegraphView(data, info, gemsSorted) 983 | } 984 | -------------------------------------------------------------------------------- /lib/stackprof-webnav/public/css/foundation.min.css: -------------------------------------------------------------------------------- 1 | meta.foundation-mq-small{font-family:"/only screen and (max-width: 40em)/";width:0em}meta.foundation-mq-medium{font-family:"/only screen and (min-width:40.063em)/";width:40.063em}meta.foundation-mq-large{font-family:"/only screen and (min-width:64.063em)/";width:64.063em}meta.foundation-mq-xlarge{font-family:"/only screen and (min-width:90.063em)/";width:90.063em}meta.foundation-mq-xxlarge{font-family:"/only screen and (min-width:120.063em)/";width:120.063em}*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}html,body{font-size:100%}body{background:#fff;color:#222;padding:0;margin:0;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-weight:normal;font-style:normal;line-height:1;position:relative;cursor:default}a:hover{cursor:pointer}img,object,embed{max-width:100%;height:auto}object,embed{height:100%}img{-ms-interpolation-mode:bicubic}#map_canvas img,#map_canvas embed,#map_canvas object,.map_canvas img,.map_canvas embed,.map_canvas object{max-width:none !important}.left{float:left !important}.right{float:right !important}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}.text-left{text-align:left !important}.text-right{text-align:right !important}.text-center{text-align:center !important}.text-justify{text-align:justify !important}.hide{display:none}.start{float:left !important}.end{float:right !important}.text-start{text-align:left !important}.text-end{text-align:right !important}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}img{display:inline-block;vertical-align:middle}textarea{height:auto;min-height:50px}select{width:100%}.row{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5rem;*zoom:1}.row:before,.row:after{content:" ";display:table}.row:after{clear:both}.row.collapse>.column,.row.collapse>.columns{position:relative;padding-left:0;padding-right:0;float:left}.row.collapse .row{margin-left:0;margin-right:0}.row .row{width:auto;margin-left:-0.9375rem;margin-right:-0.9375rem;margin-top:0;margin-bottom:0;max-width:none;*zoom:1}.row .row:before,.row .row:after{content:" ";display:table}.row .row:after{clear:both}.row .row.collapse{width:auto;margin:0;max-width:none;*zoom:1}.row .row.collapse:before,.row .row.collapse:after{content:" ";display:table}.row .row.collapse:after{clear:both}.column,.columns{position:relative;padding-left:0.9375rem;padding-right:0.9375rem;width:100%;float:left}@media only screen{.column.small-centered,.columns.small-centered{position:relative;margin-left:auto;margin-right:auto;float:none}.column.small-uncentered,.columns.small-uncentered{margin-left:0;margin-right:0;float:left}.column.small-uncentered.opposite,.columns.small-uncentered.opposite{float:right}.small-push-1{position:relative;left:8.33333%;right:auto}.small-pull-1{position:relative;right:8.33333%;left:auto}.small-push-2{position:relative;left:16.66667%;right:auto}.small-pull-2{position:relative;right:16.66667%;left:auto}.small-push-3{position:relative;left:25%;right:auto}.small-pull-3{position:relative;right:25%;left:auto}.small-push-4{position:relative;left:33.33333%;right:auto}.small-pull-4{position:relative;right:33.33333%;left:auto}.small-push-5{position:relative;left:41.66667%;right:auto}.small-pull-5{position:relative;right:41.66667%;left:auto}.small-push-6{position:relative;left:50%;right:auto}.small-pull-6{position:relative;right:50%;left:auto}.small-push-7{position:relative;left:58.33333%;right:auto}.small-pull-7{position:relative;right:58.33333%;left:auto}.small-push-8{position:relative;left:66.66667%;right:auto}.small-pull-8{position:relative;right:66.66667%;left:auto}.small-push-9{position:relative;left:75%;right:auto}.small-pull-9{position:relative;right:75%;left:auto}.small-push-10{position:relative;left:83.33333%;right:auto}.small-pull-10{position:relative;right:83.33333%;left:auto}.small-push-11{position:relative;left:91.66667%;right:auto}.small-pull-11{position:relative;right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375rem;padding-right:0.9375rem;float:left}.small-1{position:relative;width:8.33333%}.small-2{position:relative;width:16.66667%}.small-3{position:relative;width:25%}.small-4{position:relative;width:33.33333%}.small-5{position:relative;width:41.66667%}.small-6{position:relative;width:50%}.small-7{position:relative;width:58.33333%}.small-8{position:relative;width:66.66667%}.small-9{position:relative;width:75%}.small-10{position:relative;width:83.33333%}.small-11{position:relative;width:91.66667%}.small-12{position:relative;width:100%}[class*="column"]+[class*="column"]:last-child{float:right}[class*="column"]+[class*="column"].end{float:left}.small-offset-0{position:relative;margin-left:0% !important}.small-offset-1{position:relative;margin-left:8.33333% !important}.small-offset-2{position:relative;margin-left:16.66667% !important}.small-offset-3{position:relative;margin-left:25% !important}.small-offset-4{position:relative;margin-left:33.33333% !important}.small-offset-5{position:relative;margin-left:41.66667% !important}.small-offset-6{position:relative;margin-left:50% !important}.small-offset-7{position:relative;margin-left:58.33333% !important}.small-offset-8{position:relative;margin-left:66.66667% !important}.small-offset-9{position:relative;margin-left:75% !important}.small-offset-10{position:relative;margin-left:83.33333% !important}.column.small-reset-order,.columns.small-reset-order{margin-left:0;margin-right:0;left:auto;right:auto;float:left}}@media only screen and (min-width: 40.063em){.column.medium-centered,.columns.medium-centered{position:relative;margin-left:auto;margin-right:auto;float:none}.column.medium-uncentered,.columns.medium-uncentered{margin-left:0;margin-right:0;float:left}.column.medium-uncentered.opposite,.columns.medium-uncentered.opposite{float:right}.medium-push-1{position:relative;left:8.33333%;right:auto}.medium-pull-1{position:relative;right:8.33333%;left:auto}.medium-push-2{position:relative;left:16.66667%;right:auto}.medium-pull-2{position:relative;right:16.66667%;left:auto}.medium-push-3{position:relative;left:25%;right:auto}.medium-pull-3{position:relative;right:25%;left:auto}.medium-push-4{position:relative;left:33.33333%;right:auto}.medium-pull-4{position:relative;right:33.33333%;left:auto}.medium-push-5{position:relative;left:41.66667%;right:auto}.medium-pull-5{position:relative;right:41.66667%;left:auto}.medium-push-6{position:relative;left:50%;right:auto}.medium-pull-6{position:relative;right:50%;left:auto}.medium-push-7{position:relative;left:58.33333%;right:auto}.medium-pull-7{position:relative;right:58.33333%;left:auto}.medium-push-8{position:relative;left:66.66667%;right:auto}.medium-pull-8{position:relative;right:66.66667%;left:auto}.medium-push-9{position:relative;left:75%;right:auto}.medium-pull-9{position:relative;right:75%;left:auto}.medium-push-10{position:relative;left:83.33333%;right:auto}.medium-pull-10{position:relative;right:83.33333%;left:auto}.medium-push-11{position:relative;left:91.66667%;right:auto}.medium-pull-11{position:relative;right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375rem;padding-right:0.9375rem;float:left}.medium-1{position:relative;width:8.33333%}.medium-2{position:relative;width:16.66667%}.medium-3{position:relative;width:25%}.medium-4{position:relative;width:33.33333%}.medium-5{position:relative;width:41.66667%}.medium-6{position:relative;width:50%}.medium-7{position:relative;width:58.33333%}.medium-8{position:relative;width:66.66667%}.medium-9{position:relative;width:75%}.medium-10{position:relative;width:83.33333%}.medium-11{position:relative;width:91.66667%}.medium-12{position:relative;width:100%}[class*="column"]+[class*="column"]:last-child{float:right}[class*="column"]+[class*="column"].end{float:left}.medium-offset-0{position:relative;margin-left:0% !important}.medium-offset-1{position:relative;margin-left:8.33333% !important}.medium-offset-2{position:relative;margin-left:16.66667% !important}.medium-offset-3{position:relative;margin-left:25% !important}.medium-offset-4{position:relative;margin-left:33.33333% !important}.medium-offset-5{position:relative;margin-left:41.66667% !important}.medium-offset-6{position:relative;margin-left:50% !important}.medium-offset-7{position:relative;margin-left:58.33333% !important}.medium-offset-8{position:relative;margin-left:66.66667% !important}.medium-offset-9{position:relative;margin-left:75% !important}.medium-offset-10{position:relative;margin-left:83.33333% !important}.column.medium-reset-order,.columns.medium-reset-order{margin-left:0;margin-right:0;left:auto;right:auto;float:left}.push-1{position:relative;left:8.33333%;right:auto}.pull-1{position:relative;right:8.33333%;left:auto}.push-2{position:relative;left:16.66667%;right:auto}.pull-2{position:relative;right:16.66667%;left:auto}.push-3{position:relative;left:25%;right:auto}.pull-3{position:relative;right:25%;left:auto}.push-4{position:relative;left:33.33333%;right:auto}.pull-4{position:relative;right:33.33333%;left:auto}.push-5{position:relative;left:41.66667%;right:auto}.pull-5{position:relative;right:41.66667%;left:auto}.push-6{position:relative;left:50%;right:auto}.pull-6{position:relative;right:50%;left:auto}.push-7{position:relative;left:58.33333%;right:auto}.pull-7{position:relative;right:58.33333%;left:auto}.push-8{position:relative;left:66.66667%;right:auto}.pull-8{position:relative;right:66.66667%;left:auto}.push-9{position:relative;left:75%;right:auto}.pull-9{position:relative;right:75%;left:auto}.push-10{position:relative;left:83.33333%;right:auto}.pull-10{position:relative;right:83.33333%;left:auto}.push-11{position:relative;left:91.66667%;right:auto}.pull-11{position:relative;right:91.66667%;left:auto}}@media only screen and (min-width: 64.063em){.column.large-centered,.columns.large-centered{position:relative;margin-left:auto;margin-right:auto;float:none}.column.large-uncentered,.columns.large-uncentered{margin-left:0;margin-right:0;float:left}.column.large-uncentered.opposite,.columns.large-uncentered.opposite{float:right}.large-push-1{position:relative;left:8.33333%;right:auto}.large-pull-1{position:relative;right:8.33333%;left:auto}.large-push-2{position:relative;left:16.66667%;right:auto}.large-pull-2{position:relative;right:16.66667%;left:auto}.large-push-3{position:relative;left:25%;right:auto}.large-pull-3{position:relative;right:25%;left:auto}.large-push-4{position:relative;left:33.33333%;right:auto}.large-pull-4{position:relative;right:33.33333%;left:auto}.large-push-5{position:relative;left:41.66667%;right:auto}.large-pull-5{position:relative;right:41.66667%;left:auto}.large-push-6{position:relative;left:50%;right:auto}.large-pull-6{position:relative;right:50%;left:auto}.large-push-7{position:relative;left:58.33333%;right:auto}.large-pull-7{position:relative;right:58.33333%;left:auto}.large-push-8{position:relative;left:66.66667%;right:auto}.large-pull-8{position:relative;right:66.66667%;left:auto}.large-push-9{position:relative;left:75%;right:auto}.large-pull-9{position:relative;right:75%;left:auto}.large-push-10{position:relative;left:83.33333%;right:auto}.large-pull-10{position:relative;right:83.33333%;left:auto}.large-push-11{position:relative;left:91.66667%;right:auto}.large-pull-11{position:relative;right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375rem;padding-right:0.9375rem;float:left}.large-1{position:relative;width:8.33333%}.large-2{position:relative;width:16.66667%}.large-3{position:relative;width:25%}.large-4{position:relative;width:33.33333%}.large-5{position:relative;width:41.66667%}.large-6{position:relative;width:50%}.large-7{position:relative;width:58.33333%}.large-8{position:relative;width:66.66667%}.large-9{position:relative;width:75%}.large-10{position:relative;width:83.33333%}.large-11{position:relative;width:91.66667%}.large-12{position:relative;width:100%}[class*="column"]+[class*="column"]:last-child{float:right}[class*="column"]+[class*="column"].end{float:left}.large-offset-0{position:relative;margin-left:0% !important}.large-offset-1{position:relative;margin-left:8.33333% !important}.large-offset-2{position:relative;margin-left:16.66667% !important}.large-offset-3{position:relative;margin-left:25% !important}.large-offset-4{position:relative;margin-left:33.33333% !important}.large-offset-5{position:relative;margin-left:41.66667% !important}.large-offset-6{position:relative;margin-left:50% !important}.large-offset-7{position:relative;margin-left:58.33333% !important}.large-offset-8{position:relative;margin-left:66.66667% !important}.large-offset-9{position:relative;margin-left:75% !important}.large-offset-10{position:relative;margin-left:83.33333% !important}.column.large-reset-order,.columns.large-reset-order{margin-left:0;margin-right:0;left:auto;right:auto;float:left}.push-1{position:relative;left:8.33333%;right:auto}.pull-1{position:relative;right:8.33333%;left:auto}.push-2{position:relative;left:16.66667%;right:auto}.pull-2{position:relative;right:16.66667%;left:auto}.push-3{position:relative;left:25%;right:auto}.pull-3{position:relative;right:25%;left:auto}.push-4{position:relative;left:33.33333%;right:auto}.pull-4{position:relative;right:33.33333%;left:auto}.push-5{position:relative;left:41.66667%;right:auto}.pull-5{position:relative;right:41.66667%;left:auto}.push-6{position:relative;left:50%;right:auto}.pull-6{position:relative;right:50%;left:auto}.push-7{position:relative;left:58.33333%;right:auto}.pull-7{position:relative;right:58.33333%;left:auto}.push-8{position:relative;left:66.66667%;right:auto}.pull-8{position:relative;right:66.66667%;left:auto}.push-9{position:relative;left:75%;right:auto}.pull-9{position:relative;right:75%;left:auto}.push-10{position:relative;left:83.33333%;right:auto}.pull-10{position:relative;right:83.33333%;left:auto}.push-11{position:relative;left:91.66667%;right:auto}.pull-11{position:relative;right:91.66667%;left:auto}}meta.foundation-mq-topbar{font-family:"/only screen and (min-width:40.063em)/";width:40.063em}.contain-to-grid{width:100%;background:#333}.contain-to-grid .top-bar{margin-bottom:0}.fixed{width:100%;left:0;position:fixed;top:0;z-index:99}.fixed.expanded:not(.top-bar){overflow-y:auto;height:auto;width:100%;max-height:100%}.fixed.expanded:not(.top-bar) .title-area{position:fixed;width:100%;z-index:99}.fixed.expanded:not(.top-bar) .top-bar-section{z-index:98;margin-top:45px}.top-bar{overflow:hidden;height:45px;line-height:45px;position:relative;background:#333;margin-bottom:0}.top-bar ul{margin-bottom:0;list-style:none}.top-bar .row{max-width:none}.top-bar form,.top-bar input{margin-bottom:0}.top-bar input{height:auto;padding-top:.35rem;padding-bottom:.35rem;font-size:0.75rem}.top-bar .button{padding-top:.45rem;padding-bottom:.35rem;margin-bottom:0;font-size:0.75rem}.top-bar .title-area{position:relative;margin:0}.top-bar .name{height:45px;margin:0;font-size:16px}.top-bar .name h1{line-height:45px;font-size:1.0625rem;margin:0}.top-bar .name h1 a{font-weight:normal;color:#fff;width:50%;display:block;padding:0 15px}.top-bar .toggle-topbar{position:absolute;right:0;top:0}.top-bar .toggle-topbar a{color:#fff;text-transform:uppercase;font-size:0.8125rem;font-weight:bold;position:relative;display:block;padding:0 15px;height:45px;line-height:45px}.top-bar .toggle-topbar.menu-icon{right:15px;top:50%;margin-top:-16px;padding-left:40px}.top-bar .toggle-topbar.menu-icon a{height:34px;line-height:33px;padding:0;padding-right:25px;color:#fff;position:relative}.top-bar .toggle-topbar.menu-icon a::after{content:"";position:absolute;right:0;display:block;width:16px;top:0;height:0;-webkit-box-shadow:0 10px 0 1px #fff,0 16px 0 1px #fff,0 22px 0 1px #fff;box-shadow:0 10px 0 1px #fff,0 16px 0 1px #fff,0 22px 0 1px #fff}.top-bar.expanded{height:auto;background:transparent}.top-bar.expanded .title-area{background:#333}.top-bar.expanded .toggle-topbar a{color:#888}.top-bar.expanded .toggle-topbar a span{-webkit-box-shadow:0 10px 0 1px #888,0 16px 0 1px #888,0 22px 0 1px #888;box-shadow:0 10px 0 1px #888,0 16px 0 1px #888,0 22px 0 1px #888}.top-bar-section{left:0;position:relative;width:auto;-webkit-transition:left 300ms ease-out;-moz-transition:left 300ms ease-out;transition:left 300ms ease-out}.top-bar-section ul{width:100%;height:auto;display:block;background:#333;font-size:16px;margin:0}.top-bar-section .divider,.top-bar-section [role="separator"]{border-top:solid 1px #1a1a1a;clear:both;height:1px;width:100%}.top-bar-section ul li>a{display:block;width:100%;color:#fff;padding:12px 0 12px 0;padding-left:15px;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-size:0.8125rem;font-weight:normal;background:#333}.top-bar-section ul li>a.button{background:#008cba;font-size:0.8125rem;padding-right:15px;padding-left:15px}.top-bar-section ul li>a.button:hover{background:#068}.top-bar-section ul li>a.button.secondary{background:#e7e7e7}.top-bar-section ul li>a.button.secondary:hover{background:#cecece}.top-bar-section ul li>a.button.success{background:#43ac6a}.top-bar-section ul li>a.button.success:hover{background:#358854}.top-bar-section ul li>a.button.alert{background:#f04124}.top-bar-section ul li>a.button.alert:hover{background:#d42b0f}.top-bar-section ul li:hover>a{background:#272727;color:#fff}.top-bar-section ul li.active>a{background:#008cba;color:#fff}.top-bar-section ul li.active>a:hover{background:#0078a0}.top-bar-section .has-form{padding:15px}.top-bar-section .has-dropdown{position:relative}.top-bar-section .has-dropdown>a:after{content:"";display:block;width:0;height:0;border:inset 5px;border-color:transparent transparent transparent rgba(255,255,255,0.4);border-left-style:solid;margin-right:15px;margin-top:-4.5px;position:absolute;top:50%;right:0}.top-bar-section .has-dropdown.moved{position:static}.top-bar-section .has-dropdown.moved>.dropdown{display:block}.top-bar-section .dropdown{position:absolute;left:100%;top:0;display:none;z-index:99}.top-bar-section .dropdown li{width:100%;height:auto}.top-bar-section .dropdown li a{font-weight:normal;padding:8px 15px}.top-bar-section .dropdown li a.parent-link{font-weight:normal}.top-bar-section .dropdown li.title h5{margin-bottom:0}.top-bar-section .dropdown li.title h5 a{color:#fff;line-height:22.5px;display:block}.top-bar-section .dropdown li.has-form{padding:8px 15px}.top-bar-section .dropdown li .button{top:auto}.top-bar-section .dropdown label{padding:8px 15px 2px;margin-bottom:0;text-transform:uppercase;color:#777;font-weight:bold;font-size:0.625rem}.js-generated{display:block}@media only screen and (min-width: 40.063em){.top-bar{background:#333;*zoom:1;overflow:visible}.top-bar:before,.top-bar:after{content:" ";display:table}.top-bar:after{clear:both}.top-bar .toggle-topbar{display:none}.top-bar .title-area{float:left}.top-bar .name h1 a{width:auto}.top-bar input,.top-bar .button{font-size:0.875rem;position:relative;top:7px}.top-bar.expanded{background:#333}.contain-to-grid .top-bar{max-width:62.5rem;margin:0 auto;margin-bottom:0}.top-bar-section{-webkit-transition:none 0 0;-moz-transition:none 0 0;transition:none 0 0;left:0 !important}.top-bar-section ul{width:auto;height:auto !important;display:inline}.top-bar-section ul li{float:left}.top-bar-section ul li .js-generated{display:none}.top-bar-section li.hover>a:not(.button){background:#272727;color:#fff}.top-bar-section li:not(.has-form) a:not(.button){padding:0 15px;line-height:45px;background:#333}.top-bar-section li:not(.has-form) a:not(.button):hover{background:#272727}.top-bar-section .has-dropdown>a{padding-right:35px !important}.top-bar-section .has-dropdown>a:after{content:"";display:block;width:0;height:0;border:inset 5px;border-color:rgba(255,255,255,0.4) transparent transparent transparent;border-top-style:solid;margin-top:-2.5px;top:22.5px}.top-bar-section .has-dropdown.moved{position:relative}.top-bar-section .has-dropdown.moved>.dropdown{display:none}.top-bar-section .has-dropdown.hover>.dropdown,.top-bar-section .has-dropdown.not-click:hover>.dropdown{display:block}.top-bar-section .has-dropdown .dropdown li.has-dropdown>a:after{border:none;content:"\00bb";top:1rem;margin-top:-2px;right:5px;line-height:1.2}.top-bar-section .dropdown{left:0;top:auto;background:transparent;min-width:100%}.top-bar-section .dropdown li a{color:#fff;line-height:1;white-space:nowrap;padding:12px 15px;background:#333}.top-bar-section .dropdown li label{white-space:nowrap;background:#333}.top-bar-section .dropdown li .dropdown{left:100%;top:0}.top-bar-section>ul>.divider,.top-bar-section>ul>[role="separator"]{border-bottom:none;border-top:none;border-right:solid 1px #4e4e4e;clear:none;height:45px;width:0}.top-bar-section .has-form{background:#333;padding:0 15px;height:45px}.top-bar-section .right li .dropdown{left:auto;right:0}.top-bar-section .right li .dropdown li .dropdown{right:100%}.top-bar-section .left li .dropdown{right:auto;left:0}.top-bar-section .left li .dropdown li .dropdown{left:100%}.no-js .top-bar-section ul li:hover>a{background:#272727;color:#fff}.no-js .top-bar-section ul li:active>a{background:#008cba;color:#fff}.no-js .top-bar-section .has-dropdown:hover>.dropdown{display:block}}.breadcrumbs{display:block;padding:0.5625rem 0.875rem 0.5625rem;overflow:hidden;margin-left:0;list-style:none;border-style:solid;border-width:1px;background-color:#f4f4f4;border-color:#dcdcdc;-webkit-border-radius:3px;border-radius:3px}.breadcrumbs>*{margin:0;float:left;font-size:0.6875rem;text-transform:uppercase}.breadcrumbs>*:hover a,.breadcrumbs>*:focus a{text-decoration:underline}.breadcrumbs>* a,.breadcrumbs>* span{text-transform:uppercase;color:#008cba}.breadcrumbs>*.current{cursor:default;color:#333}.breadcrumbs>*.current a{cursor:default;color:#333}.breadcrumbs>*.current:hover,.breadcrumbs>*.current:hover a,.breadcrumbs>*.current:focus,.breadcrumbs>*.current:focus a{text-decoration:none}.breadcrumbs>*.unavailable{color:#999}.breadcrumbs>*.unavailable a{color:#999}.breadcrumbs>*.unavailable:hover,.breadcrumbs>*.unavailable:hover a,.breadcrumbs>*.unavailable:focus,.breadcrumbs>*.unavailable a:focus{text-decoration:none;color:#999;cursor:default}.breadcrumbs>*:before{content:"/";color:#aaa;margin:0 0.75rem;position:relative;top:1px}.breadcrumbs>*:first-child:before{content:" ";margin:0}.alert-box{border-style:solid;border-width:1px;display:block;font-weight:normal;margin-bottom:1.25rem;position:relative;padding:0.875rem 1.5rem 0.875rem 0.875rem;font-size:0.8125rem;background-color:#008cba;border-color:#0078a0;color:#fff}.alert-box .close{font-size:1.375rem;padding:9px 6px 4px;line-height:0;position:absolute;top:50%;margin-top:-0.6875rem;right:0.25rem;color:#333;opacity:0.3}.alert-box .close:hover,.alert-box .close:focus{opacity:0.5}.alert-box.radius{-webkit-border-radius:3px;border-radius:3px}.alert-box.round{-webkit-border-radius:1000px;border-radius:1000px}.alert-box.success{background-color:#43ac6a;border-color:#3a945b;color:#fff}.alert-box.alert{background-color:#f04124;border-color:#de2d0f;color:#fff}.alert-box.secondary{background-color:#e7e7e7;border-color:#c7c7c7;color:#4f4f4f}.alert-box.warning{background-color:#f08a24;border-color:#de770f;color:#fff}.alert-box.info{background-color:#a0d3e8;border-color:#74bfdd;color:#4f4f4f}.inline-list{margin:0 auto 1.0625rem auto;margin-left:-1.375rem;margin-right:0;padding:0;list-style:none;overflow:hidden}.inline-list>li{list-style:none;float:left;margin-left:1.375rem;display:block}.inline-list>li>*{display:block}button,.button{border-style:solid;border-width:0px;cursor:pointer;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-weight:normal;line-height:normal;margin:0 0 1.25rem;position:relative;text-decoration:none;text-align:center;display:inline-block;padding-top:1rem;padding-right:2rem;padding-bottom:1.0625rem;padding-left:2rem;font-size:1rem;background-color:#008cba;border-color:#007095;color:#fff;-webkit-transition:background-color 300ms ease-out;-moz-transition:background-color 300ms ease-out;transition:background-color 300ms ease-out;padding-top:1.0625rem;padding-bottom:1rem;-webkit-appearance:none;border:none;font-weight:normal !important}button:hover,button:focus,.button:hover,.button:focus{background-color:#007095}button:hover,button:focus,.button:hover,.button:focus{color:#fff}button.secondary,.button.secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333}button.secondary:hover,button.secondary:focus,.button.secondary:hover,.button.secondary:focus{background-color:#b9b9b9}button.secondary:hover,button.secondary:focus,.button.secondary:hover,.button.secondary:focus{color:#333}button.success,.button.success{background-color:#43ac6a;border-color:#368a55;color:#fff}button.success:hover,button.success:focus,.button.success:hover,.button.success:focus{background-color:#368a55}button.success:hover,button.success:focus,.button.success:hover,.button.success:focus{color:#fff}button.alert,.button.alert{background-color:#f04124;border-color:#cf2a0e;color:#fff}button.alert:hover,button.alert:focus,.button.alert:hover,.button.alert:focus{background-color:#cf2a0e}button.alert:hover,button.alert:focus,.button.alert:hover,.button.alert:focus{color:#fff}button.large,.button.large{padding-top:1.125rem;padding-right:2.25rem;padding-bottom:1.1875rem;padding-left:2.25rem;font-size:1.25rem}button.small,.button.small{padding-top:0.875rem;padding-right:1.75rem;padding-bottom:0.9375rem;padding-left:1.75rem;font-size:0.8125rem}button.tiny,.button.tiny{padding-top:0.625rem;padding-right:1.25rem;padding-bottom:0.6875rem;padding-left:1.25rem;font-size:0.6875rem}button.expand,.button.expand{padding-right:0;padding-left:0;width:100%}button.left-align,.button.left-align{text-align:left;text-indent:0.75rem}button.right-align,.button.right-align{text-align:right;padding-right:0.75rem}button.radius,.button.radius{-webkit-border-radius:3px;border-radius:3px}button.round,.button.round{-webkit-border-radius:1000px;border-radius:1000px}button.disabled,button[disabled],.button.disabled,.button[disabled]{background-color:#008cba;border-color:#007095;color:#fff;cursor:default;opacity:0.7;-webkit-box-shadow:none;box-shadow:none}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{background-color:#007095}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{color:#fff}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{background-color:#008cba}button.disabled.secondary,button[disabled].secondary,.button.disabled.secondary,.button[disabled].secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333;cursor:default;opacity:0.7;-webkit-box-shadow:none;box-shadow:none}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{background-color:#b9b9b9}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{color:#333}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{background-color:#e7e7e7}button.disabled.success,button[disabled].success,.button.disabled.success,.button[disabled].success{background-color:#43ac6a;border-color:#368a55;color:#fff;cursor:default;opacity:0.7;-webkit-box-shadow:none;box-shadow:none}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{background-color:#368a55}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{color:#fff}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{background-color:#43ac6a}button.disabled.alert,button[disabled].alert,.button.disabled.alert,.button[disabled].alert{background-color:#f04124;border-color:#cf2a0e;color:#fff;cursor:default;opacity:0.7;-webkit-box-shadow:none;box-shadow:none}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{background-color:#cf2a0e}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{color:#fff}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{background-color:#f04124}@media only screen and (min-width: 40.063em){button,.button{display:inline-block}}.button-group{list-style:none;margin:0;*zoom:1}.button-group:before,.button-group:after{content:" ";display:table}.button-group:after{clear:both}.button-group>*{margin:0;float:left}.button-group>*>button,.button-group>* .button{border-right:1px solid;border-color:rgba(255,255,255,0.5)}.button-group>*:last-child button,.button-group>*:last-child .button{border-right:0}.button-group>*:first-child{margin-left:0}.button-group.radius>*>button,.button-group.radius>* .button{border-right:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius>*:last-child button,.button-group.radius>*:last-child .button{border-right:0}.button-group.radius>*:first-child,.button-group.radius>*:first-child>a,.button-group.radius>*:first-child>button,.button-group.radius>*:first-child>.button{-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.button-group.radius>*:last-child,.button-group.radius>*:last-child>a,.button-group.radius>*:last-child>button,.button-group.radius>*:last-child>.button{-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px;-webkit-border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.button-group.round>*>button,.button-group.round>* .button{border-right:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round>*:last-child button,.button-group.round>*:last-child .button{border-right:0}.button-group.round>*:first-child,.button-group.round>*:first-child>a,.button-group.round>*:first-child>button,.button-group.round>*:first-child>.button{-moz-border-radius-bottomleft:1000px;-moz-border-radius-topleft:1000px;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.button-group.round>*:last-child,.button-group.round>*:last-child>a,.button-group.round>*:last-child>button,.button-group.round>*:last-child>.button{-moz-border-radius-topright:1000px;-moz-border-radius-bottomright:1000px;-webkit-border-top-right-radius:1000px;-webkit-border-bottom-right-radius:1000px;border-top-right-radius:1000px;border-bottom-right-radius:1000px}.button-group.even-2 li{width:50%}.button-group.even-2 li>button,.button-group.even-2 li .button{border-right:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-2 li:last-child button,.button-group.even-2 li:last-child .button{border-right:0}.button-group.even-2 li button,.button-group.even-2 li .button{width:100%}.button-group.even-3 li{width:33.33333%}.button-group.even-3 li>button,.button-group.even-3 li .button{border-right:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-3 li:last-child button,.button-group.even-3 li:last-child .button{border-right:0}.button-group.even-3 li button,.button-group.even-3 li .button{width:100%}.button-group.even-4 li{width:25%}.button-group.even-4 li>button,.button-group.even-4 li .button{border-right:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-4 li:last-child button,.button-group.even-4 li:last-child .button{border-right:0}.button-group.even-4 li button,.button-group.even-4 li .button{width:100%}.button-group.even-5 li{width:20%}.button-group.even-5 li>button,.button-group.even-5 li .button{border-right:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-5 li:last-child button,.button-group.even-5 li:last-child .button{border-right:0}.button-group.even-5 li button,.button-group.even-5 li .button{width:100%}.button-group.even-6 li{width:16.66667%}.button-group.even-6 li>button,.button-group.even-6 li .button{border-right:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-6 li:last-child button,.button-group.even-6 li:last-child .button{border-right:0}.button-group.even-6 li button,.button-group.even-6 li .button{width:100%}.button-group.even-7 li{width:14.28571%}.button-group.even-7 li>button,.button-group.even-7 li .button{border-right:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-7 li:last-child button,.button-group.even-7 li:last-child .button{border-right:0}.button-group.even-7 li button,.button-group.even-7 li .button{width:100%}.button-group.even-8 li{width:12.5%}.button-group.even-8 li>button,.button-group.even-8 li .button{border-right:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-8 li:last-child button,.button-group.even-8 li:last-child .button{border-right:0}.button-group.even-8 li button,.button-group.even-8 li .button{width:100%}.button-bar{*zoom:1}.button-bar:before,.button-bar:after{content:" ";display:table}.button-bar:after{clear:both}.button-bar .button-group{float:left;margin-right:0.625rem}.button-bar .button-group div{overflow:hidden}.panel{border-style:solid;border-width:1px;border-color:#d8d8d8;margin-bottom:1.25rem;padding:1.25rem;background:#f2f2f2}.panel>:first-child{margin-top:0}.panel>:last-child{margin-bottom:0}.panel h1,.panel h2,.panel h3,.panel h4,.panel h5,.panel h6,.panel p{color:#333}.panel h1,.panel h2,.panel h3,.panel h4,.panel h5,.panel h6{line-height:1;margin-bottom:0.625rem}.panel h1.subheader,.panel h2.subheader,.panel h3.subheader,.panel h4.subheader,.panel h5.subheader,.panel h6.subheader{line-height:1.4}.panel.callout{border-style:solid;border-width:1px;border-color:#b6edff;margin-bottom:1.25rem;padding:1.25rem;background:#ecfaff}.panel.callout>:first-child{margin-top:0}.panel.callout>:last-child{margin-bottom:0}.panel.callout h1,.panel.callout h2,.panel.callout h3,.panel.callout h4,.panel.callout h5,.panel.callout h6,.panel.callout p{color:#333}.panel.callout h1,.panel.callout h2,.panel.callout h3,.panel.callout h4,.panel.callout h5,.panel.callout h6{line-height:1;margin-bottom:0.625rem}.panel.callout h1.subheader,.panel.callout h2.subheader,.panel.callout h3.subheader,.panel.callout h4.subheader,.panel.callout h5.subheader,.panel.callout h6.subheader{line-height:1.4}.panel.callout a{color:#008cba}.panel.radius{-webkit-border-radius:3px;border-radius:3px}.dropdown.button{position:relative;padding-right:3.5625rem}.dropdown.button:before{position:absolute;content:"";width:0;height:0;display:block;border-style:solid;border-color:#fff transparent transparent transparent;top:50%}.dropdown.button:before{border-width:0.375rem;right:1.40625rem;margin-top:-0.15625rem}.dropdown.button:before{border-color:#fff transparent transparent transparent}.dropdown.button.tiny{padding-right:2.625rem}.dropdown.button.tiny:before{border-width:0.375rem;right:1.125rem;margin-top:-0.125rem}.dropdown.button.tiny:before{border-color:#fff transparent transparent transparent}.dropdown.button.small{padding-right:3.0625rem}.dropdown.button.small:before{border-width:0.4375rem;right:1.3125rem;margin-top:-0.15625rem}.dropdown.button.small:before{border-color:#fff transparent transparent transparent}.dropdown.button.large{padding-right:3.625rem}.dropdown.button.large:before{border-width:0.3125rem;right:1.71875rem;margin-top:-0.15625rem}.dropdown.button.large:before{border-color:#fff transparent transparent transparent}.dropdown.button.secondary:before{border-color:#333 transparent transparent transparent}div.switch{position:relative;padding:0;display:block;overflow:hidden;border-style:solid;border-width:1px;margin-bottom:1.25rem;height:2.25rem;background:#fff;border-color:#ccc}div.switch label{position:relative;left:0;z-index:2;float:left;width:50%;height:100%;margin:0;font-weight:bold;text-align:left;-webkit-transition:all 0.1s ease-out;-moz-transition:all 0.1s ease-out;transition:all 0.1s ease-out}div.switch input{position:absolute;z-index:3;opacity:0;width:100%;height:100%;-moz-appearance:none}div.switch input:hover,div.switch input:focus{cursor:pointer}div.switch span:last-child{position:absolute;top:-1px;left:-1px;z-index:1;display:block;padding:0;border-width:1px;border-style:solid;-webkit-transition:all 0.1s ease-out;-moz-transition:all 0.1s ease-out;transition:all 0.1s ease-out}div.switch input:not(:checked)+label{opacity:0}div.switch input:checked{display:none !important}div.switch input{left:0;display:block !important}div.switch input:first-of-type+label,div.switch input:first-of-type+span+label{left:-50%}div.switch input:first-of-type:checked+label,div.switch input:first-of-type:checked+span+label{left:0%}div.switch input:last-of-type+label,div.switch input:last-of-type+span+label{right:-50%;left:auto;text-align:right}div.switch input:last-of-type:checked+label,div.switch input:last-of-type:checked+span+label{right:0%;left:auto}div.switch span.custom{display:none !important}form.custom div.switch .hidden-field{margin-left:auto;position:absolute;visibility:visible}div.switch label{padding:0;line-height:2.3rem;font-size:0.875rem}div.switch input:first-of-type:checked ~ span:last-child{left:100%;margin-left:-2.1875rem}div.switch span:last-child{width:2.25rem;height:2.25rem}div.switch span:last-child{border-color:#b3b3b3;background:#fff;background:-moz-linear-gradient(top, #fff 0%, #f2f2f2 100%);background:-webkit-linear-gradient(top, #fff 0%, #f2f2f2 100%);background:linear-gradient(to bottom, #fff 0%, #f2f2f2 100%);-webkit-box-shadow:2px 0 10px 0 rgba(0,0,0,0.07),1000px 0 0 1000px #f3faf6,-2px 0 10px 0 rgba(0,0,0,0.07),-1000px 0 0 1000px #f5f5f5;box-shadow:2px 0 10px 0 rgba(0,0,0,0.07),1000px 0 0 980px #f3faf6,-2px 0 10px 0 rgba(0,0,0,0.07),-1000px 0 0 1000px #f5f5f5}div.switch:hover span:last-child,div.switch:focus span:last-child{background:#fff;background:-moz-linear-gradient(top, #fff 0%, #e6e6e6 100%);background:-webkit-linear-gradient(top, #fff 0%, #e6e6e6 100%);background:linear-gradient(to bottom, #fff 0%, #e6e6e6 100%)}div.switch:active{background:transparent}div.switch.large{height:2.75rem}div.switch.large label{padding:0;line-height:2.3rem;font-size:1.0625rem}div.switch.large input:first-of-type:checked ~ span:last-child{left:100%;margin-left:-2.6875rem}div.switch.large span:last-child{width:2.75rem;height:2.75rem}div.switch.small{height:1.75rem}div.switch.small label{padding:0;line-height:2.1rem;font-size:0.75rem}div.switch.small input:first-of-type:checked ~ span:last-child{left:100%;margin-left:-1.6875rem}div.switch.small span:last-child{width:1.75rem;height:1.75rem}div.switch.tiny{height:1.375rem}div.switch.tiny label{padding:0;line-height:1.9rem;font-size:0.6875rem}div.switch.tiny input:first-of-type:checked ~ span:last-child{left:100%;margin-left:-1.3125rem}div.switch.tiny span:last-child{width:1.375rem;height:1.375rem}div.switch.radius{-webkit-border-radius:4px;border-radius:4px}div.switch.radius span:last-child{-webkit-border-radius:3px;border-radius:3px}div.switch.round{-webkit-border-radius:1000px;border-radius:1000px}div.switch.round span:last-child{-webkit-border-radius:999px;border-radius:999px}div.switch.round label{padding:0 0.5625rem}@-webkit-keyframes webkitSiblingBugfix{from{position:relative}to{position:relative}}.th{line-height:0;display:inline-block;border:solid 4px #fff;max-width:100%;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,0.2);box-shadow:0 0 0 1px rgba(0,0,0,0.2);-webkit-transition:all 200ms ease-out;-moz-transition:all 200ms ease-out;transition:all 200ms ease-out}.th:hover,.th:focus{-webkit-box-shadow:0 0 6px 1px rgba(0,140,186,0.5);box-shadow:0 0 6px 1px rgba(0,140,186,0.5)}.th.radius{-webkit-border-radius:3px;border-radius:3px}.pricing-table{border:solid 1px #ddd;margin-left:0;margin-bottom:1.25rem}.pricing-table *{list-style:none;line-height:1}.pricing-table .title{background-color:#333;padding:0.9375rem 1.25rem;text-align:center;color:#eee;font-weight:normal;font-size:1rem;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif}.pricing-table .price{background-color:#f6f6f6;padding:0.9375rem 1.25rem;text-align:center;color:#333;font-weight:normal;font-size:2rem;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif}.pricing-table .description{background-color:#fff;padding:0.9375rem;text-align:center;color:#777;font-size:0.75rem;font-weight:normal;line-height:1.4;border-bottom:dotted 1px #ddd}.pricing-table .bullet-item{background-color:#fff;padding:0.9375rem;text-align:center;color:#333;font-size:0.875rem;font-weight:normal;border-bottom:dotted 1px #ddd}.pricing-table .cta-button{background-color:#fff;text-align:center;padding:1.25rem 1.25rem 0}@-webkit-keyframes rotate{from{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(360deg)}}@-moz-keyframes rotate{from{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(360deg)}}@-o-keyframes rotate{from{-o-transform:rotate(0deg)}to{-o-transform:rotate(360deg)}}@keyframes rotate{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}.slideshow-wrapper{position:relative}.slideshow-wrapper ul{list-style-type:none;margin:0}.slideshow-wrapper ul li,.slideshow-wrapper ul li .orbit-caption{display:none}.slideshow-wrapper ul li:first-child{display:block}.slideshow-wrapper .orbit-container{background-color:transparent}.slideshow-wrapper .orbit-container li{display:block}.slideshow-wrapper .orbit-container li .orbit-caption{display:block}.preloader{display:block;width:40px;height:40px;position:absolute;top:50%;left:50%;margin-top:-20px;margin-left:-20px;border:solid 3px;border-color:#555 #fff;-webkit-border-radius:1000px;border-radius:1000px;-webkit-animation-name:rotate;-webkit-animation-duration:1.5s;-webkit-animation-iteration-count:infinite;-webkit-animation-timing-function:linear;-moz-animation-name:rotate;-moz-animation-duration:1.5s;-moz-animation-iteration-count:infinite;-moz-animation-timing-function:linear;-o-animation-name:rotate;-o-animation-duration:1.5s;-o-animation-iteration-count:infinite;-o-animation-timing-function:linear;animation-name:rotate;animation-duration:1.5s;animation-iteration-count:infinite;animation-timing-function:linear}.orbit-container{overflow:hidden;width:100%;position:relative;background:none}.orbit-container .orbit-slides-container{list-style:none;margin:0;padding:0;position:relative}.orbit-container .orbit-slides-container img{display:block;max-width:100%}.orbit-container .orbit-slides-container>*{position:absolute;top:0;width:100%;margin-left:100%}.orbit-container .orbit-slides-container>*:first-child{margin-left:0%}.orbit-container .orbit-slides-container>* .orbit-caption{position:absolute;bottom:0;background-color:rgba(51,51,51,0.8);color:#fff;width:100%;padding:0.625rem 0.875rem;font-size:0.875rem}.orbit-container .orbit-slide-number{position:absolute;top:10px;left:10px;font-size:12px;color:#fff;background:rgba(0,0,0,0);z-index:10}.orbit-container .orbit-slide-number span{font-weight:700;padding:0.3125rem}.orbit-container .orbit-timer{position:absolute;top:12px;right:10px;height:6px;width:100px;z-index:10}.orbit-container .orbit-timer .orbit-progress{height:3px;background-color:rgba(255,255,255,0.3);display:block;width:0%;position:relative;right:20px;top:5px}.orbit-container .orbit-timer>span{display:none;position:absolute;top:0px;right:0;width:11px;height:14px;border:solid 4px #fff;border-top:none;border-bottom:none}.orbit-container .orbit-timer.paused>span{right:-4px;top:0px;width:11px;height:14px;border:inset 8px;border-right-style:solid;border-color:transparent transparent transparent #fff}.orbit-container .orbit-timer.paused>span.dark{border-color:transparent transparent transparent #333}.orbit-container:hover .orbit-timer>span{display:block}.orbit-container .orbit-prev,.orbit-container .orbit-next{position:absolute;top:45%;margin-top:-25px;width:36px;height:60px;line-height:50px;color:white;background-color:none;text-indent:-9999px !important;z-index:10}.orbit-container .orbit-prev:hover,.orbit-container .orbit-next:hover{background-color:rgba(0,0,0,0.3)}.orbit-container .orbit-prev>span,.orbit-container .orbit-next>span{position:absolute;top:50%;margin-top:-10px;display:block;width:0;height:0;border:inset 10px}.orbit-container .orbit-prev{left:0}.orbit-container .orbit-prev>span{border-right-style:solid;border-color:transparent;border-right-color:#fff}.orbit-container .orbit-prev:hover>span{border-right-color:#fff}.orbit-container .orbit-next{right:0}.orbit-container .orbit-next>span{border-color:transparent;border-left-style:solid;border-left-color:#fff;left:50%;margin-left:-4px}.orbit-container .orbit-next:hover>span{border-left-color:#fff}.orbit-bullets-container{text-align:center}.orbit-bullets{margin:0 auto 30px auto;overflow:hidden;position:relative;top:10px;float:none;text-align:center;display:block}.orbit-bullets li{display:inline-block;width:0.5625rem;height:0.5625rem;background:#ccc;float:none;margin-right:6px;-webkit-border-radius:1000px;border-radius:1000px}.orbit-bullets li.active{background:#999}.orbit-bullets li:last-child{margin-right:0}.touch .orbit-container .orbit-prev,.touch .orbit-container .orbit-next{display:none}.touch .orbit-bullets{display:none}@media only screen and (min-width: 40.063em){.touch .orbit-container .orbit-prev,.touch .orbit-container .orbit-next{display:inherit}.touch .orbit-bullets{display:block}}@media only screen and (max-width: 40em){.orbit-stack-on-small .orbit-slides-container{height:auto !important}.orbit-stack-on-small .orbit-slides-container>*{position:relative;margin-left:0% !important}.orbit-stack-on-small .orbit-timer,.orbit-stack-on-small .orbit-next,.orbit-stack-on-small .orbit-prev,.orbit-stack-on-small .orbit-bullets{display:none}}[data-magellan-expedition]{background:#fff;z-index:50;min-width:100%;padding:10px}[data-magellan-expedition] .sub-nav{margin-bottom:0}[data-magellan-expedition] .sub-nav dd{margin-bottom:0}[data-magellan-expedition] .sub-nav .active{line-height:1.8em}.tabs{*zoom:1;margin-bottom:0 !important}.tabs:before,.tabs:after{content:" ";display:table}.tabs:after{clear:both}.tabs dd{position:relative;margin-bottom:0 !important;top:1px;float:left}.tabs dd>a{display:block;background:#efefef;color:#222;padding-top:1rem;padding-right:2rem;padding-bottom:1.0625rem;padding-left:2rem;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-size:1rem}.tabs dd>a:hover{background:#e1e1e1}.tabs dd.active a{background:#fff}.tabs.radius dd:first-child a{-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.tabs.radius dd:last-child a{-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px;-webkit-border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.tabs.vertical dd{position:inherit;float:none;display:block;top:auto}.tabs-content{*zoom:1;margin-bottom:1.5rem}.tabs-content:before,.tabs-content:after{content:" ";display:table}.tabs-content:after{clear:both}.tabs-content>.content{display:none;float:left;padding:0.9375rem 0}.tabs-content>.content.active{display:block}.tabs-content>.content.contained{padding:0.9375rem}.tabs-content.vertical{display:block}.tabs-content.vertical>.content{padding:0 0.9375rem}@media only screen and (min-width: 40.063em){.tabs.vertical{width:20%;float:left;margin-bottom:1.25rem}.tabs-content.vertical{width:80%;float:left;margin-left:-1px}}ul.pagination{display:block;height:1.5rem;margin-left:-0.3125rem}ul.pagination li{height:1.5rem;color:#222;font-size:0.875rem;margin-left:0.3125rem}ul.pagination li a{display:block;padding:0.0625rem 0.625rem 0.0625rem;color:#999;-webkit-border-radius:3px;border-radius:3px}ul.pagination li:hover a,ul.pagination li a:focus{background:#e6e6e6}ul.pagination li.unavailable a{cursor:default;color:#999}ul.pagination li.unavailable:hover a,ul.pagination li.unavailable a:focus{background:transparent}ul.pagination li.current a{background:#008cba;color:#fff;font-weight:bold;cursor:default}ul.pagination li.current a:hover,ul.pagination li.current a:focus{background:#008cba}ul.pagination li{float:left;display:block}.pagination-centered{text-align:center}.pagination-centered ul.pagination li{float:none;display:inline-block}.side-nav{display:block;margin:0;padding:0.875rem 0;list-style-type:none;list-style-position:inside;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif}.side-nav li{margin:0 0 0.4375rem 0;font-size:0.875rem}.side-nav li a{display:block;color:#008cba}.side-nav li.active>a:first-child{color:#4d4d4d;font-weight:normal;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif}.side-nav li.divider{border-top:1px solid;height:0;padding:0;list-style:none;border-top-color:#fff}.accordion{*zoom:1;margin-bottom:0}.accordion:before,.accordion:after{content:" ";display:table}.accordion:after{clear:both}.accordion dd{display:block;margin-bottom:0 !important}.accordion dd.active a{background:#e8e8e8}.accordion dd>a{background:#efefef;color:#222;padding:1rem;display:block;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-size:1rem}.accordion dd>a:hover{background:#e3e3e3}.accordion .content{display:none;padding:0.9375rem}.accordion .content.active{display:block;background:#fff}div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}a{color:#008cba;text-decoration:none;line-height:inherit}a:hover,a:focus{color:#0078a0}a img{border:none}p{font-family:inherit;font-weight:normal;font-size:1rem;line-height:1.6;margin-bottom:1.25rem;text-rendering:optimizeLegibility}p.lead{font-size:1.21875rem;line-height:1.6}p aside{font-size:0.875rem;line-height:1.35;font-style:italic}h1,h2,h3,h4,h5,h6{font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-weight:300;font-style:normal;color:#222;text-rendering:optimizeLegibility;margin-top:0.2rem;margin-bottom:0.5rem;line-height:1.4}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-size:60%;color:#6f6f6f;line-height:0}h1{font-size:2.125rem}h2{font-size:1.6875rem}h3{font-size:1.375rem}h4{font-size:1.125rem}h5{font-size:1.125rem}h6{font-size:1rem}.subheader{line-height:1.4;color:#6f6f6f;font-weight:300;margin-top:0.2rem;margin-bottom:0.5rem}hr{border:solid #ddd;border-width:1px 0 0;clear:both;margin:1.25rem 0 1.1875rem;height:0}em,i{font-style:italic;line-height:inherit}strong,b{font-weight:bold;line-height:inherit}small{font-size:60%;line-height:inherit}code{font-family:Consolas,"Liberation Mono",Courier,monospace;font-weight:bold;color:#bd260d}ul,ol,dl{font-size:1rem;line-height:1.6;margin-bottom:1.25rem;list-style-position:outside;font-family:inherit}ul{margin-left:1.1rem}ul.no-bullet{margin-left:0}ul.no-bullet li ul,ul.no-bullet li ol{margin-left:1.25rem;margin-bottom:0;list-style:none}ul li ul,ul li ol{margin-left:1.25rem;margin-bottom:0;font-size:1rem}ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}ul.square{list-style-type:square;margin-left:1.1rem}ul.circle{list-style-type:circle;margin-left:1.1rem}ul.disc{list-style-type:disc;margin-left:1.1rem}ul.no-bullet{list-style:none}ol{margin-left:1.4rem}ol li ul,ol li ol{margin-left:1.25rem;margin-bottom:0}dl dt{margin-bottom:0.3rem;font-weight:bold}dl dd{margin-bottom:0.75rem}abbr,acronym{text-transform:uppercase;font-size:90%;color:#222;border-bottom:1px dotted #ddd;cursor:help}abbr{text-transform:none}blockquote{margin:0 0 1.25rem;padding:0.5625rem 1.25rem 0 1.1875rem;border-left:1px solid #ddd}blockquote cite{display:block;font-size:0.8125rem;color:#555}blockquote cite:before{content:"\2014 \0020"}blockquote cite a,blockquote cite a:visited{color:#555}blockquote,blockquote p{line-height:1.6;color:#6f6f6f}.vcard{display:inline-block;margin:0 0 1.25rem 0;border:1px solid #ddd;padding:0.625rem 0.75rem}.vcard li{margin:0;display:block}.vcard .fn{font-weight:bold;font-size:0.9375rem}.vevent .summary{font-weight:bold}.vevent abbr{cursor:default;text-decoration:none;font-weight:bold;border:none;padding:0 0.0625rem}@media only screen and (min-width: 40.063em){h1,h2,h3,h4,h5,h6{line-height:1.4}h1{font-size:2.75rem}h2{font-size:2.3125rem}h3{font-size:1.6875rem}h4{font-size:1.4375rem}}.print-only{display:none !important}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.hide-on-print{display:none !important}.print-only{display:block !important}.hide-for-print{display:none !important}.show-for-print{display:inherit !important}}.split.button{position:relative;padding-right:5.0625rem}.split.button span{display:block;height:100%;position:absolute;right:0;top:0;border-left:solid 1px}.split.button span:before{position:absolute;content:"";width:0;height:0;display:block;border-style:inset;top:50%;left:50%}.split.button span:active{background-color:rgba(0,0,0,0.1)}.split.button span{border-left-color:rgba(255,255,255,0.5)}.split.button span{width:3.09375rem}.split.button span:before{border-top-style:solid;border-width:0.375rem;top:48%;margin-left:-0.375rem}.split.button span:before{border-color:#fff transparent transparent transparent}.split.button.secondary span{border-left-color:rgba(255,255,255,0.5)}.split.button.secondary span:before{border-color:#fff transparent transparent transparent}.split.button.alert span{border-left-color:rgba(255,255,255,0.5)}.split.button.success span{border-left-color:rgba(255,255,255,0.5)}.split.button.tiny{padding-right:3.75rem}.split.button.tiny span{width:2.25rem}.split.button.tiny span:before{border-top-style:solid;border-width:0.375rem;top:48%;margin-left:-0.375rem}.split.button.small{padding-right:4.375rem}.split.button.small span{width:2.625rem}.split.button.small span:before{border-top-style:solid;border-width:0.4375rem;top:48%;margin-left:-0.375rem}.split.button.large{padding-right:5.5rem}.split.button.large span{width:3.4375rem}.split.button.large span:before{border-top-style:solid;border-width:0.3125rem;top:48%;margin-left:-0.375rem}.split.button.expand{padding-left:2rem}.split.button.secondary span:before{border-color:#333 transparent transparent transparent}.split.button.radius span{-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px;-webkit-border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.split.button.round span{-moz-border-radius-topright:1000px;-moz-border-radius-bottomright:1000px;-webkit-border-top-right-radius:1000px;-webkit-border-bottom-right-radius:1000px;border-top-right-radius:1000px;border-bottom-right-radius:1000px}.reveal-modal-bg{position:fixed;height:100%;width:100%;background:#000;background:rgba(0,0,0,0.45);z-index:98;display:none;top:0;left:0}.reveal-modal{visibility:hidden;display:none;position:absolute;left:50%;z-index:99;height:auto;margin-left:-40%;width:80%;background-color:#fff;padding:1.25rem;border:solid 1px #666;-webkit-box-shadow:0 0 10px rgba(0,0,0,0.4);box-shadow:0 0 10px rgba(0,0,0,0.4);top:6.25rem}.reveal-modal .column,.reveal-modal .columns{min-width:0}.reveal-modal>:first-child{margin-top:0}.reveal-modal>:last-child{margin-bottom:0}.reveal-modal .close-reveal-modal{font-size:1.375rem;line-height:1;position:absolute;top:0.5rem;right:0.6875rem;color:#aaa;font-weight:bold;cursor:pointer}@media only screen and (min-width: 40.063em){.reveal-modal{padding:1.875rem;top:6.25rem}.reveal-modal.tiny{margin-left:-15%;width:30%}.reveal-modal.small{margin-left:-20%;width:40%}.reveal-modal.medium{margin-left:-30%;width:60%}.reveal-modal.large{margin-left:-35%;width:70%}.reveal-modal.xlarge{margin-left:-47.5%;width:95%}}@media print{.reveal-modal{background:#fff !important}}.has-tip{border-bottom:dotted 1px #ccc;cursor:help;font-weight:bold;color:#333}.has-tip:hover,.has-tip:focus{border-bottom:dotted 1px #003f54;color:#008cba}.has-tip.tip-left,.has-tip.tip-right{float:none !important}.tooltip{display:none;position:absolute;z-index:999;font-weight:normal;font-size:0.875rem;line-height:1.3;padding:0.75rem;max-width:85%;left:50%;width:100%;color:#fff;background:#333;-webkit-border-radius:3px;border-radius:3px}.tooltip>.nub{display:block;left:5px;position:absolute;width:0;height:0;border:solid 5px;border-color:transparent transparent #333 transparent;top:-10px}.tooltip.opened{color:#008cba !important;border-bottom:dotted 1px #003f54 !important}.tap-to-close{display:block;font-size:0.625rem;color:#777;font-weight:normal}@media only screen and (min-width: 40.063em){.tooltip>.nub{border-color:transparent transparent #333 transparent;top:-10px}.tooltip.tip-top>.nub{border-color:#333 transparent transparent transparent;top:auto;bottom:-10px}.tooltip.tip-left,.tooltip.tip-right{float:none !important}.tooltip.tip-left>.nub{border-color:transparent transparent transparent #333;right:-10px;left:auto;top:50%;margin-top:-5px}.tooltip.tip-right>.nub{border-color:transparent #333 transparent transparent;right:auto;left:-10px;top:50%;margin-top:-5px}}[data-clearing]{*zoom:1;margin-bottom:0;margin-left:0;list-style:none}[data-clearing]:before,[data-clearing]:after{content:" ";display:table}[data-clearing]:after{clear:both}[data-clearing] li{float:left;margin-right:10px}.clearing-blackout{background:#333;position:fixed;width:100%;height:100%;top:0;left:0;z-index:998}.clearing-blackout .clearing-close{display:block}.clearing-container{position:relative;z-index:998;height:100%;overflow:hidden;margin:0}.visible-img{height:95%;position:relative}.visible-img img{position:absolute;left:50%;top:50%;margin-left:-50%;max-height:100%;max-width:100%}.clearing-caption{color:#ccc;font-size:0.875em;line-height:1.3;margin-bottom:0;text-align:center;bottom:0;background:#333;width:100%;padding:10px 30px 20px;position:absolute;left:0}.clearing-close{z-index:999;padding-left:20px;padding-top:10px;font-size:30px;line-height:1;color:#ccc;display:none}.clearing-close:hover,.clearing-close:focus{color:#ccc}.clearing-assembled .clearing-container{height:100%}.clearing-assembled .clearing-container .carousel>ul{display:none}.clearing-feature li{display:none}.clearing-feature li.clearing-featured-img{display:block}@media only screen and (min-width: 40.063em){.clearing-main-prev,.clearing-main-next{position:absolute;height:100%;width:40px;top:0}.clearing-main-prev>span,.clearing-main-next>span{position:absolute;top:50%;display:block;width:0;height:0;border:solid 12px}.clearing-main-prev>span:hover,.clearing-main-next>span:hover{opacity:0.8}.clearing-main-prev{left:0}.clearing-main-prev>span{left:5px;border-color:transparent;border-right-color:#ccc}.clearing-main-next{right:0}.clearing-main-next>span{border-color:transparent;border-left-color:#ccc}.clearing-main-prev.disabled,.clearing-main-next.disabled{opacity:0.3}.clearing-assembled .clearing-container .carousel{background:rgba(51,51,51,0.8);height:120px;margin-top:10px;text-align:center}.clearing-assembled .clearing-container .carousel>ul{display:inline-block;z-index:999;height:100%;position:relative;float:none}.clearing-assembled .clearing-container .carousel>ul li{display:block;width:120px;min-height:inherit;float:left;overflow:hidden;margin-right:0;padding:0;position:relative;cursor:pointer;opacity:0.4}.clearing-assembled .clearing-container .carousel>ul li.fix-height img{height:100%;max-width:none}.clearing-assembled .clearing-container .carousel>ul li a.th{border:none;-webkit-box-shadow:none;box-shadow:none;display:block}.clearing-assembled .clearing-container .carousel>ul li img{cursor:pointer !important;width:100% !important}.clearing-assembled .clearing-container .carousel>ul li.visible{opacity:1}.clearing-assembled .clearing-container .carousel>ul li:hover{opacity:0.8}.clearing-assembled .clearing-container .visible-img{background:#333;overflow:hidden;height:85%}.clearing-close{position:absolute;top:10px;right:20px;padding-left:0;padding-top:0}}.progress{background-color:#f6f6f6;height:1.5625rem;border:1px solid #fff;padding:0.125rem;margin-bottom:0.625rem}.progress .meter{background:#008cba;height:100%;display:block}.progress.secondary .meter{background:#e7e7e7;height:100%;display:block}.progress.success .meter{background:#43ac6a;height:100%;display:block}.progress.alert .meter{background:#f04124;height:100%;display:block}.progress.radius{-webkit-border-radius:3px;border-radius:3px}.progress.radius .meter{-webkit-border-radius:2px;border-radius:2px}.progress.round{-webkit-border-radius:1000px;border-radius:1000px}.progress.round .meter{-webkit-border-radius:999px;border-radius:999px}.sub-nav{display:block;width:auto;overflow:hidden;margin:-0.25rem 0 1.125rem;padding-top:0.25rem;margin-right:0;margin-left:-0.75rem}.sub-nav dt{text-transform:uppercase}.sub-nav dt,.sub-nav dd,.sub-nav li{float:left;display:inline;margin-left:1rem;margin-bottom:0.625rem;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-weight:normal;font-size:0.875rem;color:#999}.sub-nav dt a,.sub-nav dd a,.sub-nav li a{text-decoration:none;color:#999}.sub-nav dt a:hover,.sub-nav dd a:hover,.sub-nav li a:hover{color:#0085b1}.sub-nav dt.active a,.sub-nav dd.active a,.sub-nav li.active a{-webkit-border-radius:3px;border-radius:3px;font-weight:normal;background:#008cba;padding:0.1875rem 1rem;cursor:default;color:#fff}.sub-nav dt.active a:hover,.sub-nav dd.active a:hover,.sub-nav li.active a:hover{background:#0085b1}.joyride-list{display:none}.joyride-tip-guide{display:none;position:absolute;background:#333;color:#fff;z-index:101;top:0;left:2.5%;font-family:inherit;font-weight:normal;width:95%}.lt-ie9 .joyride-tip-guide{max-width:800px;left:50%;margin-left:-400px}.joyride-content-wrapper{width:100%;padding:1.125rem 1.25rem 1.5rem}.joyride-content-wrapper .button{margin-bottom:0 !important}.joyride-tip-guide .joyride-nub{display:block;position:absolute;left:22px;width:0;height:0;border:10px solid #333}.joyride-tip-guide .joyride-nub.top{border-top-style:solid;border-color:#333;border-top-color:transparent !important;border-left-color:transparent !important;border-right-color:transparent !important;top:-20px}.joyride-tip-guide .joyride-nub.bottom{border-bottom-style:solid;border-color:#333 !important;border-bottom-color:transparent !important;border-left-color:transparent !important;border-right-color:transparent !important;bottom:-20px}.joyride-tip-guide .joyride-nub.right{right:-20px}.joyride-tip-guide .joyride-nub.left{left:-20px}.joyride-tip-guide h1,.joyride-tip-guide h2,.joyride-tip-guide h3,.joyride-tip-guide h4,.joyride-tip-guide h5,.joyride-tip-guide h6{line-height:1.25;margin:0;font-weight:bold;color:#fff}.joyride-tip-guide p{margin:0 0 1.125rem 0;font-size:0.875rem;line-height:1.3}.joyride-timer-indicator-wrap{width:50px;height:3px;border:solid 1px #555;position:absolute;right:1.0625rem;bottom:1rem}.joyride-timer-indicator{display:block;width:0;height:inherit;background:#666}.joyride-close-tip{position:absolute;right:12px;top:10px;color:#777 !important;text-decoration:none;font-size:24px;font-weight:normal;line-height:0.5 !important}.joyride-close-tip:hover,.joyride-close-tip:focus{color:#eee !important}.joyride-modal-bg{position:fixed;height:100%;width:100%;background:transparent;background:rgba(0,0,0,0.5);z-index:100;display:none;top:0;left:0;cursor:pointer}.joyride-expose-wrapper{background-color:#ffffff;position:absolute;border-radius:3px;z-index:102;-moz-box-shadow:0 0 30px #fff;-webkit-box-shadow:0 0 15px #fff;box-shadow:0 0 15px #fff}.joyride-expose-cover{background:transparent;border-radius:3px;position:absolute;z-index:9999;top:0;left:0}@media only screen and (min-width: 40.063em){.joyride-tip-guide{width:300px;left:inherit}.joyride-tip-guide .joyride-nub.bottom{border-color:#333 !important;border-bottom-color:transparent !important;border-left-color:transparent !important;border-right-color:transparent !important;bottom:-20px}.joyride-tip-guide .joyride-nub.right{border-color:#333 !important;border-top-color:transparent !important;border-right-color:transparent !important;border-bottom-color:transparent !important;top:22px;left:auto;right:-20px}.joyride-tip-guide .joyride-nub.left{border-color:#333 !important;border-top-color:transparent !important;border-left-color:transparent !important;border-bottom-color:transparent !important;top:22px;left:-20px;right:auto}}.label{font-weight:normal;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;text-align:center;text-decoration:none;line-height:1;white-space:nowrap;display:inline-block;position:relative;margin-bottom:inherit;padding:0.25rem 0.5rem 0.375rem;font-size:0.6875rem;background-color:#008cba;color:#fff}.label.radius{-webkit-border-radius:3px;border-radius:3px}.label.round{-webkit-border-radius:1000px;border-radius:1000px}.label.alert{background-color:#f04124;color:#fff}.label.success{background-color:#43ac6a;color:#fff}.label.secondary{background-color:#e7e7e7;color:#333}.off-canvas-wrap{-webkit-backface-visibility:hidden;position:relative;width:100%;overflow:hidden}.inner-wrap{-webkit-backface-visibility:hidden;position:relative;width:100%;*zoom:1;-webkit-transition:-webkit-transform 500ms ease;-moz-transition:-moz-transform 500ms ease;-ms-transition:-ms-transform 500ms ease;-o-transition:-o-transform 500ms ease;transition:transform 500ms ease}.inner-wrap:before,.inner-wrap:after{content:" ";display:table}.inner-wrap:after{clear:both}nav.tab-bar{-webkit-backface-visibility:hidden;background:#333;color:#fff;height:2.8125rem;line-height:2.8125rem;position:relative}nav.tab-bar h1,nav.tab-bar h2,nav.tab-bar h3,nav.tab-bar h4,nav.tab-bar h5,nav.tab-bar h6{color:#fff;font-weight:bold;line-height:2.8125rem;margin:0}nav.tab-bar h1,nav.tab-bar h2,nav.tab-bar h3,nav.tab-bar h4{font-size:1.125rem}section.left-small{width:2.8125rem;height:2.8125rem;position:absolute;top:0;border-right:solid 1px #1a1a1a;box-shadow:1px 0 0 #4e4e4e;left:0}section.right-small{width:2.8125rem;height:2.8125rem;position:absolute;top:0;border-left:solid 1px #4e4e4e;box-shadow:-1px 0 0 #1a1a1a;right:0}section.tab-bar-section{padding:0 0.625rem;position:absolute;text-align:center;height:2.8125rem;top:0}@media only screen and (min-width: 40.063em){section.tab-bar-section{text-align:left}}section.tab-bar-section.left{left:0;right:2.8125rem}section.tab-bar-section.right{left:2.8125rem;right:0}section.tab-bar-section.middle{left:2.8125rem;right:2.8125rem}a.menu-icon{text-indent:2.1875rem;width:2.8125rem;height:2.8125rem;display:block;line-height:2.0625rem;padding:0;color:#fff;position:relative}a.menu-icon span{position:absolute;display:block;width:1rem;height:0;left:0.8125rem;top:0.3125rem;-webkit-box-shadow:1px 10px 1px 1px #fff,1px 16px 1px 1px #fff,1px 22px 1px 1px #fff;box-shadow:0 10px 0 1px #fff,0 16px 0 1px #fff,0 22px 0 1px #fff}a.menu-icon:hover span{-webkit-box-shadow:1px 10px 1px 1px #b3b3b3,1px 16px 1px 1px #b3b3b3,1px 22px 1px 1px #b3b3b3;box-shadow:0 10px 0 1px #b3b3b3,0 16px 0 1px #b3b3b3,0 22px 0 1px #b3b3b3}.left-off-canvas-menu{-webkit-backface-visibility:hidden;width:250px;top:0;bottom:0;height:100%;position:absolute;overflow-y:auto;background:#333;z-index:1001;box-sizing:content-box;-webkit-transform:translate3d(-100%, 0, 0);-moz-transform:translate3d(-100%, 0, 0);-ms-transform:translate3d(-100%, 0, 0);-o-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0)}.left-off-canvas-menu *{-webkit-backface-visibility:hidden}.right-off-canvas-menu{-webkit-backface-visibility:hidden;width:250px;top:0;bottom:0;height:100%;position:absolute;overflow-y:auto;background:#333;z-index:1001;box-sizing:content-box;-webkit-transform:translate3d(100%, 0, 0);-moz-transform:translate3d(100%, 0, 0);-ms-transform:translate3d(100%, 0, 0);-o-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);right:0}ul.off-canvas-list{list-style-type:none;padding:0;margin:0}ul.off-canvas-list li label{padding:0.3rem 0.9375rem;color:#999;text-transform:uppercase;font-weight:bold;background:#444;border-top:1px solid #5e5e5e;border-bottom:none;margin:0}ul.off-canvas-list li a{display:block;padding:0.66667rem;color:rgba(255,255,255,0.7);border-bottom:1px solid #262626}.move-right>.inner-wrap{-webkit-transform:translate3d(250px, 0, 0);-moz-transform:translate3d(250px, 0, 0);-ms-transform:translate3d(250px, 0, 0);-o-transform:translate3d(250px, 0, 0);transform:translate3d(250px, 0, 0)}.move-right a.exit-off-canvas{-webkit-backface-visibility:hidden;transition:background 300ms ease;cursor:pointer;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);display:block;position:absolute;background:rgba(255,255,255,0.2);top:0;bottom:0;left:0;right:0;z-index:1002;-webkit-tap-highlight-color:rgba(0,0,0,0)}@media only screen and (min-width: 40.063em){.move-right a.exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.move-left>.inner-wrap{-webkit-transform:translate3d(-250px, 0, 0);-moz-transform:translate3d(-250px, 0, 0);-ms-transform:translate3d(-250px, 0, 0);-o-transform:translate3d(-250px, 0, 0);transform:translate3d(-250px, 0, 0)}.move-left a.exit-off-canvas{-webkit-backface-visibility:hidden;transition:background 300ms ease;cursor:pointer;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);display:block;position:absolute;background:rgba(255,255,255,0.2);top:0;bottom:0;left:0;right:0;z-index:1002;-webkit-tap-highlight-color:rgba(0,0,0,0)}@media only screen and (min-width: 40.063em){.move-left a.exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.csstransforms.no-csstransforms3d .left-off-canvas-menu{-webkit-transform:translate(-100%, 0);-moz-transform:translate(-100%, 0);-ms-transform:translate(-100%, 0);-o-transform:translate(-100%, 0);transform:translate(-100%, 0)}.csstransforms.no-csstransforms3d .right-off-canvas-menu{-webkit-transform:translate(100%, 0);-moz-transform:translate(100%, 0);-ms-transform:translate(100%, 0);-o-transform:translate(100%, 0);transform:translate(100%, 0)}.csstransforms.no-csstransforms3d .move-left>.inner-wrap{-webkit-transform:translate(-250px, 0);-moz-transform:translate(-250px, 0);-ms-transform:translate(-250px, 0);-o-transform:translate(-250px, 0);transform:translate(-250px, 0)}.csstransforms.no-csstransforms3d .move-right>.inner-wrap{-webkit-transform:translate(250px, 0);-moz-transform:translate(250px, 0);-ms-transform:translate(250px, 0);-o-transform:translate(250px, 0);transform:translate(250px, 0)}.no-csstransforms .left-off-canvas-menu{left:-250px}.no-csstransforms .right-off-canvas-menu{right:-250px}.no-csstransforms .move-left>.inner-wrap{right:250px}.no-csstransforms .move-right>.inner-wrap{left:250px}@media only screen and (max-width: 40em){.f-dropdown{max-width:100%;left:0}}.f-dropdown{position:absolute;left:-9999px;list-style:none;margin-left:0;width:100%;max-height:none;height:auto;background:#fff;border:solid 1px #ccc;font-size:16px;z-index:99;margin-top:2px;max-width:200px}.f-dropdown>*:first-child{margin-top:0}.f-dropdown>*:last-child{margin-bottom:0}.f-dropdown:before{content:"";display:block;width:0;height:0;border:inset 6px;border-color:transparent transparent #fff transparent;border-bottom-style:solid;position:absolute;top:-12px;left:10px;z-index:99}.f-dropdown:after{content:"";display:block;width:0;height:0;border:inset 7px;border-color:transparent transparent #ccc transparent;border-bottom-style:solid;position:absolute;top:-14px;left:9px;z-index:98}.f-dropdown.right:before{left:auto;right:10px}.f-dropdown.right:after{left:auto;right:9px}.f-dropdown li{font-size:0.875rem;cursor:pointer;line-height:1.125rem;margin:0}.f-dropdown li:hover,.f-dropdown li:focus{background:#eee}.f-dropdown li a{display:block;padding:0.5rem;color:#555}.f-dropdown.content{position:absolute;left:-9999px;list-style:none;margin-left:0;padding:1.25rem;width:100%;height:auto;max-height:none;background:#fff;border:solid 1px #ccc;font-size:16px;z-index:99;max-width:200px}.f-dropdown.content>*:first-child{margin-top:0}.f-dropdown.content>*:last-child{margin-bottom:0}.f-dropdown.tiny{max-width:200px}.f-dropdown.small{max-width:300px}.f-dropdown.medium{max-width:500px}.f-dropdown.large{max-width:800px}table{background:#fff;margin-bottom:1.25rem;border:solid 1px #ddd}table thead,table tfoot{background:#f5f5f5}table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:0.5rem 0.625rem 0.625rem;font-size:0.875rem;font-weight:bold;color:#222;text-align:left}table tr th,table tr td{padding:0.5625rem 0.625rem;font-size:0.875rem;color:#222}table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f9f9f9}table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.125rem}form{margin:0 0 1rem}form .row .row{margin:0 -0.5rem}form .row .row .column,form .row .row .columns{padding:0 0.5rem}form .row .row.collapse{margin:0}form .row .row.collapse .column,form .row .row.collapse .columns{padding:0}form .row .row.collapse input{-moz-border-radius-bottomright:0;-moz-border-radius-topright:0;-webkit-border-bottom-right-radius:0;-webkit-border-top-right-radius:0}form .row input.column,form .row input.columns,form .row textarea.column,form .row textarea.columns{padding-left:0.5rem}label{font-size:0.875rem;color:#4d4d4d;cursor:pointer;display:block;font-weight:normal;margin-bottom:0.5rem}label.right{float:none;text-align:right}label.inline{margin:0 0 1rem 0;padding:0.625rem 0}label small{text-transform:capitalize;color:#676767}select{-webkit-appearance:none !important;background:#fafafa url("data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iM3B4IiB2aWV3Qm94PSIwIDAgNiAzIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA2IDMiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwb2x5Z29uIHBvaW50cz0iNS45OTIsMCAyLjk5MiwzIC0wLjAwOCwwICIvPjwvc3ZnPg==") no-repeat;background-position-x:97%;background-position-y:center;border:1px solid #ccc;padding:0.5rem;font-size:0.875rem;-webkit-border-radius:0;border-radius:0}select.radius{-webkit-border-radius:3px;border-radius:3px}select:hover{background:#f3f3f3 url("data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iM3B4IiB2aWV3Qm94PSIwIDAgNiAzIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA2IDMiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwb2x5Z29uIHBvaW50cz0iNS45OTIsMCAyLjk5MiwzIC0wLjAwOCwwICIvPjwvc3ZnPg==") no-repeat;background-position-x:97%;background-position-y:center;border-color:#999}select::-ms-expand{display:none}@-moz-document url-prefix(){select{background:#fafafa}select:hover{background:#f3f3f3}}.prefix,.postfix{display:block;position:relative;z-index:2;text-align:center;width:100%;padding-top:0;padding-bottom:0;border-style:solid;border-width:1px;overflow:hidden;font-size:0.875rem;height:2.3125rem;line-height:2.3125rem}.postfix.button{padding-left:0;padding-right:0;padding-top:0;padding-bottom:0;text-align:center;line-height:2.125rem;border:none}.prefix.button{padding-left:0;padding-right:0;padding-top:0;padding-bottom:0;text-align:center;line-height:2.125rem;border:none}.prefix.button.radius{-webkit-border-radius:0;border-radius:0;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.postfix.button.radius{-webkit-border-radius:0;border-radius:0;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px;-webkit-border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.prefix.button.round{-webkit-border-radius:0;border-radius:0;-moz-border-radius-bottomleft:1000px;-moz-border-radius-topleft:1000px;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.postfix.button.round{-webkit-border-radius:0;border-radius:0;-moz-border-radius-topright:1000px;-moz-border-radius-bottomright:1000px;-webkit-border-top-right-radius:1000px;-webkit-border-bottom-right-radius:1000px;border-top-right-radius:1000px;border-bottom-right-radius:1000px}span.prefix,label.prefix{background:#f2f2f2;border-color:#d8d8d8;border-right:none;color:#333}span.prefix.radius,label.prefix.radius{-webkit-border-radius:0;border-radius:0;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}span.postfix,label.postfix{background:#f2f2f2;border-color:#cbcbcb;border-left:none;color:#333}span.postfix.radius,label.postfix.radius{-webkit-border-radius:0;border-radius:0;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px;-webkit-border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.input-group.radius>*:first-child,.input-group.radius>*:first-child *{-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.input-group.radius>*:last-child,.input-group.radius>*:last-child *{-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px;-webkit-border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.input-group.round>*:first-child,.input-group.round>*:first-child *{-moz-border-radius-bottomleft:1000px;-moz-border-radius-topleft:1000px;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.input-group.round>*:last-child,.input-group.round>*:last-child *{-moz-border-radius-topright:1000px;-moz-border-radius-bottomright:1000px;-webkit-border-top-right-radius:1000px;-webkit-border-bottom-right-radius:1000px;border-top-right-radius:1000px;border-bottom-right-radius:1000px}input[type="text"],input[type="password"],input[type="date"],input[type="datetime"],input[type="datetime-local"],input[type="month"],input[type="week"],input[type="email"],input[type="number"],input[type="search"],input[type="tel"],input[type="time"],input[type="url"],textarea{-webkit-appearance:none;-webkit-border-radius:0;border-radius:0;background-color:#fff;font-family:inherit;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);color:rgba(0,0,0,0.75);display:block;font-size:0.875rem;margin:0 0 1rem 0;padding:0.5rem;height:2.3125rem;width:100%;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-transition:-webkit-box-shadow 0.45s,border-color 0.45s ease-in-out;-moz-transition:-moz-box-shadow 0.45s,border-color 0.45s ease-in-out;transition:box-shadow 0.45s,border-color 0.45s ease-in-out}input[type="text"]:focus,input[type="password"]:focus,input[type="date"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="month"]:focus,input[type="week"]:focus,input[type="email"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="time"]:focus,input[type="url"]:focus,textarea:focus{-webkit-box-shadow:0 0 5px #999;-moz-box-shadow:0 0 5px #999;box-shadow:0 0 5px #999;border-color:#999}input[type="text"]:focus,input[type="password"]:focus,input[type="date"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="month"]:focus,input[type="week"]:focus,input[type="email"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="time"]:focus,input[type="url"]:focus,textarea:focus{background:#fafafa;border-color:#999;outline:none}input[type="text"][disabled],input[type="password"][disabled],input[type="date"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="month"][disabled],input[type="week"][disabled],input[type="email"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="time"][disabled],input[type="url"][disabled],textarea[disabled]{background-color:#ddd}select{height:2.3125rem}input[type="file"],input[type="checkbox"],input[type="radio"],select{margin:0 0 1rem 0}input[type="checkbox"]+label,input[type="radio"]+label{display:inline-block;margin-left:0.5rem;margin-right:1rem;margin-bottom:0;vertical-align:baseline}input[type="file"]{width:100%}fieldset{border:solid 1px #ddd;padding:1.25rem;margin:1.125rem 0}fieldset legend{font-weight:bold;background:#fff;padding:0 0.1875rem;margin:0;margin-left:-0.1875rem}[data-abide] .error small.error,[data-abide] span.error,[data-abide] small.error{display:block;padding:0.375rem 0.5625rem 0.5625rem;margin-top:-1px;margin-bottom:1rem;font-size:0.75rem;font-weight:normal;font-style:italic;background:#f04124;color:#fff}[data-abide] span.error,[data-abide] small.error{display:none}span.error,small.error{display:block;padding:0.375rem 0.5625rem 0.5625rem;margin-top:-1px;margin-bottom:1rem;font-size:0.75rem;font-weight:normal;font-style:italic;background:#f04124;color:#fff}.error input,.error textarea,.error select{margin-bottom:0}.error label,.error label.error{color:#f04124}.error>small,.error small.error{display:block;padding:0.375rem 0.5625rem 0.5625rem;margin-top:-1px;margin-bottom:1rem;font-size:0.75rem;font-weight:normal;font-style:italic;background:#f04124;color:#fff}.error>label>small{color:#676767;background:transparent;padding:0;text-transform:capitalize;font-style:normal;font-size:60%;margin:0;display:inline}.error span.error-message{display:block}input.error,textarea.error{margin-bottom:0}label.error{color:#f04124}[class*="block-grid-"]{display:block;padding:0;margin:0 0 0 -0.625rem;*zoom:1}[class*="block-grid-"]:before,[class*="block-grid-"]:after{content:" ";display:table}[class*="block-grid-"]:after{clear:both}[class*="block-grid-"]>li{display:inline;height:auto;float:left;padding:0 0.625rem 1.25rem}@media only screen{.small-block-grid-1>li{width:100%;padding:0 0.625rem 1.25rem;list-style:none}.small-block-grid-1>li:nth-of-type(n){clear:none}.small-block-grid-1>li:nth-of-type(1n+1){clear:both}.small-block-grid-2>li{width:50%;padding:0 0.625rem 1.25rem;list-style:none}.small-block-grid-2>li:nth-of-type(n){clear:none}.small-block-grid-2>li:nth-of-type(2n+1){clear:both}.small-block-grid-3>li{width:33.33333%;padding:0 0.625rem 1.25rem;list-style:none}.small-block-grid-3>li:nth-of-type(n){clear:none}.small-block-grid-3>li:nth-of-type(3n+1){clear:both}.small-block-grid-4>li{width:25%;padding:0 0.625rem 1.25rem;list-style:none}.small-block-grid-4>li:nth-of-type(n){clear:none}.small-block-grid-4>li:nth-of-type(4n+1){clear:both}.small-block-grid-5>li{width:20%;padding:0 0.625rem 1.25rem;list-style:none}.small-block-grid-5>li:nth-of-type(n){clear:none}.small-block-grid-5>li:nth-of-type(5n+1){clear:both}.small-block-grid-6>li{width:16.66667%;padding:0 0.625rem 1.25rem;list-style:none}.small-block-grid-6>li:nth-of-type(n){clear:none}.small-block-grid-6>li:nth-of-type(6n+1){clear:both}.small-block-grid-7>li{width:14.28571%;padding:0 0.625rem 1.25rem;list-style:none}.small-block-grid-7>li:nth-of-type(n){clear:none}.small-block-grid-7>li:nth-of-type(7n+1){clear:both}.small-block-grid-8>li{width:12.5%;padding:0 0.625rem 1.25rem;list-style:none}.small-block-grid-8>li:nth-of-type(n){clear:none}.small-block-grid-8>li:nth-of-type(8n+1){clear:both}.small-block-grid-9>li{width:11.11111%;padding:0 0.625rem 1.25rem;list-style:none}.small-block-grid-9>li:nth-of-type(n){clear:none}.small-block-grid-9>li:nth-of-type(9n+1){clear:both}.small-block-grid-10>li{width:10%;padding:0 0.625rem 1.25rem;list-style:none}.small-block-grid-10>li:nth-of-type(n){clear:none}.small-block-grid-10>li:nth-of-type(10n+1){clear:both}.small-block-grid-11>li{width:9.09091%;padding:0 0.625rem 1.25rem;list-style:none}.small-block-grid-11>li:nth-of-type(n){clear:none}.small-block-grid-11>li:nth-of-type(11n+1){clear:both}.small-block-grid-12>li{width:8.33333%;padding:0 0.625rem 1.25rem;list-style:none}.small-block-grid-12>li:nth-of-type(n){clear:none}.small-block-grid-12>li:nth-of-type(12n+1){clear:both}}@media only screen and (min-width: 40.063em){.medium-block-grid-1>li{width:100%;padding:0 0.625rem 1.25rem;list-style:none}.medium-block-grid-1>li:nth-of-type(n){clear:none}.medium-block-grid-1>li:nth-of-type(1n+1){clear:both}.medium-block-grid-2>li{width:50%;padding:0 0.625rem 1.25rem;list-style:none}.medium-block-grid-2>li:nth-of-type(n){clear:none}.medium-block-grid-2>li:nth-of-type(2n+1){clear:both}.medium-block-grid-3>li{width:33.33333%;padding:0 0.625rem 1.25rem;list-style:none}.medium-block-grid-3>li:nth-of-type(n){clear:none}.medium-block-grid-3>li:nth-of-type(3n+1){clear:both}.medium-block-grid-4>li{width:25%;padding:0 0.625rem 1.25rem;list-style:none}.medium-block-grid-4>li:nth-of-type(n){clear:none}.medium-block-grid-4>li:nth-of-type(4n+1){clear:both}.medium-block-grid-5>li{width:20%;padding:0 0.625rem 1.25rem;list-style:none}.medium-block-grid-5>li:nth-of-type(n){clear:none}.medium-block-grid-5>li:nth-of-type(5n+1){clear:both}.medium-block-grid-6>li{width:16.66667%;padding:0 0.625rem 1.25rem;list-style:none}.medium-block-grid-6>li:nth-of-type(n){clear:none}.medium-block-grid-6>li:nth-of-type(6n+1){clear:both}.medium-block-grid-7>li{width:14.28571%;padding:0 0.625rem 1.25rem;list-style:none}.medium-block-grid-7>li:nth-of-type(n){clear:none}.medium-block-grid-7>li:nth-of-type(7n+1){clear:both}.medium-block-grid-8>li{width:12.5%;padding:0 0.625rem 1.25rem;list-style:none}.medium-block-grid-8>li:nth-of-type(n){clear:none}.medium-block-grid-8>li:nth-of-type(8n+1){clear:both}.medium-block-grid-9>li{width:11.11111%;padding:0 0.625rem 1.25rem;list-style:none}.medium-block-grid-9>li:nth-of-type(n){clear:none}.medium-block-grid-9>li:nth-of-type(9n+1){clear:both}.medium-block-grid-10>li{width:10%;padding:0 0.625rem 1.25rem;list-style:none}.medium-block-grid-10>li:nth-of-type(n){clear:none}.medium-block-grid-10>li:nth-of-type(10n+1){clear:both}.medium-block-grid-11>li{width:9.09091%;padding:0 0.625rem 1.25rem;list-style:none}.medium-block-grid-11>li:nth-of-type(n){clear:none}.medium-block-grid-11>li:nth-of-type(11n+1){clear:both}.medium-block-grid-12>li{width:8.33333%;padding:0 0.625rem 1.25rem;list-style:none}.medium-block-grid-12>li:nth-of-type(n){clear:none}.medium-block-grid-12>li:nth-of-type(12n+1){clear:both}}@media only screen and (min-width: 64.063em){.large-block-grid-1>li{width:100%;padding:0 0.625rem 1.25rem;list-style:none}.large-block-grid-1>li:nth-of-type(n){clear:none}.large-block-grid-1>li:nth-of-type(1n+1){clear:both}.large-block-grid-2>li{width:50%;padding:0 0.625rem 1.25rem;list-style:none}.large-block-grid-2>li:nth-of-type(n){clear:none}.large-block-grid-2>li:nth-of-type(2n+1){clear:both}.large-block-grid-3>li{width:33.33333%;padding:0 0.625rem 1.25rem;list-style:none}.large-block-grid-3>li:nth-of-type(n){clear:none}.large-block-grid-3>li:nth-of-type(3n+1){clear:both}.large-block-grid-4>li{width:25%;padding:0 0.625rem 1.25rem;list-style:none}.large-block-grid-4>li:nth-of-type(n){clear:none}.large-block-grid-4>li:nth-of-type(4n+1){clear:both}.large-block-grid-5>li{width:20%;padding:0 0.625rem 1.25rem;list-style:none}.large-block-grid-5>li:nth-of-type(n){clear:none}.large-block-grid-5>li:nth-of-type(5n+1){clear:both}.large-block-grid-6>li{width:16.66667%;padding:0 0.625rem 1.25rem;list-style:none}.large-block-grid-6>li:nth-of-type(n){clear:none}.large-block-grid-6>li:nth-of-type(6n+1){clear:both}.large-block-grid-7>li{width:14.28571%;padding:0 0.625rem 1.25rem;list-style:none}.large-block-grid-7>li:nth-of-type(n){clear:none}.large-block-grid-7>li:nth-of-type(7n+1){clear:both}.large-block-grid-8>li{width:12.5%;padding:0 0.625rem 1.25rem;list-style:none}.large-block-grid-8>li:nth-of-type(n){clear:none}.large-block-grid-8>li:nth-of-type(8n+1){clear:both}.large-block-grid-9>li{width:11.11111%;padding:0 0.625rem 1.25rem;list-style:none}.large-block-grid-9>li:nth-of-type(n){clear:none}.large-block-grid-9>li:nth-of-type(9n+1){clear:both}.large-block-grid-10>li{width:10%;padding:0 0.625rem 1.25rem;list-style:none}.large-block-grid-10>li:nth-of-type(n){clear:none}.large-block-grid-10>li:nth-of-type(10n+1){clear:both}.large-block-grid-11>li{width:9.09091%;padding:0 0.625rem 1.25rem;list-style:none}.large-block-grid-11>li:nth-of-type(n){clear:none}.large-block-grid-11>li:nth-of-type(11n+1){clear:both}.large-block-grid-12>li{width:8.33333%;padding:0 0.625rem 1.25rem;list-style:none}.large-block-grid-12>li:nth-of-type(n){clear:none}.large-block-grid-12>li:nth-of-type(12n+1){clear:both}}.flex-video{position:relative;padding-top:1.5625rem;padding-bottom:67.5%;height:0;margin-bottom:1rem;overflow:hidden}.flex-video.widescreen{padding-bottom:57.25%}.flex-video.vimeo{padding-top:0}.flex-video iframe,.flex-video object,.flex-video embed,.flex-video video{position:absolute;top:0;left:0;width:100%;height:100%}.keystroke,kbd{background-color:#ededed;border-color:#ddd;color:#222;border-style:solid;border-width:1px;margin:0;font-family:"Consolas","Menlo","Courier",monospace;font-size:0.875rem;padding:0.125rem 0.25rem 0;-webkit-border-radius:3px;border-radius:3px}.show-for-small,.show-for-small-only,.show-for-medium-down,.show-for-large-down,.hide-for-medium,.hide-for-medium-up,.hide-for-medium-only,.hide-for-large,.hide-for-large-up,.hide-for-large-only,.hide-for-xlarge,.hide-for-xlarge-up,.hide-for-xlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge-only{display:inherit !important}.hide-for-small,.hide-for-small-only,.hide-for-medium-down,.show-for-medium,.show-for-medium-up,.show-for-medium-only,.hide-for-large-down,.show-for-large,.show-for-large-up,.show-for-large-only,.show-for-xlarge,.show-for-xlarge-up,.show-for-xlarge-only,.show-for-xxlarge-up,.show-for-xxlarge-only{display:none !important}table.show-for-small,table.show-for-small-only,table.show-for-medium-down,table.show-for-large-down,table.hide-for-medium,table.hide-for-medium-up,table.hide-for-medium-only,table.hide-for-large,table.hide-for-large-up,table.hide-for-large-only,table.hide-for-xlarge,table.hide-for-xlarge-up,table.hide-for-xlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge-only{display:table}thead.show-for-small,thead.show-for-small-only,thead.show-for-medium-down,thead.show-for-large-down,thead.hide-for-medium,thead.hide-for-medium-up,thead.hide-for-medium-only,thead.hide-for-large,thead.hide-for-large-up,thead.hide-for-large-only,thead.hide-for-xlarge,thead.hide-for-xlarge-up,thead.hide-for-xlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge-only{display:table-header-group !important}tbody.show-for-small,tbody.show-for-small-only,tbody.show-for-medium-down,tbody.show-for-large-down,tbody.hide-for-medium,tbody.hide-for-medium-up,tbody.hide-for-medium-only,tbody.hide-for-large,tbody.hide-for-large-up,tbody.hide-for-large-only,tbody.hide-for-xlarge,tbody.hide-for-xlarge-up,tbody.hide-for-xlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge-only{display:table-row-group !important}tr.show-for-small,tr.show-for-small-only,tr.show-for-medium-down,tr.show-for-large-down,tr.hide-for-medium,tr.hide-for-medium-up,tr.hide-for-medium-only,tr.hide-for-large,tr.hide-for-large-up,tr.hide-for-large-only,tr.hide-for-xlarge,tr.hide-for-xlarge-up,tr.hide-for-xlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge-only{display:table-row !important}td.show-for-small,td.show-for-small-only,td.show-for-medium-down td.show-for-large-down,td.hide-for-medium,td.hide-for-medium-up,td.hide-for-large,td.hide-for-large-up,td.hide-for-xlarge td.hide-for-xlarge-up,td.hide-for-xxlarge-up,th.show-for-small,th.show-for-small-only,th.show-for-medium-down th.show-for-large-down,th.hide-for-medium,th.hide-for-medium-up,th.hide-for-large,th.hide-for-large-up,th.hide-for-xlarge th.hide-for-xlarge-up,th.hide-for-xxlarge-up{display:table-cell !important}@media only screen and (min-width: 40.063em){.hide-for-small,.hide-for-small-only,.show-for-medium,.show-for-medium-down,.show-for-medium-up,.show-for-medium-only,.hide-for-large,.hide-for-large-up,.hide-for-large-only,.hide-for-xlarge,.hide-for-xlarge-up,.hide-for-xlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge-only{display:inherit !important}.show-for-small,.show-for-small-only,.hide-for-medium,.hide-for-medium-down,.hide-for-medium-up,.hide-for-medium-only,.hide-for-large-down,.show-for-large,.show-for-large-up,.show-for-large-only,.show-for-xlarge,.show-for-xlarge-up,.show-for-xlarge-only,.show-for-xxlarge-up,.show-for-xxlarge-only{display:none !important}table.hide-for-small,table.hide-for-small-only,table.show-for-medium,table.show-for-medium-down,table.show-for-medium-up,table.show-for-medium-only,table.hide-for-large,table.hide-for-large-up,table.hide-for-large-only,table.hide-for-xlarge,table.hide-for-xlarge-up,table.hide-for-xlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge-only{display:table}thead.hide-for-small,thead.hide-for-small-only,thead.show-for-medium,thead.show-for-medium-down,thead.show-for-medium-up,thead.show-for-medium-only,thead.hide-for-large,thead.hide-for-large-up,thead.hide-for-large-only,thead.hide-for-xlarge,thead.hide-for-xlarge-up,thead.hide-for-xlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge-only{display:table-header-group !important}tbody.hide-for-small,tbody.hide-for-small-only,tbody.show-for-medium,tbody.show-for-medium-down,tbody.show-for-medium-up,tbody.show-for-medium-only,tbody.hide-for-large,tbody.hide-for-large-up,tbody.hide-for-large-only,tbody.hide-for-xlarge,tbody.hide-for-xlarge-up,tbody.hide-for-xlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge-only{display:table-row-group !important}tr.hide-for-small,tr.hide-for-small-only,tr.show-for-medium,tr.show-for-medium-down,tr.show-for-medium-up,tr.show-for-medium-only,tr.hide-for-large,tr.hide-for-large-up,tr.hide-for-large-only,tr.hide-for-xlarge,tr.hide-for-xlarge-up,tr.hide-for-xlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge-only{display:table-row !important}td.hide-for-small,td.hide-for-small-only,td.show-for-medium,td.show-for-medium-down,td.show-for-medium-up,td.show-for-medium-only,td.hide-for-large,td.hide-for-large-up,td.hide-for-large-only,td.hide-for-xlarge,td.hide-for-xlarge-up,td.hide-for-xlarge-only,td.hide-for-xxlarge-up,td.hide-for-xxlarge-only,th.hide-for-small,th.hide-for-small-only,th.show-for-medium,th.show-for-medium-down,th.show-for-medium-up,th.show-for-medium-only,th.hide-for-large,th.hide-for-large-up,th.hide-for-large-only,th.hide-for-xlarge,th.hide-for-xlarge-up,th.hide-for-xlarge-only,th.hide-for-xxlarge-up,th.hide-for-xxlarge-only{display:table-cell !important}}@media only screen and (min-width: 64.063em){.hide-for-small,.hide-for-small-only,.hide-for-medium,.hide-for-medium-down,.hide-for-medium-only,.show-for-medium-up,.show-for-large,.show-for-large-up,.show-for-large-only,.hide-for-xlarge,.hide-for-xlarge-up,.hide-for-xlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge-only{display:inherit !important}.show-for-small-only,.show-for-medium,.show-for-medium-down,.show-for-medium-only,.hide-for-large,.hide-for-large-up,.hide-for-large-only,.show-for-xlarge,.show-for-xlarge-up,.show-for-xlarge-only,.show-for-xxlarge-up,.show-for-xxlarge-only{display:none !important}table.hide-for-small,table.hide-for-small-only,table.hide-for-medium,table.hide-for-medium-down,table.hide-for-medium-only,table.show-for-medium-up,table.show-for-large,table.show-for-large-up,table.show-for-large-only,table.hide-for-xlarge,table.hide-for-xlarge-up,table.hide-for-xlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge-only{display:table}thead.hide-for-small,thead.hide-for-small-only,thead.hide-for-medium,thead.hide-for-medium-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.show-for-large,thead.show-for-large-up,thead.show-for-large-only,thead.hide-for-xlarge,thead.hide-for-xlarge-up,thead.hide-for-xlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge-only{display:table-header-group !important}tbody.hide-for-small,tbody.hide-for-small-only,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.show-for-large,tbody.show-for-large-up,tbody.show-for-large-only,tbody.hide-for-xlarge,tbody.hide-for-xlarge-up,tbody.hide-for-xlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge-only{display:table-row-group !important}tr.hide-for-small,tr.hide-for-small-only,tr.hide-for-medium,tr.hide-for-medium-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.show-for-large,tr.show-for-large-up,tr.show-for-large-only,tr.hide-for-xlarge,tr.hide-for-xlarge-up,tr.hide-for-xlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge-only{display:table-row !important}td.hide-for-small,td.hide-for-small-only,td.hide-for-medium,td.hide-for-medium-down,td.hide-for-medium-only,td.show-for-medium-up,td.show-for-large,td.show-for-large-up,td.show-for-large-only,td.hide-for-xlarge,td.hide-for-xlarge-up,td.hide-for-xlarge-only,td.hide-for-xxlarge-up,td.hide-for-xxlarge-only,th.hide-for-small,th.hide-for-small-only,th.hide-for-medium,th.hide-for-medium-down,th.hide-for-medium-only,th.show-for-medium-up,th.show-for-large,th.show-for-large-up,th.show-for-large-only,th.hide-for-xlarge,th.hide-for-xlarge-up,th.hide-for-xlarge-only,th.hide-for-xxlarge-up,th.hide-for-xxlarge-only{display:table-cell !important}}@media only screen and (min-width: 90.063em){.hide-for-small,.hide-for-small-only,.hide-for-medium,.hide-for-medium-down,.hide-for-medium-only,.show-for-medium-up,.show-for-large-up,.hide-for-large-only,.show-for-xlarge,.show-for-xlarge-up,.show-for-xlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge-only{display:inherit !important}.show-for-small-only,.show-for-medium,.show-for-medium-down,.show-for-medium-only,.show-for-large,.show-for-large-only,.show-for-large-down,.hide-for-xlarge,.hide-for-xlarge-up,.hide-for-xlarge-only,.show-for-xxlarge-up,.show-for-xxlarge-only{display:none !important}table.hide-for-small,table.hide-for-small-only,table.hide-for-medium,table.hide-for-medium-down,table.hide-for-medium-only,table.show-for-medium-up,table.show-for-large-up,table.hide-for-large-only,table.show-for-xlarge,table.show-for-xlarge-up,table.show-for-xlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge-only{display:table}thead.hide-for-small,thead.hide-for-small-only,thead.hide-for-medium,thead.hide-for-medium-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.show-for-large-up,thead.hide-for-large-only,thead.show-for-xlarge,thead.show-for-xlarge-up,thead.show-for-xlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge-only{display:table-header-group !important}tbody.hide-for-small,tbody.hide-for-small-only,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.show-for-large-up,tbody.hide-for-large-only,tbody.show-for-xlarge,tbody.show-for-xlarge-up,tbody.show-for-xlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge-only{display:table-row-group !important}tr.hide-for-small,tr.hide-for-small-only,tr.hide-for-medium,tr.hide-for-medium-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.show-for-large-up,tr.hide-for-large-only,tr.show-for-xlarge,tr.show-for-xlarge-up,tr.show-for-xlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge-only{display:table-row !important}td.hide-for-small,td.hide-for-small-only,td.hide-for-medium,td.hide-for-medium-down,td.hide-for-medium-only,td.show-for-medium-up,td.show-for-large-up,td.hide-for-large-only,td.show-for-xlarge,td.show-for-xlarge-up,td.show-for-xlarge-only,td.hide-for-xxlarge-up,td.hide-for-xxlarge-only,th.hide-for-small,th.hide-for-small-only,th.hide-for-medium,th.hide-for-medium-down,th.hide-for-medium-only,th.show-for-medium-up,th.show-for-large-up,th.hide-for-large-only,th.show-for-xlarge,th.show-for-xlarge-up,th.show-for-xlarge-only,th.hide-for-xxlarge-up,th.hide-for-xxlarge-only{display:table-cell !important}}@media only screen and (min-width: 120.063em){.hide-for-small,.hide-for-small-only,.hide-for-medium,.hide-for-medium-down,.hide-for-medium-only,.show-for-medium-up,.show-for-large-up,.hide-for-large-only,.hide-for-xlarge-only,.show-for-xlarge-up,.show-for-xxlarge-up,.show-for-xxlarge-only{display:inherit !important}.show-for-small-only,.show-for-medium,.show-for-medium-down,.show-for-medium-only,.show-for-large,.show-for-large-only,.show-for-large-down,.hide-for-xlarge,.show-for-xlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge-only{display:none !important}table.hide-for-small,table.hide-for-small-only,table.hide-for-medium,table.hide-for-medium-down,table.hide-for-medium-only,table.show-for-medium-up,table.show-for-large-up,table.hide-for-xlarge-only,table.show-for-xlarge-up,table.show-for-xxlarge-up,table.show-for-xxlarge-only{display:table}thead.hide-for-small,thead.hide-for-small-only,thead.hide-for-medium,thead.hide-for-medium-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.show-for-large-up,thead.hide-for-xlarge-only,thead.show-for-xlarge-up,thead.show-for-xxlarge-up,thead.show-for-xxlarge-only{display:table-header-group !important}tbody.hide-for-small,tbody.hide-for-small-only,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.show-for-large-up,tbody.hide-for-xlarge-only,tbody.show-for-xlarge-up,tbody.show-for-xxlarge-up,tbody.show-for-xxlarge-only{display:table-row-group !important}tr.hide-for-small,tr.hide-for-small-only,tr.hide-for-medium,tr.hide-for-medium-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.show-for-large-up,tr.hide-for-xlarge-only,tr.show-for-xlarge-up,tr.show-for-xxlarge-up,tr.show-for-xxlarge-only{display:table-row !important}td.hide-for-small,td.hide-for-small-only,td.hide-for-medium,td.hide-for-medium-down,td.hide-for-medium-only,td.show-for-medium-up,td.show-for-large-up,td.hide-for-xlarge-only,td.show-for-xlarge-up,td.show-for-xxlarge-up,td.show-for-xxlarge-only,th.hide-for-small,th.hide-for-small-only,th.hide-for-medium,th.hide-for-medium-down,th.hide-for-medium-only,th.show-for-medium-up,th.show-for-large-up,th.hide-for-xlarge-only,th.show-for-xlarge-up,th.show-for-xxlarge-up,th.show-for-xxlarge-only{display:table-cell !important}}.show-for-landscape,.hide-for-portrait{display:inherit !important}.hide-for-landscape,.show-for-portrait{display:none !important}table.hide-for-landscape,table.show-for-portrait{display:table}thead.hide-for-landscape,thead.show-for-portrait{display:table-header-group !important}tbody.hide-for-landscape,tbody.show-for-portrait{display:table-row-group !important}tr.hide-for-landscape,tr.show-for-portrait{display:table-row !important}td.hide-for-landscape,td.show-for-portrait,th.hide-for-landscape,th.show-for-portrait{display:table-cell !important}@media only screen and (orientation: landscape){.show-for-landscape,.hide-for-portrait{display:inherit !important}.hide-for-landscape,.show-for-portrait{display:none !important}table.show-for-landscape,table.hide-for-portrait{display:table}thead.show-for-landscape,thead.hide-for-portrait{display:table-header-group !important}tbody.show-for-landscape,tbody.hide-for-portrait{display:table-row-group !important}tr.show-for-landscape,tr.hide-for-portrait{display:table-row !important}td.show-for-landscape,td.hide-for-portrait,th.show-for-landscape,th.hide-for-portrait{display:table-cell !important}}@media only screen and (orientation: portrait){.show-for-portrait,.hide-for-landscape{display:inherit !important}.hide-for-portrait,.show-for-landscape{display:none !important}table.show-for-portrait,table.hide-for-landscape{display:table}thead.show-for-portrait,thead.hide-for-landscape{display:table-header-group !important}tbody.show-for-portrait,tbody.hide-for-landscape{display:table-row-group !important}tr.show-for-portrait,tr.hide-for-landscape{display:table-row !important}td.show-for-portrait,td.hide-for-landscape,th.show-for-portrait,th.hide-for-landscape{display:table-cell !important}}.show-for-touch{display:none !important}.hide-for-touch{display:inherit !important}.touch .show-for-touch{display:inherit !important}.touch .hide-for-touch{display:none !important}table.hide-for-touch{display:table}.touch table.show-for-touch{display:table}thead.hide-for-touch{display:table-header-group !important}.touch thead.show-for-touch{display:table-header-group !important}tbody.hide-for-touch{display:table-row-group !important}.touch tbody.show-for-touch{display:table-row-group !important}tr.hide-for-touch{display:table-row !important}.touch tr.show-for-touch{display:table-row !important}td.hide-for-touch{display:table-cell !important}.touch td.show-for-touch{display:table-cell !important}th.hide-for-touch{display:table-cell !important}.touch th.show-for-touch{display:table-cell !important} 2 | --------------------------------------------------------------------------------