"
5 | exit 1
6 | fi
7 |
8 | APP_NAME=$1
9 |
10 | if [ -n "$CIRCLE_SHA1" ]; then
11 | SHA_TO_DEPLOY=$CIRCLE_SHA1
12 | else
13 | echo "CIRCLE_SHA1 isn't set, deploying the latest revision."
14 | SHA_TO_DEPLOY=`git rev-parse HEAD`
15 | fi
16 |
17 | REMOTE_MISSING=$(git remote | grep heroku | wc -l)
18 |
19 | if [ $REMOTE_MISSING -eq 0 ] ; then
20 | git remote add heroku git@heroku.com:$APP_NAME.git
21 | fi
22 |
23 | PREV_WORKERS=$(heroku ps --app $APP_NAME | grep "^worker." | wc -l | xargs)
24 |
25 | heroku maintenance:on --app $APP_NAME
26 |
27 | heroku scale worker=0 --app $APP_NAME
28 |
29 | # This little hacky morsel gets around a change in the latest git client.
30 | # A better solution is in the works (we hope).
31 | [[ ! -s "$(git rev-parse --git-dir)/shallow" ]] || git fetch --unshallow
32 |
33 | git push -f heroku $SHA_TO_DEPLOY:refs/heads/master
34 |
35 | heroku run rake db:migrate db:seed --app $APP_NAME
36 |
37 | heroku scale worker=$PREV_WORKERS --app $APP_NAME
38 |
39 | heroku maintenance:off --app $APP_NAME
40 |
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 |
3 | # create text with an icon to the left for bootstrap menus and buttons
4 | def text_with_icon(text, icon_name)
5 | raw("#{icon(icon_name)} #{text}")
6 | end
7 |
8 | # generate a standard bootstrap glyphicon
9 | def icon(name)
10 | content_tag(:span, nil, class: "glyphicon glyphicon-#{name}")
11 | end
12 |
13 | # action name to use for the primary submit button on scaffold-created CRUD forms
14 | def btn_action_prefix
15 | case action_name
16 | when 'new', 'create'
17 | 'Create'
18 | when 'edit', 'update'
19 | 'Update'
20 | else
21 | nil
22 | end
23 | end
24 |
25 | # bootstrap icon name to use for the primary submit button on scaffold-created forms
26 | def action_icon_name
27 | case action_name
28 | when 'new', 'create'
29 | 'plus'
30 | when 'edit', 'update'
31 | 'edit'
32 | else
33 | nil
34 | end
35 | end
36 |
37 | def alert_class(alert_type)
38 | alert_type = {
39 | alert: 'danger',
40 | notice: 'info'
41 | }.with_indifferent_access.fetch(alert_type, alert_type.to_s)
42 | "alert-#{alert_type}"
43 | end
44 |
45 | end
46 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.css.scss:
--------------------------------------------------------------------------------
1 | @import 'bootstrap-sprockets';
2 | @import 'bootstrap';
3 | @import '_navbar';
4 | @import '_footer';
5 |
6 | #localVideo {
7 | -moz-transform: scaleX(-1);
8 | -o-transform: scaleX(-1);
9 | -webkit-transform: scaleX(-1);
10 | transform: scaleX(-1);
11 | filter: FlipH;
12 | -ms-filter: "FlipH";
13 | }
14 |
15 | #remoteVideos video {
16 | position: fixed;
17 | right: 0;
18 | bottom: 0;
19 | min-width: 100%;
20 | min-height: 100%;
21 | width: auto;
22 | height: auto;
23 | z-index: -100;
24 | background-size: cover;
25 | }
26 |
27 | #allVideos {
28 | position: fixed;
29 | margin: 0;
30 | left: 5px;
31 | bottom: 0;
32 |
33 | li {
34 | position: relative;
35 | padding: 0 5px 0 0;
36 | }
37 | video {
38 | width: 160px;
39 | height: 90px;
40 | }
41 |
42 | .name {
43 | position: absolute;
44 | color: white;
45 | font-weight: bold;
46 | right: 5px;
47 | bottom: 5px;
48 | background-color: black;
49 | padding: 2px 5px;
50 | }
51 | }
52 | #messages {
53 | position: fixed;
54 | bottom: 0;
55 | margin-bottom: 0;
56 | padding: 10px;
57 | right: 0;
58 | background-color: black;
59 | color: white;
60 | opacity: 0.7;
61 | }
62 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Heroku uses the ruby version to configure your application's runtime.
4 | ruby '2.1.2'
5 |
6 | gem 'unicorn'
7 | gem 'rack-canonical-host'
8 | gem 'rails', '~> 4.1.5'
9 | gem 'pg'
10 |
11 | gem 'slim-rails'
12 | gem 'sass-rails'
13 | gem 'bootstrap-sass'
14 | gem 'jquery-rails'
15 | gem 'coffee-rails'
16 | gem 'turbolinks'
17 | gem 'simple_form', '~> 3.1.0.rc2' # Bootstrap 3 support
18 | gem 'uglifier'
19 | gem 'pusher'
20 | gem 'awesome_print'
21 |
22 | group :production, :acceptance do
23 | gem 'rails_stdout_logging'
24 | gem 'heroku_rails_deflate'
25 | end
26 |
27 | group :test do
28 | gem 'fuubar'
29 | gem 'jasminerice', github: 'bradphelan/jasminerice' # Latest release still depends on haml.
30 | gem 'capybara'
31 | #gem 'capybara-email'
32 | gem 'poltergeist'
33 | gem 'factory_girl_rails'
34 | #gem 'timecop'
35 | gem 'database_cleaner'
36 | gem 'simplecov'
37 | end
38 |
39 | group :test, :development do
40 | gem 'rspec-rails'
41 | #gem 'cane'
42 | #gem 'morecane'
43 | end
44 |
45 | group :development do
46 | gem 'spring'
47 | gem 'spring-commands-rspec'
48 | gem 'foreman'
49 | gem 'launchy'
50 | gem 'better_errors'
51 | gem 'binding_of_caller'
52 | gem 'quiet_assets'
53 | gem 'guard', '~> 2'
54 | gem 'guard-rspec'
55 | gem 'guard-jasmine'
56 | gem 'guard-livereload'
57 | gem 'rb-fsevent'
58 | gem 'growl'
59 | end
60 |
--------------------------------------------------------------------------------
/Guardfile:
--------------------------------------------------------------------------------
1 | # More info at https://github.com/guard/guard#readme
2 |
3 | guard :livereload do
4 | watch(%r{app/views/.+\.slim$})
5 | watch(%r{app/helpers/.+\.rb})
6 | watch(%r{public/.+\.(css|js|html)})
7 | watch(%r{conf ig/locales/.+\.yml})
8 |
9 | # Rails Assets Pipeline
10 | watch(%r{(app|vendor)(/assets/\w+/(.+\.(css|less|js|html))).*}) { |m| "/assets/#{m[3]}" }
11 | end
12 |
13 | guard :rspec do
14 | watch(%r{^spec/.+_spec\.rb$})
15 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
16 | watch('spec/spec_helper.rb') { "spec" }
17 |
18 | # Rails example
19 | watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
20 | watch(%r{^app/(.*)(\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
21 | watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/requests/#{m[1]}_spec.rb"] }
22 | watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
23 | watch('app/controllers/application_controller.rb') { "spec/controllers" }
24 |
25 | # Capybara request specs
26 | watch(%r{^app/views/(.+)/.*(\.slim)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
27 | end
28 |
29 | guard :jasmine do
30 | watch(%r{spec/javascripts/spec\.(js\.coffee|js|coffee)$}) { 'spec/javascripts' }
31 | watch(%r{spec/javascripts/.+_spec\.(js\.coffee|js|coffee)$})
32 | watch(%r{app/assets/javascripts/(.+?)\.(js\.coffee|js|coffee)(?:\.\w+)*$}) { |m| "spec/javascripts/#{ m[1] }_spec.#{ m[2] }" }
33 | end
34 |
35 |
--------------------------------------------------------------------------------
/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
48 |
49 |
50 |
51 |
52 |
53 |
We're sorry, but something went wrong.
54 |
55 | If you are the application owner check the logs for more information.
56 |
57 |
58 |
--------------------------------------------------------------------------------
/lib/templates/slim/scaffold/index.html.slim:
--------------------------------------------------------------------------------
1 | div.page-header
2 | h1 Listing <%= plural_table_name %>
3 |
4 | table.table.table-striped
5 | thead
6 | tr
7 | th ID
8 | <% attributes.each do |attribute| -%>
9 | th <%= attribute.human_name %>
10 | <% end -%>
11 | th Actions
12 |
13 | tbody
14 | - @<%= plural_table_name %>.each do |<%= singular_table_name %>|
15 | tr
16 | /td= link_to_if can?(:show, <%= singular_table_name %>), <%= singular_table_name %>.id, <%= singular_table_name %>_path(<%= singular_table_name %>)
17 | td= link_to <%= singular_table_name %>.id, <%= singular_table_name %>_path(<%= singular_table_name %>)
18 | <% attributes.each do |attribute| -%>
19 | td= <%= singular_table_name %>.<%= attribute.name %>
20 | <% end -%>
21 | td
22 | /- if can? :edit, <%= singular_table_name %>
23 | = link_to text_with_icon('Edit', 'edit'), edit_<%= singular_table_name %>_path(<%= singular_table_name %>), class: 'btn btn-default btn-xs'
24 | '
25 | /- if can? :destroy, <%= singular_table_name %>
26 | = link_to text_with_icon('Destroy', 'remove'), <%= singular_table_name %>_path(<%= singular_table_name %>), \
27 | method: :delete, data: { confirm: "Are you sure?" }, class: 'btn btn-default btn-xs btn-danger'
28 |
29 | /- if can? :create, <%= singular_table_name.classify %>
30 | = link_to text_with_icon('New <%= human_name %>', 'plus'), new_<%= singular_table_name %>_path, class: 'btn btn-primary'
31 |
--------------------------------------------------------------------------------
/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
48 |
49 |
50 |
51 |
52 |
53 |
The change you wanted was rejected.
54 |
Maybe you tried to change something you didn't have access to.
55 |
56 | If you are the application owner check the logs for more information.
57 |
58 |
59 |
--------------------------------------------------------------------------------
/lib/tasks/spec.rake:
--------------------------------------------------------------------------------
1 | begin
2 | require 'rspec/core'
3 | require 'rspec/core/rake_task'
4 |
5 | namespace :spec do
6 | desc "Run the code examples in spec/ except those in spec/features"
7 | RSpec::Core::RakeTask.new('without_features' => 'db:test:prepare') do |t|
8 | file_list = FileList['spec/**/*_spec.rb'].exclude('spec/features/**/*_spec.rb')
9 |
10 | t.pattern = file_list
11 | end
12 | end
13 |
14 | rescue LoadError
15 | namespace :spec do
16 | task :without_requests do
17 | end
18 | end
19 | end
20 |
21 | begin
22 | require 'guard/jasmine/task'
23 |
24 | namespace :spec do
25 | desc "Run all javascript specs"
26 | task :javascripts do
27 | begin
28 | ::Guard::Jasmine::CLI.start([])
29 |
30 | rescue SystemExit => e
31 | case e.status
32 | when 1
33 | fail "Some specs have failed."
34 | when 2
35 | fail "The spec couldn't be run: #{e.message}."
36 | end
37 | end
38 | end
39 |
40 | desc 'Runs specs with coverage and cane checks'
41 | task cane: ['spec:enable_coverage', 'spec:coverage', 'quality']
42 | end
43 |
44 | Rake::Task['spec'].enhance do
45 | Rake::Task['spec:javascripts'].invoke
46 | end
47 |
48 | rescue LoadError
49 | namespace :spec do
50 | task :javascripts do
51 | puts "Guard is not available in this environment: #{Rails.env}."
52 | end
53 | end
54 | end
55 |
56 |
57 | Rake::Task['spec'].clear_actions
58 |
59 | desc 'Runs all specs'
60 | task spec: ['spec:without_features', 'spec:features', 'spec:javascripts']
61 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
48 |
49 |
50 |
51 |
52 |
53 |
The page you were looking for doesn't exist.
54 |
You may have mistyped the address or the page may have moved.
55 |
56 | If you are the application owner check the logs for more information.
57 |
58 |
59 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require 'rails/all'
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module Pushertc
10 | class Application < Rails::Application
11 | # Settings in config/environments/* take precedence over those specified here.
12 | # Application configuration should go into files in config/initializers
13 | # -- all .rb files in that directory are automatically loaded.
14 |
15 | # Disable unwanted generators.
16 | config.generators do |generate|
17 | generate.stylesheets false
18 | #generate.helper false
19 | generate.routing_specs false
20 | #generate.view_specs false
21 | generate.request_specs false
22 | end
23 |
24 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
25 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
26 | # config.time_zone = 'Central Time (US & Canada)'
27 |
28 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
29 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
30 | # config.i18n.default_locale = :de
31 |
32 | # Don't initialize the application when precompiling assets. Doing so on Heroku problematic
33 | # since the environment config is not available during slug completions (see user-env-compile).
34 | config.assets.initialize_on_precompile = false
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports and disable caching.
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send.
17 | config.action_mailer.raise_delivery_errors = false
18 |
19 | # Print deprecation notices to the Rails logger.
20 | config.active_support.deprecation = :log
21 |
22 | # Raise an error on page load if there are pending migrations.
23 | config.active_record.migration_error = :page_load
24 |
25 | # Debug mode disables concatenation and preprocessing of assets.
26 | # This option may cause significant delays in view rendering with a large
27 | # number of complex assets.
28 | config.assets.debug = true
29 |
30 | # Adds additional error checking when serving assets at runtime.
31 | # Checks for improperly declared sprockets dependencies.
32 | # Raises helpful error messages.
33 | config.assets.raise_runtime_errors = true
34 |
35 | # Raises error for missing translations
36 | # config.action_view.raise_on_missing_translations = true
37 |
38 | config.action_mailer.default_url_options = { host: 'localhost:3000' }
39 |
40 | end
41 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure static asset server for tests with Cache-Control for performance.
16 | config.serve_static_assets = true
17 | config.static_cache_control = 'public, max-age=3600'
18 |
19 | # Show full error reports and disable caching.
20 | config.consider_all_requests_local = true
21 | config.action_controller.perform_caching = false
22 |
23 | # Raise exceptions instead of rendering exception templates.
24 | config.action_dispatch.show_exceptions = false
25 |
26 | # Disable request forgery protection in test environment.
27 | config.action_controller.allow_forgery_protection = false
28 |
29 | # Tell Action Mailer not to deliver emails to the real world.
30 | # The :test delivery method accumulates sent emails in the
31 | # ActionMailer::Base.deliveries array.
32 | config.action_mailer.delivery_method = :test
33 |
34 | # Print deprecation notices to the stderr.
35 | config.active_support.deprecation = :stderr
36 |
37 | # Raises error for missing translations
38 | # config.action_view.raise_on_missing_translations = true
39 |
40 | config.action_mailer.default_url_options = { host: 'example.com' }
41 | end
42 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.slim:
--------------------------------------------------------------------------------
1 | doctype 5
2 | html
3 | head
4 | title PusherTC
5 | = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
6 | = javascript_include_tag 'application', 'data-turbolinks-track' => true
7 | = csrf_meta_tag
8 | meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
9 |
10 | body id=(controller.controller_name) class=(controller.action_name)
11 | .wrapper
12 | nav.navbar.navbar-default.navbar-fixed-top role='navigation'
13 | .container
14 | .navbar-header
15 | button.button.navbar-toggle data-toggle='collapse' data-target='.navbar-collapse'
16 | span.icon-bar
17 | span.icon-bar
18 | span.icon-bar
19 | a.navbar-brand href='/' PusherTC
20 | .collapse.navbar-collapse
21 | form#send-message.navbar-form.navbar-right
22 | .form-group
23 | input.form-control#message
24 | '
25 | button.btn.btn-success type='submit' Send Message
26 |
27 | .container
28 | #name-prompt.modal.fade
29 | .modal-dialog
30 | form
31 | .modal-content
32 | .modal-header
33 | button.close type="button" data-dismiss="modal"
34 | span aria-hidden="true" ×
35 | span.sr-only Close
36 | h4.modal-title What's your name?
37 | .modal-body
38 | input.form-control autofocus=true
39 | .modal-footer
40 | button.btn.btn-success type='submit' Ok
41 |
42 | #chat data-api-key=Pusher.key data-auth-endpoint=pusher_auth_path
43 | br
44 |
45 | #remoteVideos
46 |
47 | ul#allVideos.list-inline
48 | li
49 | video#localVideo autoplay=true muted="muted"
50 |
51 | dl.dl-horizontal#messages
52 |
--------------------------------------------------------------------------------
/spec/rails_helper.rb:
--------------------------------------------------------------------------------
1 | # This file is copied to spec/ when you run 'rails generate rspec:install'
2 | ENV['RAILS_ENV'] ||= 'test'
3 | require 'spec_helper'
4 | require File.expand_path('../../config/environment', __FILE__)
5 | require 'rspec/rails'
6 |
7 | # Requires supporting ruby files with custom matchers and macros, etc, in
8 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
9 | # run as spec files by default. This means that files in spec/support that end
10 | # in _spec.rb will both be required and run as specs, causing the specs to be
11 | # run twice. It is recommended that you do not name files matching this glob to
12 | # end with _spec.rb. You can configure this pattern with the --pattern
13 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
14 | Dir[Rails.root.join('spec/support/**/*.rb')].sort.each { |f| require f }
15 |
16 | # Checks for pending migrations before tests are run.
17 | # If you are not using ActiveRecord, you can remove this line.
18 | ActiveRecord::Migration.maintain_test_schema!
19 |
20 | RSpec.configure do |config|
21 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
22 | #config.fixture_path = "#{::Rails.root}/spec/fixtures"
23 |
24 | # See database_cleaner.rb for database cleanup details.
25 | config.use_transactional_fixtures = false
26 |
27 | # RSpec Rails can automatically mix in different behaviours to your tests
28 | # based on their file location, for example enabling you to call `get` and
29 | # `post` in specs under `spec/controllers`.
30 | #
31 | # You can disable this behaviour by removing the line below, and instead
32 | # explicitly tag your specs with their type, e.g.:
33 | #
34 | # RSpec.describe UsersController, :type => :controller do
35 | # # ...
36 | # end
37 | #
38 | # The different available types are documented in the features, such as in
39 | # https://relishapp.com/rspec/rspec-rails/docs
40 | config.infer_spec_type_from_file_location!
41 | end
42 |
--------------------------------------------------------------------------------
/lib/templates/rails/scaffold_controller/controller.rb:
--------------------------------------------------------------------------------
1 | <% if namespaced? -%>
2 | require_dependency "<%= namespaced_file_path %>/application_controller"
3 |
4 | <% end -%>
5 | <% module_namespacing do -%>
6 | class <%= controller_class_name %>Controller < ApplicationController
7 | before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy]
8 |
9 | def index
10 | @<%= plural_table_name %> = <%= orm_class.all(class_name) %>
11 | end
12 |
13 | def show
14 | end
15 |
16 | def new
17 | @<%= singular_table_name %> = <%= orm_class.build(class_name) %>
18 | end
19 |
20 | def edit
21 | end
22 |
23 | def create
24 | @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
25 |
26 | if @<%= orm_instance.save %>
27 | redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %>
28 | else
29 | render action: 'new'
30 | end
31 | end
32 |
33 | def update
34 | if @<%= orm_instance.update("#{singular_table_name}_params") %>
35 | redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %>
36 | else
37 | render action: 'edit'
38 | end
39 | end
40 |
41 | def destroy
42 | @<%= orm_instance.destroy %>
43 | redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %>
44 | end
45 |
46 | private
47 | # Use callbacks to share common setup or constraints between actions.
48 | def set_<%= singular_table_name %>
49 | @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
50 | end
51 |
52 | # Only allow a trusted parameter "white list" through.
53 | def <%= "#{singular_table_name}_params" %>
54 | <%- if attributes_names.empty? -%>
55 | params[<%= ":#{singular_table_name}" %>]
56 | <%- else -%>
57 | params.require(<%= ":#{singular_table_name}" %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
58 | <%- end -%>
59 | end
60 | end
61 | <% end -%>
62 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both thread web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application
18 | # Add `rack-cache` to your Gemfile before enabling this.
19 | # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.
20 | # config.action_dispatch.rack_cache = true
21 |
22 | # When deploying to Heroku, the app must serve static assets (or serve them from a CDN).
23 | config.serve_static_assets = true
24 |
25 | # Compress JavaScripts and CSS.
26 | config.assets.js_compressor = :uglifier
27 | # config.assets.css_compressor = :sass
28 |
29 | # Do not fallback to assets pipeline if a precompiled asset is missed.
30 | config.assets.compile = false
31 |
32 | # Generate digests for assets URLs.
33 | config.assets.digest = true
34 |
35 | # Specifies the header that your server uses for sending files.
36 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for apache
37 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
38 |
39 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
40 | # config.force_ssl = true
41 |
42 | # Set to :debug to see everything in the log.
43 | config.log_level = :info
44 |
45 | # Prepend all log lines with the following tags.
46 | # config.log_tags = [ :subdomain, :uuid ]
47 |
48 | # Use a different logger for distributed setups.
49 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
50 |
51 | # Use a different cache store in production.
52 | # config.cache_store = :mem_cache_store
53 |
54 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
55 | # config.action_controller.asset_host = "http://assets.example.com"
56 |
57 | # Precompile additional assets.
58 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
59 | # config.assets.precompile += %w( search.js )
60 |
61 | # Ignore bad email addresses and do not raise email delivery errors.
62 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
63 | # config.action_mailer.raise_delivery_errors = false
64 |
65 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
66 | # the I18n.default_locale when a translation cannot be found).
67 | config.i18n.fallbacks = true
68 |
69 | # Send deprecation notices to registered listeners.
70 | config.active_support.deprecation = :notify
71 |
72 | # Disable automatic flushing of the log to improve performance.
73 | # config.autoflush_log = false
74 |
75 | # Use default logging formatter so that PID and timestamp are not suppressed.
76 | config.log_formatter = ::Logger::Formatter.new
77 |
78 | # Do not dump schema after migrations.
79 | config.active_record.dump_schema_after_migration = false
80 |
81 | config.action_mailer.default_url_options = { host: 'pushertc.herokuapp.com' }
82 | end
83 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # Coverage must be enabled before the application is loaded.
2 | if ENV['COVERAGE']
3 | require 'simplecov'
4 |
5 | # Writes the coverage stat to a file to be used by Cane.
6 | class SimpleCov::Formatter::QualityFormatter
7 | def format(result)
8 | SimpleCov::Formatter::HTMLFormatter.new.format(result)
9 | File.open('coverage/covered_percent', 'w') do |f|
10 | f.puts result.source_files.covered_percent.to_f
11 | end
12 | end
13 | end
14 | SimpleCov.formatter = SimpleCov::Formatter::QualityFormatter
15 |
16 | SimpleCov.start do
17 | add_filter '/spec/'
18 | add_filter '/config/'
19 | add_filter '/vendor/'
20 | add_group 'Models', 'app/models'
21 | add_group 'Controllers', 'app/controllers'
22 | add_group 'Helpers', 'app/helpers'
23 | add_group 'Views', 'app/views'
24 | add_group 'Mailers', 'app/mailers'
25 | end
26 | end
27 |
28 | # Given that it is always loaded, you are encouraged to keep this file as
29 | # light-weight as possible. Requiring heavyweight dependencies from this file
30 | # will add to the boot time of your test suite on EVERY test run, even for an
31 | # individual file that may not need all of that loaded. Instead, make a
32 | # separate helper file that requires this one and then use it only in the specs
33 | # that actually need it.
34 | #
35 | # The `.rspec` file also contains a few flags that are not defaults but that
36 | # users commonly want.
37 | #
38 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
39 | RSpec.configure do |config|
40 | # The settings below are suggested to provide a good initial experience
41 | # with RSpec, but feel free to customize to your heart's content.
42 |
43 | # These two settings work together to allow you to limit a spec run
44 | # to individual examples or groups you care about by tagging them with
45 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples
46 | # get run.
47 | config.filter_run :focus
48 | config.run_all_when_everything_filtered = true
49 |
50 | # Many RSpec users commonly either run the entire suite or an individual
51 | # file, and it's useful to allow more verbose output when running an
52 | # individual spec file.
53 | if config.files_to_run.one?
54 | # Use the documentation formatter for detailed output,
55 | # unless a formatter has already been configured
56 | # (e.g. via a command-line flag).
57 | config.default_formatter = 'doc'
58 | end
59 |
60 | # Print the 10 slowest examples and example groups at the
61 | # end of the spec run, to help surface which specs are running
62 | # particularly slow.
63 | # config.profile_examples = 10
64 |
65 | # Run specs in random order to surface order dependencies. If you find an
66 | # order dependency and want to debug it, you can fix the order by providing
67 | # the seed, which is printed after each run.
68 | # --seed 1234
69 | # config.order = :random
70 |
71 | # Run non-feature specs (shuffled) before feature specs.
72 | config.register_ordering(:global) do |items|
73 | features, others = items.partition { |g| g.metadata[:type] == :feature }
74 | others.shuffle + features
75 | end
76 |
77 | # Seed global randomization in this process using the `--seed` CLI option.
78 | # Setting this allows you to use `--seed` to deterministically reproduce
79 | # test failures related to randomization by passing the same `--seed` value
80 | # as the one that triggered the failure.
81 | Kernel.srand config.seed
82 |
83 | # rspec-expectations config goes here. You can use an alternate
84 | # assertion/expectation library such as wrong or the stdlib/minitest
85 | # assertions if you prefer.
86 | config.expect_with :rspec do |expectations|
87 | # Enable only the newer, non-monkey-patching expect syntax.
88 | # For more details, see:
89 | # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
90 | expectations.syntax = :expect
91 | end
92 |
93 | # rspec-mocks config goes here. You can use an alternate test double
94 | # library (such as bogus or mocha) by changing the `mock_with` option here.
95 | config.mock_with :rspec do |mocks|
96 | # Enable only the newer, non-monkey-patching expect syntax.
97 | # For more details, see:
98 | # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
99 | mocks.syntax = :expect
100 |
101 | # Prevents you from mocking or stubbing a method that does not exist on
102 | # a real object. This is generally recommended.
103 | mocks.verify_partial_doubles = true
104 | end
105 | end
106 |
--------------------------------------------------------------------------------
/config/initializers/simple_form_bootstrap.rb:
--------------------------------------------------------------------------------
1 | # Use this setup block to configure all options available in SimpleForm.
2 | SimpleForm.setup do |config|
3 | config.error_notification_class = 'alert alert-danger'
4 | config.button_class = 'btn btn-default'
5 | config.boolean_label_class = nil
6 |
7 | config.wrappers :vertical_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
8 | b.use :html5
9 | b.use :placeholder
10 | b.optional :maxlength
11 | b.optional :pattern
12 | b.optional :min_max
13 | b.optional :readonly
14 | b.use :label, class: 'control-label'
15 |
16 | b.use :input, class: 'form-control'
17 | b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
18 | b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
19 | end
20 |
21 | config.wrappers :vertical_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
22 | b.use :html5
23 | b.use :placeholder
24 | b.optional :maxlength
25 | b.optional :readonly
26 | b.use :label, class: 'control-label'
27 |
28 | b.use :input
29 | b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
30 | b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
31 | end
32 |
33 | config.wrappers :vertical_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
34 | b.use :html5
35 | b.optional :readonly
36 |
37 | b.wrapper tag: 'div', class: 'checkbox' do |ba|
38 | ba.use :label_input
39 | end
40 |
41 | b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
42 | b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
43 | end
44 |
45 | config.wrappers :vertical_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
46 | b.use :html5
47 | b.optional :readonly
48 | b.use :label, class: 'control-label'
49 | b.use :input
50 | b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
51 | b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
52 | end
53 |
54 | config.wrappers :horizontal_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
55 | b.use :html5
56 | b.use :placeholder
57 | b.optional :maxlength
58 | b.optional :pattern
59 | b.optional :min_max
60 | b.optional :readonly
61 | b.use :label, class: 'col-sm-3 control-label'
62 |
63 | b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
64 | ba.use :input, class: 'form-control'
65 | ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
66 | ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
67 | end
68 | end
69 |
70 | config.wrappers :horizontal_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
71 | b.use :html5
72 | b.use :placeholder
73 | b.optional :maxlength
74 | b.optional :readonly
75 | b.use :label, class: 'col-sm-3 control-label'
76 |
77 | b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
78 | ba.use :input
79 | ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
80 | ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
81 | end
82 | end
83 |
84 | config.wrappers :horizontal_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
85 | b.use :html5
86 | b.optional :readonly
87 |
88 | b.wrapper tag: 'div', class: 'col-sm-offset-3 col-sm-9' do |wr|
89 | wr.wrapper tag: 'div', class: 'checkbox' do |ba|
90 | ba.use :label_input, class: 'col-sm-9'
91 | end
92 |
93 | wr.use :error, wrap_with: { tag: 'span', class: 'help-block' }
94 | wr.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
95 | end
96 | end
97 |
98 | config.wrappers :horizontal_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
99 | b.use :html5
100 | b.optional :readonly
101 |
102 | b.use :label, class: 'col-sm-3 control-label'
103 |
104 | b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
105 | ba.use :input
106 | ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
107 | ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
108 | end
109 | end
110 |
111 | config.wrappers :inline_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
112 | b.use :html5
113 | b.use :placeholder
114 | b.optional :maxlength
115 | b.optional :pattern
116 | b.optional :min_max
117 | b.optional :readonly
118 | b.use :label, class: 'sr-only'
119 |
120 | b.use :input, class: 'form-control'
121 | b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
122 | b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
123 | end
124 |
125 | # Wrappers for forms and inputs using the Bootstrap toolkit.
126 | # Check the Bootstrap docs (http://getbootstrap.com)
127 | # to learn about the different styles for forms and inputs,
128 | # buttons and other elements.
129 | config.default_wrapper = :vertical_form
130 | end
131 |
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file.
9 | //
10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11 | // GO AFTER THE REQUIRES BELOW.
12 | //
13 | //= require jquery
14 | //= require jquery_ujs
15 | //= require bootstrap
16 | //= require pusher
17 | //= require simplepeer
18 | //= require hark
19 |
20 | // not a real GUID, but it will do
21 | // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
22 | var guid = (function() {
23 | function s4() {
24 | return Math.floor((1 + Math.random()) * 0x10000)
25 | .toString(16)
26 | .substring(1);
27 | }
28 | return function() {
29 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
30 | s4() + '-' + s4() + s4() + s4();
31 | };
32 | })();
33 |
34 | navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
35 |
36 | var mediaOptions = {
37 | audio: true,
38 | video: {
39 | mandatory: {
40 | minWidth: 1280,
41 | minHeight: 720
42 | }
43 | }
44 | };
45 |
46 | $(function() {
47 | var $messages = $('#messages'),
48 | $modal = $('#name-prompt').modal({ backdrop: 'static' })
49 |
50 | $modal.find('button').click(function(e) {
51 | e.preventDefault();
52 | name = $modal.find('input').val().trim();
53 |
54 | if (name === '') return;
55 |
56 | $modal.modal('hide');
57 |
58 | var currentUser = {
59 | name: name,
60 | id: guid(),
61 | stream: undefined
62 | };
63 |
64 | navigator.getUserMedia(mediaOptions, function(stream) {
65 | currentUser.stream = stream;
66 | var video = $('#localVideo')[0];
67 | video.src = window.URL.createObjectURL(stream);
68 |
69 | start();
70 | }, function() {});
71 |
72 |
73 | function start() {
74 | var pusher = new Pusher($('#chat').data().apiKey, {
75 | authEndpoint: '/pusher/auth',
76 | auth: {
77 | params: currentUser
78 | }
79 | });
80 |
81 | var channel = pusher.subscribe('presence-chat');
82 | var peers = {};
83 |
84 | function lookForPeers() {
85 | for (var userId in channel.members.members) {
86 | if (userId != currentUser.id) {
87 | var member = channel.members.members[userId];
88 |
89 | peers[userId] = initiateConnection(userId, member.name)
90 | }
91 | }
92 | }
93 |
94 | channel.bind('pusher:subscription_succeeded', lookForPeers);
95 |
96 | function gotRemoteVideo(userId, userName, stream) {
97 | var video = $("");
98 | video[0].src = window.URL.createObjectURL(stream);
99 | $('#remoteVideos').append(video);
100 |
101 | var preview = $("");
102 | preview.append("");
103 | preview.append("" + userName + "
")
104 | preview.find('video')[0].src = window.URL.createObjectURL(stream);
105 |
106 | $('#allVideos').append(preview);
107 | }
108 |
109 | function appendMessage(name, message) {
110 | $messages.append('' + name + '');
111 | $messages.append('' + message + '');
112 | }
113 |
114 | function close(userId, name) {
115 | var peer = peers[userId];
116 | if (peer) {
117 | peer.destroy();
118 | peers[userId] = undefined;
119 | }
120 | $("[data-user-id='" + userId + "']").remove();
121 | appendMessage(name, 'Disconnected');
122 | }
123 |
124 | function setupPeer(peerUserId, peerUserName, initiator) {
125 | var peer = new SimplePeer({ initiator: initiator, stream: currentUser.stream, trickle: false });
126 |
127 | peer.on('signal', function (data) {
128 | channel.trigger('client-signal-' + peerUserId, {
129 | userId: currentUser.id, userName: currentUser.name, data: data
130 | });
131 | });
132 |
133 | peer.on('stream', function(stream) { gotRemoteVideo(peerUserId, peerUserName, stream) });
134 | peer.on('close', function() { close(peerUserId, peerUserName) });
135 | $(window).on('beforeunload', function() { close(peerUserId, peerUserName) });
136 |
137 | peer.on('message', function (data) {
138 | if (data == '__SPEAKING__') {
139 | $('#remoteVideos video').hide();
140 | $("#remoteVideos video[data-user-id='" + peerUserId + "']").show();
141 | } else {
142 | appendMessage(peerUserName, data);
143 | }
144 | });
145 |
146 | return peer;
147 | }
148 |
149 | function initiateConnection(peerUserId, peerUserName) {
150 | return setupPeer(peerUserId, peerUserName, true);
151 | };
152 |
153 | channel.bind('client-signal-' + currentUser.id, function(signal) {
154 | var peer = peers[signal.userId];
155 |
156 | if (peer === undefined) {
157 | peer = setupPeer(signal.userId, signal.userName, false);
158 | }
159 |
160 | peer.on('ready', function() {
161 | appendMessage(signal.userName, 'Connected');
162 | });
163 | peer.signal(signal.data)
164 | });
165 |
166 | var speech = hark(currentUser.stream);
167 |
168 | speech.on('speaking', function() {
169 | for (var userId in peers) {
170 | var peer = peers[userId];
171 | peer.send('__SPEAKING__');
172 | }
173 | });
174 |
175 | $('#send-message').submit(function(e) {
176 | e.preventDefault();
177 | var $input = $(this).find('input'),
178 | message = $input.val();
179 |
180 | $input.val('');
181 |
182 | for (var userId in peers) {
183 | var peer = peers[userId];
184 | peer.send(message);
185 | }
186 | appendMessage(currentUser.name, message);
187 | });
188 | }
189 | });
190 | });
191 |
192 |
--------------------------------------------------------------------------------
/config/initializers/simple_form.rb:
--------------------------------------------------------------------------------
1 | # Use this setup block to configure all options available in SimpleForm.
2 | SimpleForm.setup do |config|
3 | # Wrappers are used by the form builder to generate a
4 | # complete input. You can remove any component from the
5 | # wrapper, change the order or even add your own to the
6 | # stack. The options given below are used to wrap the
7 | # whole input.
8 | config.wrappers :default, class: :input,
9 | hint_class: :field_with_hint, error_class: :field_with_errors do |b|
10 | ## Extensions enabled by default
11 | # Any of these extensions can be disabled for a
12 | # given input by passing: `f.input EXTENSION_NAME => false`.
13 | # You can make any of these extensions optional by
14 | # renaming `b.use` to `b.optional`.
15 |
16 | # Determines whether to use HTML5 (:email, :url, ...)
17 | # and required attributes
18 | b.use :html5
19 |
20 | # Calculates placeholders automatically from I18n
21 | # You can also pass a string as f.input placeholder: "Placeholder"
22 | b.use :placeholder
23 |
24 | ## Optional extensions
25 | # They are disabled unless you pass `f.input EXTENSION_NAME => true`
26 | # to the input. If so, they will retrieve the values from the model
27 | # if any exists. If you want to enable any of those
28 | # extensions by default, you can change `b.optional` to `b.use`.
29 |
30 | # Calculates maxlength from length validations for string inputs
31 | b.optional :maxlength
32 |
33 | # Calculates pattern from format validations for string inputs
34 | b.optional :pattern
35 |
36 | # Calculates min and max from length validations for numeric inputs
37 | b.optional :min_max
38 |
39 | # Calculates readonly automatically from readonly attributes
40 | b.optional :readonly
41 |
42 | ## Inputs
43 | b.use :label_input
44 | b.use :hint, wrap_with: { tag: :span, class: :hint }
45 | b.use :error, wrap_with: { tag: :span, class: :error }
46 |
47 | ## full_messages_for
48 | # If you want to display the full error message for the attribute, you can
49 | # use the component :full_error, like:
50 | #
51 | # b.use :full_error, wrap_with: { tag: :span, class: :error }
52 | end
53 |
54 | # The default wrapper to be used by the FormBuilder.
55 | config.default_wrapper = :default
56 |
57 | # Define the way to render check boxes / radio buttons with labels.
58 | # Defaults to :nested for bootstrap config.
59 | # inline: input + label
60 | # nested: label > input
61 | config.boolean_style = :nested
62 |
63 | # Default class for buttons
64 | config.button_class = 'btn'
65 |
66 | # Method used to tidy up errors. Specify any Rails Array method.
67 | # :first lists the first message for each field.
68 | # Use :to_sentence to list all errors for each field.
69 | # config.error_method = :first
70 |
71 | # Default tag used for error notification helper.
72 | config.error_notification_tag = :div
73 |
74 | # CSS class to add for error notification helper.
75 | config.error_notification_class = 'error_notification'
76 |
77 | # ID to add for error notification helper.
78 | # config.error_notification_id = nil
79 |
80 | # Series of attempts to detect a default label method for collection.
81 | # config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
82 |
83 | # Series of attempts to detect a default value method for collection.
84 | # config.collection_value_methods = [ :id, :to_s ]
85 |
86 | # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
87 | # config.collection_wrapper_tag = nil
88 |
89 | # You can define the class to use on all collection wrappers. Defaulting to none.
90 | # config.collection_wrapper_class = nil
91 |
92 | # You can wrap each item in a collection of radio/check boxes with a tag,
93 | # defaulting to :span. Please note that when using :boolean_style = :nested,
94 | # SimpleForm will force this option to be a label.
95 | # config.item_wrapper_tag = :span
96 |
97 | # You can define a class to use in all item wrappers. Defaulting to none.
98 | # config.item_wrapper_class = nil
99 |
100 | # How the label text should be generated altogether with the required text.
101 | # config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" }
102 | config.label_text = lambda { |label, required, explicit_label| "#{label}" }
103 |
104 | # You can define the class to use on all labels. Default is nil.
105 | # config.label_class = nil
106 |
107 | # You can define the class to use on all forms. Default is simple_form.
108 | # config.form_class = :simple_form
109 |
110 | # You can define which elements should obtain additional classes
111 | # config.generate_additional_classes_for = [:wrapper, :label, :input]
112 |
113 | # Whether attributes are required by default (or not). Default is true.
114 | # config.required_by_default = true
115 |
116 | # Tell browsers whether to use the native HTML5 validations (novalidate form option).
117 | # These validations are enabled in SimpleForm's internal config but disabled by default
118 | # in this configuration, which is recommended due to some quirks from different browsers.
119 | # To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations,
120 | # change this configuration to true.
121 | config.browser_validations = false
122 |
123 | # Collection of methods to detect if a file type was given.
124 | # config.file_methods = [ :mounted_as, :file?, :public_filename ]
125 |
126 | # Custom mappings for input types. This should be a hash containing a regexp
127 | # to match as key, and the input type that will be used when the field name
128 | # matches the regexp as value.
129 | # config.input_mappings = { /count/ => :integer }
130 |
131 | # Custom wrappers for input types. This should be a hash containing an input
132 | # type as key and the wrapper that will be used for all inputs with specified type.
133 | # config.wrapper_mappings = { string: :prepend }
134 |
135 | # Namespaces where SimpleForm should look for custom input classes that
136 | # override default inputs.
137 | # config.custom_inputs_namespaces << "CustomInputs"
138 |
139 | # Default priority for time_zone inputs.
140 | # config.time_zone_priority = nil
141 |
142 | # Default priority for country inputs.
143 | # config.country_priority = nil
144 |
145 | # When false, do not use translations for labels.
146 | # config.translate_labels = true
147 |
148 | # Automatically discover new inputs in Rails' autoload path.
149 | # config.inputs_discovery = true
150 |
151 | # Cache SimpleForm inputs discovery
152 | # config.cache_discovery = !Rails.env.development?
153 |
154 | # Default class for inputs
155 | # config.input_class = nil
156 |
157 | # Define the default class of the input wrapper of the boolean input.
158 | config.boolean_label_class = 'checkbox'
159 |
160 | # Defines if the default input wrapper class should be included in radio
161 | # collection wrappers.
162 | # config.include_default_input_wrapper_class = true
163 |
164 | # Defines which i18n scope will be used in Simple Form.
165 | # config.i18n_scope = 'simple_form'
166 | end
167 |
--------------------------------------------------------------------------------
/lib/templates/rspec/scaffold/controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | <% module_namespacing do -%>
4 | describe <%= controller_class_name %>Controller do
5 |
6 | # This should return the minimal set of attributes required to create a valid
7 | # <%= class_name %>. As you add validations to <%= class_name %>, be sure to
8 | # update the return value of this method accordingly.
9 | def valid_attributes
10 | attributes_for :<%= file_name %>
11 | end
12 |
13 | # This should return the minimal set of values that should be in the session
14 | # in order to pass any filters (e.g. authentication) defined in
15 | # <%= controller_class_name %>Controller. Be sure to keep this updated too.
16 | def valid_session
17 | {}
18 | end
19 |
20 | before do
21 | # TODO Sign a user in for these specs.
22 | # login_user build :admin
23 | end
24 |
25 | <% unless options[:singleton] -%>
26 | describe "#index" do
27 | it "assigns all <%= table_name.pluralize %> as @<%= table_name.pluralize %>" do
28 | <%= file_name %> = <%= class_name %>.create! valid_attributes
29 | get :index, {}, valid_session
30 | expect(assigns(:<%= table_name %>)).to eq([<%= file_name %>])
31 | end
32 | end
33 |
34 | <% end -%>
35 | describe "#show" do
36 | it "assigns the requested <%= ns_file_name %> as @<%= ns_file_name %>" do
37 | <%= file_name %> = <%= class_name %>.create! valid_attributes
38 | get :show, { :id => <%= file_name %>.to_param }, valid_session
39 | expect(assigns(:<%= ns_file_name %>)).to eq(<%= file_name %>)
40 | end
41 | end
42 |
43 | describe "#new" do
44 | it "assigns a new <%= ns_file_name %> as @<%= ns_file_name %>" do
45 | get :new, {}, valid_session
46 | expect(assigns(:<%= ns_file_name %>)).to be_a_new(<%= class_name %>)
47 | end
48 | end
49 |
50 | describe "#edit" do
51 | it "assigns the requested <%= ns_file_name %> as @<%= ns_file_name %>" do
52 | <%= file_name %> = <%= class_name %>.create! valid_attributes
53 | get :edit, { id: <%= file_name %>.to_param }, valid_session
54 | expect(assigns(:<%= ns_file_name %>)).to eq(<%= file_name %>)
55 | end
56 | end
57 |
58 | describe "#create" do
59 | describe "with valid params" do
60 | it "creates a new <%= class_name %>" do
61 | expect {
62 | post :create, { <%= ns_file_name %>: valid_attributes }, valid_session
63 | }.to change(<%= class_name %>, :count).by(1)
64 | end
65 |
66 | it "assigns a newly created <%= ns_file_name %> as @<%= ns_file_name %>" do
67 | post :create, { <%= ns_file_name %>: valid_attributes }, valid_session
68 | expect(assigns(:<%= ns_file_name %>)).to be_a(<%= class_name %>)
69 | expect(assigns(:<%= ns_file_name %>)).to be_persisted
70 | end
71 |
72 | it "redirects to the created <%= ns_file_name %>" do
73 | post :create, { <%= ns_file_name %>: valid_attributes }, valid_session
74 | expect(response).to redirect_to(<%= class_name %>.last)
75 | end
76 | end
77 |
78 | describe "with invalid params" do
79 | it "assigns a newly created but unsaved <%= ns_file_name %> as @<%= ns_file_name %>" do
80 | # Trigger the behavior that occurs when invalid params are submitted
81 | allow_any_instance_of(<%= class_name %>).to receive(:save).and_return(false)
82 | post :create, { <%= ns_file_name %>: <%= formatted_hash(example_invalid_attributes) %> }, valid_session
83 | expect(assigns(:<%= ns_file_name %>)).to be_a_new(<%= class_name %>)
84 | end
85 |
86 | it "re-renders the 'new' template" do
87 | # Trigger the behavior that occurs when invalid params are submitted
88 | allow_any_instance_of(<%= class_name %>).to receive(:save).and_return(false)
89 | post :create, { <%= ns_file_name %>: <%= formatted_hash(example_invalid_attributes) %> }, valid_session
90 | expect(response).to render_template('new')
91 | end
92 | end
93 | end
94 |
95 | describe "#update" do
96 | describe "with valid params" do
97 | it "updates the requested <%= ns_file_name %>" do
98 | <%= file_name %> = <%= class_name %>.create! valid_attributes
99 | # Assuming there are no other <%= table_name %> in the database, this
100 | # specifies that the <%= class_name %> created on the previous line
101 | # receives the :update message with whatever params are submitted in
102 | # the request.
103 | expect_any_instance_of(<%= class_name %>).to receive(:update).with(<%= formatted_hash(example_params_for_update) %>)
104 | put :update, { id: <%= file_name %>.to_param, <%= ns_file_name %>: <%= formatted_hash(example_params_for_update) %> }, valid_session
105 | end
106 |
107 | it "assigns the requested <%= ns_file_name %> as @<%= ns_file_name %>" do
108 | <%= file_name %> = <%= class_name %>.create! valid_attributes
109 | put :update, { id: <%= file_name %>.to_param, <%= ns_file_name %>: valid_attributes }, valid_session
110 | expect(assigns(:<%= ns_file_name %>)).to eq(<%= file_name %>)
111 | end
112 |
113 | it "redirects to the <%= ns_file_name %>" do
114 | <%= file_name %> = <%= class_name %>.create! valid_attributes
115 | put :update, { id: <%= file_name %>.to_param, <%= ns_file_name %>: valid_attributes }, valid_session
116 | expect(response).to redirect_to(<%= file_name %>)
117 | end
118 | end
119 |
120 | describe "with invalid params" do
121 | it "assigns the <%= ns_file_name %> as @<%= ns_file_name %>" do
122 | <%= file_name %> = <%= class_name %>.create! valid_attributes
123 | # Trigger the behavior that occurs when invalid params are submitted
124 | allow_any_instance_of(<%= class_name %>).to receive(:save).and_return(false)
125 | put :update, { id: <%= file_name %>.to_param, <%= ns_file_name %>: <%= formatted_hash(example_invalid_attributes) %> }, valid_session
126 | expect(assigns(:<%= ns_file_name %>)).to eq(<%= file_name %>)
127 | end
128 |
129 | it "re-renders the 'edit' template" do
130 | <%= file_name %> = <%= class_name %>.create! valid_attributes
131 | # Trigger the behavior that occurs when invalid params are submitted
132 | allow_any_instance_of(<%= class_name %>).to receive(:save).and_return(false)
133 | put :update, { id: <%= file_name %>.to_param, <%= ns_file_name %>: <%= formatted_hash(example_invalid_attributes) %> }, valid_session
134 | expect(response).to render_template('edit')
135 | end
136 | end
137 | end
138 |
139 | describe "#destroy" do
140 | it "destroys the requested <%= ns_file_name %>" do
141 | <%= file_name %> = <%= class_name %>.create! valid_attributes
142 | expect {
143 | delete :destroy, { id: <%= file_name %>.to_param }, valid_session
144 | }.to change(<%= class_name %>, :count).by(-1)
145 | end
146 |
147 | it "redirects to the <%= table_name %> list" do
148 | <%= file_name %> = <%= class_name %>.create! valid_attributes
149 | delete :destroy, { id: <%= file_name %>.to_param }, valid_session
150 | expect(response).to redirect_to(<%= index_helper %>_url)
151 | end
152 | end
153 |
154 | end
155 | <% end -%>
156 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/bradphelan/jasminerice.git
3 | revision: 091349c27343bfa728b8e5745675f9ddbb7d9699
4 | specs:
5 | jasminerice (0.1.0)
6 | coffee-rails
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | actionmailer (4.1.5)
12 | actionpack (= 4.1.5)
13 | actionview (= 4.1.5)
14 | mail (~> 2.5.4)
15 | actionpack (4.1.5)
16 | actionview (= 4.1.5)
17 | activesupport (= 4.1.5)
18 | rack (~> 1.5.2)
19 | rack-test (~> 0.6.2)
20 | actionview (4.1.5)
21 | activesupport (= 4.1.5)
22 | builder (~> 3.1)
23 | erubis (~> 2.7.0)
24 | activemodel (4.1.5)
25 | activesupport (= 4.1.5)
26 | builder (~> 3.1)
27 | activerecord (4.1.5)
28 | activemodel (= 4.1.5)
29 | activesupport (= 4.1.5)
30 | arel (~> 5.0.0)
31 | activesupport (4.1.5)
32 | i18n (~> 0.6, >= 0.6.9)
33 | json (~> 1.7, >= 1.7.7)
34 | minitest (~> 5.1)
35 | thread_safe (~> 0.1)
36 | tzinfo (~> 1.1)
37 | addressable (2.3.6)
38 | arel (5.0.1.20140414130214)
39 | awesome_print (1.2.0)
40 | better_errors (1.1.0)
41 | coderay (>= 1.0.0)
42 | erubis (>= 2.6.6)
43 | binding_of_caller (0.7.2)
44 | debug_inspector (>= 0.0.1)
45 | bootstrap-sass (3.2.0.1)
46 | sass (~> 3.2)
47 | builder (3.2.2)
48 | capybara (2.4.1)
49 | mime-types (>= 1.16)
50 | nokogiri (>= 1.3.3)
51 | rack (>= 1.0.0)
52 | rack-test (>= 0.5.4)
53 | xpath (~> 2.0)
54 | celluloid (0.15.2)
55 | timers (~> 1.1.0)
56 | childprocess (0.5.3)
57 | ffi (~> 1.0, >= 1.0.11)
58 | cliver (0.3.2)
59 | coderay (1.1.0)
60 | coffee-rails (4.0.1)
61 | coffee-script (>= 2.2.0)
62 | railties (>= 4.0.0, < 5.0)
63 | coffee-script (2.3.0)
64 | coffee-script-source
65 | execjs
66 | coffee-script-source (1.7.1)
67 | database_cleaner (1.3.0)
68 | debug_inspector (0.0.2)
69 | diff-lcs (1.2.5)
70 | docile (1.1.5)
71 | dotenv (0.11.1)
72 | dotenv-deployment (~> 0.0.2)
73 | dotenv-deployment (0.0.2)
74 | em-websocket (0.5.1)
75 | eventmachine (>= 0.12.9)
76 | http_parser.rb (~> 0.6.0)
77 | erubis (2.7.0)
78 | eventmachine (1.0.3)
79 | execjs (2.2.1)
80 | factory_girl (4.4.0)
81 | activesupport (>= 3.0.0)
82 | factory_girl_rails (4.4.1)
83 | factory_girl (~> 4.4.0)
84 | railties (>= 3.0.0)
85 | ffi (1.9.3)
86 | foreman (0.74.0)
87 | dotenv (~> 0.11.1)
88 | thor (~> 0.19.1)
89 | formatador (0.2.5)
90 | fuubar (2.0.0)
91 | rspec (~> 3.0)
92 | ruby-progressbar (~> 1.4)
93 | growl (1.0.3)
94 | guard (2.6.1)
95 | formatador (>= 0.2.4)
96 | listen (~> 2.7)
97 | lumberjack (~> 1.0)
98 | pry (>= 0.9.12)
99 | thor (>= 0.18.1)
100 | guard-jasmine (1.19.2)
101 | childprocess
102 | guard (>= 2.0.0)
103 | multi_json
104 | thor
105 | tilt
106 | guard-livereload (2.3.0)
107 | em-websocket (~> 0.5)
108 | guard (~> 2.0)
109 | multi_json (~> 1.8)
110 | guard-rspec (4.3.1)
111 | guard (~> 2.1)
112 | rspec (>= 2.14, < 4.0)
113 | heroku_rails_deflate (1.0.3)
114 | actionpack (>= 3.2.13)
115 | activesupport (>= 3.2.13)
116 | rack (>= 1.4.5)
117 | hike (1.2.3)
118 | http_parser.rb (0.6.0)
119 | httpclient (2.4.0)
120 | i18n (0.6.11)
121 | jquery-rails (3.1.1)
122 | railties (>= 3.0, < 5.0)
123 | thor (>= 0.14, < 2.0)
124 | json (1.8.1)
125 | kgio (2.9.2)
126 | launchy (2.4.2)
127 | addressable (~> 2.3)
128 | listen (2.7.9)
129 | celluloid (>= 0.15.2)
130 | rb-fsevent (>= 0.9.3)
131 | rb-inotify (>= 0.9)
132 | lumberjack (1.0.9)
133 | mail (2.5.4)
134 | mime-types (~> 1.16)
135 | treetop (~> 1.4.8)
136 | method_source (0.8.2)
137 | mime-types (1.25.1)
138 | mini_portile (0.6.0)
139 | minitest (5.4.0)
140 | multi_json (1.10.1)
141 | nokogiri (1.6.3.1)
142 | mini_portile (= 0.6.0)
143 | pg (0.17.1)
144 | poltergeist (1.5.1)
145 | capybara (~> 2.1)
146 | cliver (~> 0.3.1)
147 | multi_json (~> 1.0)
148 | websocket-driver (>= 0.2.0)
149 | polyglot (0.3.5)
150 | pry (0.10.1)
151 | coderay (~> 1.1.0)
152 | method_source (~> 0.8.1)
153 | slop (~> 3.4)
154 | pusher (0.14.1)
155 | httpclient (~> 2.3)
156 | multi_json (~> 1.0)
157 | signature (~> 0.1.6)
158 | quiet_assets (1.0.3)
159 | railties (>= 3.1, < 5.0)
160 | rack (1.5.2)
161 | rack-canonical-host (0.1.0)
162 | addressable
163 | rack (~> 1.0)
164 | rack-test (0.6.2)
165 | rack (>= 1.0)
166 | rails (4.1.5)
167 | actionmailer (= 4.1.5)
168 | actionpack (= 4.1.5)
169 | actionview (= 4.1.5)
170 | activemodel (= 4.1.5)
171 | activerecord (= 4.1.5)
172 | activesupport (= 4.1.5)
173 | bundler (>= 1.3.0, < 2.0)
174 | railties (= 4.1.5)
175 | sprockets-rails (~> 2.0)
176 | rails_stdout_logging (0.0.3)
177 | railties (4.1.5)
178 | actionpack (= 4.1.5)
179 | activesupport (= 4.1.5)
180 | rake (>= 0.8.7)
181 | thor (>= 0.18.1, < 2.0)
182 | raindrops (0.13.0)
183 | rake (10.3.2)
184 | rb-fsevent (0.9.4)
185 | rb-inotify (0.9.5)
186 | ffi (>= 0.5.0)
187 | rspec (3.0.0)
188 | rspec-core (~> 3.0.0)
189 | rspec-expectations (~> 3.0.0)
190 | rspec-mocks (~> 3.0.0)
191 | rspec-core (3.0.4)
192 | rspec-support (~> 3.0.0)
193 | rspec-expectations (3.0.4)
194 | diff-lcs (>= 1.2.0, < 2.0)
195 | rspec-support (~> 3.0.0)
196 | rspec-mocks (3.0.4)
197 | rspec-support (~> 3.0.0)
198 | rspec-rails (3.0.2)
199 | actionpack (>= 3.0)
200 | activesupport (>= 3.0)
201 | railties (>= 3.0)
202 | rspec-core (~> 3.0.0)
203 | rspec-expectations (~> 3.0.0)
204 | rspec-mocks (~> 3.0.0)
205 | rspec-support (~> 3.0.0)
206 | rspec-support (3.0.4)
207 | ruby-progressbar (1.5.1)
208 | sass (3.2.19)
209 | sass-rails (4.0.3)
210 | railties (>= 4.0.0, < 5.0)
211 | sass (~> 3.2.0)
212 | sprockets (~> 2.8, <= 2.11.0)
213 | sprockets-rails (~> 2.0)
214 | signature (0.1.7)
215 | simple_form (3.1.0.rc2)
216 | actionpack (~> 4.0)
217 | activemodel (~> 4.0)
218 | simplecov (0.9.0)
219 | docile (~> 1.1.0)
220 | multi_json
221 | simplecov-html (~> 0.8.0)
222 | simplecov-html (0.8.0)
223 | slim (2.0.3)
224 | temple (~> 0.6.6)
225 | tilt (>= 1.3.3, < 2.1)
226 | slim-rails (2.1.5)
227 | actionpack (>= 3.0, < 4.2)
228 | activesupport (>= 3.0, < 4.2)
229 | railties (>= 3.0, < 4.2)
230 | slim (~> 2.0)
231 | slop (3.6.0)
232 | spring (1.1.3)
233 | spring-commands-rspec (1.0.2)
234 | spring (>= 0.9.1)
235 | sprockets (2.11.0)
236 | hike (~> 1.2)
237 | multi_json (~> 1.0)
238 | rack (~> 1.0)
239 | tilt (~> 1.1, != 1.3.0)
240 | sprockets-rails (2.1.3)
241 | actionpack (>= 3.0)
242 | activesupport (>= 3.0)
243 | sprockets (~> 2.8)
244 | temple (0.6.8)
245 | thor (0.19.1)
246 | thread_safe (0.3.4)
247 | tilt (1.4.1)
248 | timers (1.1.0)
249 | treetop (1.4.15)
250 | polyglot
251 | polyglot (>= 0.3.1)
252 | turbolinks (2.2.2)
253 | coffee-rails
254 | tzinfo (1.2.2)
255 | thread_safe (~> 0.1)
256 | uglifier (2.5.3)
257 | execjs (>= 0.3.0)
258 | json (>= 1.8.0)
259 | unicorn (4.8.3)
260 | kgio (~> 2.6)
261 | rack
262 | raindrops (~> 0.7)
263 | websocket-driver (0.3.4)
264 | xpath (2.0.0)
265 | nokogiri (~> 1.3)
266 |
267 | PLATFORMS
268 | ruby
269 |
270 | DEPENDENCIES
271 | awesome_print
272 | better_errors
273 | binding_of_caller
274 | bootstrap-sass
275 | capybara
276 | coffee-rails
277 | database_cleaner
278 | factory_girl_rails
279 | foreman
280 | fuubar
281 | growl
282 | guard (~> 2)
283 | guard-jasmine
284 | guard-livereload
285 | guard-rspec
286 | heroku_rails_deflate
287 | jasminerice!
288 | jquery-rails
289 | launchy
290 | pg
291 | poltergeist
292 | pusher
293 | quiet_assets
294 | rack-canonical-host
295 | rails (~> 4.1.5)
296 | rails_stdout_logging
297 | rb-fsevent
298 | rspec-rails
299 | sass-rails
300 | simple_form (~> 3.1.0.rc2)
301 | simplecov
302 | slim-rails
303 | spring
304 | spring-commands-rspec
305 | turbolinks
306 | uglifier
307 | unicorn
308 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/hark.js:
--------------------------------------------------------------------------------
1 | (function(e){if("function"==typeof bootstrap)bootstrap("hark",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeHark=e}else"undefined"!=typeof window?window.hark=e():global.hark=e()})(function(){var define,ses,bootstrap,module,exports;
2 | return (function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s maxVolume && fftBins[i] < 0) {
11 | maxVolume = fftBins[i];
12 | }
13 | };
14 |
15 | return maxVolume;
16 | }
17 |
18 |
19 | var audioContextType = window.webkitAudioContext || window.AudioContext;
20 | // use a single audio context due to hardware limits
21 | var audioContext = null;
22 | module.exports = function(stream, options) {
23 | var harker = new WildEmitter();
24 |
25 |
26 | // make it not break in non-supported browsers
27 | if (!audioContextType) return harker;
28 |
29 | //Config
30 | var options = options || {},
31 | smoothing = (options.smoothing || 0.1),
32 | interval = (options.interval || 50),
33 | threshold = options.threshold,
34 | play = options.play,
35 | history = options.history || 10,
36 | running = true;
37 |
38 | //Setup Audio Context
39 | if (!audioContext) {
40 | audioContext = new audioContextType();
41 | }
42 | var sourceNode, fftBins, analyser;
43 |
44 | analyser = audioContext.createAnalyser();
45 | analyser.fftSize = 512;
46 | analyser.smoothingTimeConstant = smoothing;
47 | fftBins = new Float32Array(analyser.fftSize);
48 |
49 | if (stream.jquery) stream = stream[0];
50 | if (stream instanceof HTMLAudioElement || stream instanceof HTMLVideoElement) {
51 | //Audio Tag
52 | sourceNode = audioContext.createMediaElementSource(stream);
53 | if (typeof play === 'undefined') play = true;
54 | threshold = threshold || -50;
55 | } else {
56 | //WebRTC Stream
57 | sourceNode = audioContext.createMediaStreamSource(stream);
58 | threshold = threshold || -50;
59 | }
60 |
61 | sourceNode.connect(analyser);
62 | if (play) analyser.connect(audioContext.destination);
63 |
64 | harker.speaking = false;
65 |
66 | harker.setThreshold = function(t) {
67 | threshold = t;
68 | };
69 |
70 | harker.setInterval = function(i) {
71 | interval = i;
72 | };
73 |
74 | harker.stop = function() {
75 | running = false;
76 | harker.emit('volume_change', -100, threshold);
77 | if (harker.speaking) {
78 | harker.speaking = false;
79 | harker.emit('stopped_speaking');
80 | }
81 | };
82 | harker.speakingHistory = [];
83 | for (var i = 0; i < history; i++) {
84 | harker.speakingHistory.push(0);
85 | }
86 |
87 | // Poll the analyser node to determine if speaking
88 | // and emit events if changed
89 | var looper = function() {
90 | setTimeout(function() {
91 |
92 | //check if stop has been called
93 | if(!running) {
94 | return;
95 | }
96 |
97 | var currentVolume = getMaxVolume(analyser, fftBins);
98 |
99 | harker.emit('volume_change', currentVolume, threshold);
100 |
101 | var history = 0;
102 | if (currentVolume > threshold && !harker.speaking) {
103 | // trigger quickly, short history
104 | for (var i = harker.speakingHistory.length - 3; i < harker.speakingHistory.length; i++) {
105 | history += harker.speakingHistory[i];
106 | }
107 | if (history >= 2) {
108 | harker.speaking = true;
109 | harker.emit('speaking');
110 | }
111 | } else if (currentVolume < threshold && harker.speaking) {
112 | for (var i = 0; i < harker.speakingHistory.length; i++) {
113 | history += harker.speakingHistory[i];
114 | }
115 | if (history == 0) {
116 | harker.speaking = false;
117 | harker.emit('stopped_speaking');
118 | }
119 | }
120 | harker.speakingHistory.shift();
121 | harker.speakingHistory.push(0 + (currentVolume > threshold));
122 |
123 | looper();
124 | }, interval);
125 | };
126 | looper();
127 |
128 |
129 | return harker;
130 | }
131 |
132 | },{"wildemitter":2}],2:[function(require,module,exports){
133 | /*
134 | WildEmitter.js is a slim little event emitter by @henrikjoreteg largely based
135 | on @visionmedia's Emitter from UI Kit.
136 |
137 | Why? I wanted it standalone.
138 |
139 | I also wanted support for wildcard emitters like this:
140 |
141 | emitter.on('*', function (eventName, other, event, payloads) {
142 |
143 | });
144 |
145 | emitter.on('somenamespace*', function (eventName, payloads) {
146 |
147 | });
148 |
149 | Please note that callbacks triggered by wildcard registered events also get
150 | the event name as the first argument.
151 | */
152 | module.exports = WildEmitter;
153 |
154 | function WildEmitter() {
155 | this.callbacks = {};
156 | }
157 |
158 | // Listen on the given `event` with `fn`. Store a group name if present.
159 | WildEmitter.prototype.on = function (event, groupName, fn) {
160 | var hasGroup = (arguments.length === 3),
161 | group = hasGroup ? arguments[1] : undefined,
162 | func = hasGroup ? arguments[2] : arguments[1];
163 | func._groupName = group;
164 | (this.callbacks[event] = this.callbacks[event] || []).push(func);
165 | return this;
166 | };
167 |
168 | // Adds an `event` listener that will be invoked a single
169 | // time then automatically removed.
170 | WildEmitter.prototype.once = function (event, groupName, fn) {
171 | var self = this,
172 | hasGroup = (arguments.length === 3),
173 | group = hasGroup ? arguments[1] : undefined,
174 | func = hasGroup ? arguments[2] : arguments[1];
175 | function on() {
176 | self.off(event, on);
177 | func.apply(this, arguments);
178 | }
179 | this.on(event, group, on);
180 | return this;
181 | };
182 |
183 | // Unbinds an entire group
184 | WildEmitter.prototype.releaseGroup = function (groupName) {
185 | var item, i, len, handlers;
186 | for (item in this.callbacks) {
187 | handlers = this.callbacks[item];
188 | for (i = 0, len = handlers.length; i < len; i++) {
189 | if (handlers[i]._groupName === groupName) {
190 | //console.log('removing');
191 | // remove it and shorten the array we're looping through
192 | handlers.splice(i, 1);
193 | i--;
194 | len--;
195 | }
196 | }
197 | }
198 | return this;
199 | };
200 |
201 | // Remove the given callback for `event` or all
202 | // registered callbacks.
203 | WildEmitter.prototype.off = function (event, fn) {
204 | var callbacks = this.callbacks[event],
205 | i;
206 |
207 | if (!callbacks) return this;
208 |
209 | // remove all handlers
210 | if (arguments.length === 1) {
211 | delete this.callbacks[event];
212 | return this;
213 | }
214 |
215 | // remove specific handler
216 | i = callbacks.indexOf(fn);
217 | callbacks.splice(i, 1);
218 | return this;
219 | };
220 |
221 | /// Emit `event` with the given args.
222 | // also calls any `*` handlers
223 | WildEmitter.prototype.emit = function (event) {
224 | var args = [].slice.call(arguments, 1),
225 | callbacks = this.callbacks[event],
226 | specialCallbacks = this.getWildcardCallbacks(event),
227 | i,
228 | len,
229 | item,
230 | listeners;
231 |
232 | if (callbacks) {
233 | listeners = callbacks.slice();
234 | for (i = 0, len = listeners.length; i < len; ++i) {
235 | if (listeners[i]) {
236 | listeners[i].apply(this, args);
237 | } else {
238 | break;
239 | }
240 | }
241 | }
242 |
243 | if (specialCallbacks) {
244 | len = specialCallbacks.length;
245 | listeners = specialCallbacks.slice();
246 | for (i = 0, len = listeners.length; i < len; ++i) {
247 | if (listeners[i]) {
248 | listeners[i].apply(this, [event].concat(args));
249 | } else {
250 | break;
251 | }
252 | }
253 | }
254 |
255 | return this;
256 | };
257 |
258 | // Helper for for finding special wildcard event handlers that match the event
259 | WildEmitter.prototype.getWildcardCallbacks = function (eventName) {
260 | var item,
261 | split,
262 | result = [];
263 |
264 | for (item in this.callbacks) {
265 | split = item.split('*');
266 | if (item === '*' || (split.length === 2 && eventName.slice(0, split[0].length) === split[0])) {
267 | result = result.concat(this.callbacks[item]);
268 | }
269 | }
270 | return result;
271 | };
272 |
273 | },{}]},{},[1])(1)
274 | });
275 | ;
--------------------------------------------------------------------------------
/vendor/assets/javascripts/simplepeer.js:
--------------------------------------------------------------------------------
1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;"undefined"!=typeof window?t=window:"undefined"!=typeof global?t=global:"undefined"!=typeof self&&(t=self),t.SimplePeer=e()}}(function(){return function e(t,n,r){function i(s,a){if(!n[s]){if(!t[s]){var u="function"==typeof require&&require;if(!a&&u)return u(s,!0);if(o)return o(s,!0);var c=new Error("Cannot find module '"+s+"'");throw c.code="MODULE_NOT_FOUND",c}var h=n[s]={exports:{}};t[s][0].call(h.exports,function(e){var n=t[s][1][e];return i(n?n:e)},h,h.exports,e,t,n,r)}return n[s].exports}for(var o="function"==typeof require&&require,s=0;s0?e>>>0:0;else if("string"===o)"base64"===t&&(e=A(e)),i=r.byteLength(e,t);else{if("object"!==o||null===e)throw new Error("First argument needs to be a number, array or string.");"Buffer"===e.type&&B(e.data)&&(e=e.data),i=+e.length>0?Math.floor(+e.length):0}var s;H?s=r._augment(new Uint8Array(i)):(s=this,s.length=i,s._isBuffer=!0);var a;if(H&&"number"==typeof e.byteLength)s._set(e);else if(x(e))if(r.isBuffer(e))for(a=0;i>a;a++)s[a]=e.readUInt8(a);else for(a=0;i>a;a++)s[a]=(e[a]%256+256)%256;else if("string"===o)s.write(e,0,t);else if("number"===o&&!H&&!n)for(a=0;i>a;a++)s[a]=0;return s}function i(e,t,n,r){n=Number(n)||0;var i=e.length-n;r?(r=Number(r),r>i&&(r=i)):r=i;var o=t.length;z(o%2===0,"Invalid hex string"),r>o/2&&(r=o/2);for(var s=0;r>s;s++){var a=parseInt(t.substr(2*s,2),16);z(!isNaN(a),"Invalid hex string"),e[n+s]=a}return s}function o(e,t,n,r){var i=N(R(t),e,n,r);return i}function s(e,t,n,r){var i=N(T(t),e,n,r);return i}function a(e,t,n,r){return s(e,t,n,r)}function u(e,t,n,r){var i=N(D(t),e,n,r);return i}function c(e,t,n,r){var i=N(U(t),e,n,r);return i}function h(e,t,n){return J.fromByteArray(0===t&&n===e.length?e:e.slice(t,n))}function f(e,t,n){var r="",i="";n=Math.min(e.length,n);for(var o=t;n>o;o++)e[o]<=127?(r+=O(i)+String.fromCharCode(e[o]),i=""):i+="%"+e[o].toString(16);return r+O(i)}function l(e,t,n){var r="";n=Math.min(e.length,n);for(var i=t;n>i;i++)r+=String.fromCharCode(e[i]);return r}function d(e,t,n){return l(e,t,n)}function p(e,t,n){var r=e.length;(!t||0>t)&&(t=0),(!n||0>n||n>r)&&(n=r);for(var i="",o=t;n>o;o++)i+=j(e[o]);return i}function g(e,t,n){for(var r=e.slice(t,n),i="",o=0;o=i)){var o;return n?(o=e[t],i>t+1&&(o|=e[t+1]<<8)):(o=e[t]<<8,i>t+1&&(o|=e[t+1])),o}}function v(e,t,n,r){r||(z("boolean"==typeof n,"missing or invalid endian"),z(void 0!==t&&null!==t,"missing offset"),z(t+3=i)){var o;return n?(i>t+2&&(o=e[t+2]<<16),i>t+1&&(o|=e[t+1]<<8),o|=e[t],i>t+3&&(o+=e[t+3]<<24>>>0)):(i>t+1&&(o=e[t+1]<<16),i>t+2&&(o|=e[t+2]<<8),i>t+3&&(o|=e[t+3]),o+=e[t]<<24>>>0),o}}function b(e,t,n,r){r||(z("boolean"==typeof n,"missing or invalid endian"),z(void 0!==t&&null!==t,"missing offset"),z(t+1=i)){var o=m(e,t,n,!0),s=32768&o;return s?-1*(65535-o+1):o}}function y(e,t,n,r){r||(z("boolean"==typeof n,"missing or invalid endian"),z(void 0!==t&&null!==t,"missing offset"),z(t+3=i)){var o=v(e,t,n,!0),s=2147483648&o;return s?-1*(4294967295-o+1):o}}function w(e,t,n,r){return r||(z("boolean"==typeof n,"missing or invalid endian"),z(t+3=o)){for(var s=0,a=Math.min(o-n,2);a>s;s++)e[n+s]=(t&255<<8*(r?s:1-s))>>>8*(r?s:1-s);return n+2}}function S(e,t,n,r,i){i||(z(void 0!==t&&null!==t,"missing value"),z("boolean"==typeof r,"missing or invalid endian"),z(void 0!==n&&null!==n,"missing offset"),z(n+3=o)){for(var s=0,a=Math.min(o-n,4);a>s;s++)e[n+s]=t>>>8*(r?s:3-s)&255;return n+4}}function L(e,t,n,r,i){i||(z(void 0!==t&&null!==t,"missing value"),z("boolean"==typeof r,"missing or invalid endian"),z(void 0!==n&&null!==n,"missing offset"),z(n+1=o))return t>=0?E(e,t,n,r,i):E(e,65535+t+1,n,r,i),n+2}function C(e,t,n,r,i){i||(z(void 0!==t&&null!==t,"missing value"),z("boolean"==typeof r,"missing or invalid endian"),z(void 0!==n&&null!==n,"missing offset"),z(n+3=o))return t>=0?S(e,t,n,r,i):S(e,4294967295+t+1,n,r,i),n+4}function k(e,t,n,r,i){i||(z(void 0!==t&&null!==t,"missing value"),z("boolean"==typeof r,"missing or invalid endian"),z(void 0!==n&&null!==n,"missing offset"),z(n+3=o))return q.write(e,t,n,r,23,4),n+4}function I(e,t,n,r,i){i||(z(void 0!==t&&null!==t,"missing value"),z("boolean"==typeof r,"missing or invalid endian"),z(void 0!==n&&null!==n,"missing offset"),z(n+7=o))return q.write(e,t,n,r,52,8),n+8}function A(e){for(e=M(e).replace(V,"");e.length%4!==0;)e+="=";return e}function M(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}function B(e){return(Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)})(e)}function x(e){return B(e)||r.isBuffer(e)||e&&"object"==typeof e&&"number"==typeof e.length}function j(e){return 16>e?"0"+e.toString(16):e.toString(16)}function R(e){for(var t=[],n=0;n=r)t.push(r);else{var i=n;r>=55296&&57343>=r&&n++;for(var o=encodeURIComponent(e.slice(i,n+1)).substr(1).split("%"),s=0;s>8,r=t%256,i.push(r),i.push(n);return i}function D(e){return J.toByteArray(e)}function N(e,t,n,r){for(var i=0;r>i&&!(i+n>=t.length||i>=e.length);i++)t[i+n]=e[i];return i}function O(e){try{return decodeURIComponent(e)}catch(t){return String.fromCharCode(65533)}}function F(e,t){z("number"==typeof e,"cannot write a non-number as a number"),z(e>=0,"specified a negative value for writing an unsigned value"),z(t>=e,"value is larger than maximum value for type"),z(Math.floor(e)===e,"value has a fractional component")}function W(e,t,n){z("number"==typeof e,"cannot write a non-number as a number"),z(t>=e,"value larger than maximum allowed value"),z(e>=n,"value smaller than minimum allowed value"),z(Math.floor(e)===e,"value has a fractional component")}function P(e,t,n){z("number"==typeof e,"cannot write a non-number as a number"),z(t>=e,"value larger than maximum allowed value"),z(e>=n,"value smaller than minimum allowed value")}function z(e,t){if(!e)throw new Error(t||"Failed assertion")}var J=e("base64-js"),q=e("ieee754");n.Buffer=r,n.SlowBuffer=r,n.INSPECT_MAX_BYTES=50,r.poolSize=8192;var H=function(){try{var e=new ArrayBuffer(0),t=new Uint8Array(e);return t.foo=function(){return 42},42===t.foo()&&"function"==typeof t.subarray&&0===new Uint8Array(1).subarray(1,1).byteLength}catch(n){return!1}}();r.isEncoding=function(e){switch(String(e).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"raw":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},r.isBuffer=function(e){return!(null==e||!e._isBuffer)},r.byteLength=function(e,t){var n;switch(e=e.toString(),t||"utf8"){case"hex":n=e.length/2;break;case"utf8":case"utf-8":n=R(e).length;break;case"ascii":case"binary":case"raw":n=e.length;break;case"base64":n=D(e).length;break;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":n=2*e.length;break;default:throw new Error("Unknown encoding")}return n},r.concat=function(e,t){if(z(B(e),"Usage: Buffer.concat(list[, length])"),0===e.length)return new r(0);if(1===e.length)return e[0];var n;if(void 0===t)for(t=0,n=0;no&&e[o]===t[o];o++);return o!==s&&(n=e[o],i=t[o]),i>n?-1:n>i?1:0},r.prototype.write=function(e,t,n,r){if(isFinite(t))isFinite(n)||(r=n,n=void 0);else{var h=r;r=t,t=n,n=h}t=Number(t)||0;var f=this.length-t;n?(n=Number(n),n>f&&(n=f)):n=f,r=String(r||"utf8").toLowerCase();var l;switch(r){case"hex":l=i(this,e,t,n);break;case"utf8":case"utf-8":l=o(this,e,t,n);break;case"ascii":l=s(this,e,t,n);break;case"binary":l=a(this,e,t,n);break;case"base64":l=u(this,e,t,n);break;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":l=c(this,e,t,n);break;default:throw new Error("Unknown encoding")}return l},r.prototype.toString=function(e,t,n){var r=this;if(e=String(e||"utf8").toLowerCase(),t=Number(t)||0,n=void 0===n?r.length:Number(n),n===t)return"";var i;switch(e){case"hex":i=p(r,t,n);break;case"utf8":case"utf-8":i=f(r,t,n);break;case"ascii":i=l(r,t,n);break;case"binary":i=d(r,t,n);break;case"base64":i=h(r,t,n);break;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":i=g(r,t,n);break;default:throw new Error("Unknown encoding")}return i},r.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}},r.prototype.equals=function(e){return z(r.isBuffer(e),"Argument must be a Buffer"),0===r.compare(this,e)},r.prototype.compare=function(e){return z(r.isBuffer(e),"Argument must be a Buffer"),r.compare(this,e)},r.prototype.copy=function(e,t,n,r){var i=this;if(n||(n=0),r||0===r||(r=this.length),t||(t=0),r!==n&&0!==e.length&&0!==i.length){z(r>=n,"sourceEnd < sourceStart"),z(t>=0&&t=0&&n=0&&r<=i.length,"sourceEnd out of bounds"),r>this.length&&(r=this.length),e.length-to||!H)for(var s=0;o>s;s++)e[s+t]=this[s+n];else e._set(this.subarray(n,n+o),t)}},r.prototype.slice=function(e,t){var n=this.length;if(e=~~e,t=void 0===t?n:~~t,0>e?(e+=n,0>e&&(e=0)):e>n&&(e=n),0>t?(t+=n,0>t&&(t=0)):t>n&&(t=n),e>t&&(t=e),H)return r._augment(this.subarray(e,t));for(var i=t-e,o=new r(i,void 0,!0),s=0;i>s;s++)o[s]=this[s+e];return o},r.prototype.get=function(e){return console.log(".get() is deprecated. Access using array indexes instead."),this.readUInt8(e)},r.prototype.set=function(e,t){return console.log(".set() is deprecated. Access using array indexes instead."),this.writeUInt8(e,t)},r.prototype.readUInt8=function(e,t){return t||(z(void 0!==e&&null!==e,"missing offset"),z(e=this.length?void 0:this[e]},r.prototype.readUInt16LE=function(e,t){return m(this,e,!0,t)},r.prototype.readUInt16BE=function(e,t){return m(this,e,!1,t)},r.prototype.readUInt32LE=function(e,t){return v(this,e,!0,t)},r.prototype.readUInt32BE=function(e,t){return v(this,e,!1,t)},r.prototype.readInt8=function(e,t){if(t||(z(void 0!==e&&null!==e,"missing offset"),z(e=this.length)){var n=128&this[e];return n?-1*(255-this[e]+1):this[e]}},r.prototype.readInt16LE=function(e,t){return b(this,e,!0,t)},r.prototype.readInt16BE=function(e,t){return b(this,e,!1,t)},r.prototype.readInt32LE=function(e,t){return y(this,e,!0,t)},r.prototype.readInt32BE=function(e,t){return y(this,e,!1,t)},r.prototype.readFloatLE=function(e,t){return w(this,e,!0,t)},r.prototype.readFloatBE=function(e,t){return w(this,e,!1,t)},r.prototype.readDoubleLE=function(e,t){return _(this,e,!0,t)},r.prototype.readDoubleBE=function(e,t){return _(this,e,!1,t)},r.prototype.writeUInt8=function(e,t,n){return n||(z(void 0!==e&&null!==e,"missing value"),z(void 0!==t&&null!==t,"missing offset"),z(t=this.length?void 0:(this[t]=e,t+1)},r.prototype.writeUInt16LE=function(e,t,n){return E(this,e,t,!0,n)},r.prototype.writeUInt16BE=function(e,t,n){return E(this,e,t,!1,n)},r.prototype.writeUInt32LE=function(e,t,n){return S(this,e,t,!0,n)},r.prototype.writeUInt32BE=function(e,t,n){return S(this,e,t,!1,n)},r.prototype.writeInt8=function(e,t,n){return n||(z(void 0!==e&&null!==e,"missing value"),z(void 0!==t&&null!==t,"missing offset"),z(t=this.length?void 0:(e>=0?this.writeUInt8(e,t,n):this.writeUInt8(255+e+1,t,n),t+1)},r.prototype.writeInt16LE=function(e,t,n){return L(this,e,t,!0,n)},r.prototype.writeInt16BE=function(e,t,n){return L(this,e,t,!1,n)},r.prototype.writeInt32LE=function(e,t,n){return C(this,e,t,!0,n)},r.prototype.writeInt32BE=function(e,t,n){return C(this,e,t,!1,n)},r.prototype.writeFloatLE=function(e,t,n){return k(this,e,t,!0,n)},r.prototype.writeFloatBE=function(e,t,n){return k(this,e,t,!1,n)},r.prototype.writeDoubleLE=function(e,t,n){return I(this,e,t,!0,n)},r.prototype.writeDoubleBE=function(e,t,n){return I(this,e,t,!1,n)},r.prototype.fill=function(e,t,n){if(e||(e=0),t||(t=0),n||(n=this.length),z(n>=t,"end < start"),n!==t&&0!==this.length){z(t>=0&&t=0&&n<=this.length,"end out of bounds");var r;if("number"==typeof e)for(r=t;n>r;r++)this[r]=e;else{var i=R(e.toString()),o=i.length;for(r=t;n>r;r++)this[r]=i[r%o]}return this}},r.prototype.inspect=function(){for(var e=[],t=this.length,r=0;t>r;r++)if(e[r]=j(this[r]),r===n.INSPECT_MAX_BYTES){e[r+1]="...";break}return""},r.prototype.toArrayBuffer=function(){if("undefined"!=typeof Uint8Array){if(H)return new r(this).buffer;for(var e=new Uint8Array(this.length),t=0,n=e.length;n>t;t+=1)e[t]=this[t];return e.buffer}throw new Error("Buffer.toArrayBuffer not supported in this browser")};var $=r.prototype;r._augment=function(e){return e._isBuffer=!0,e._get=e.get,e._set=e.set,e.get=$.get,e.set=$.set,e.write=$.write,e.toString=$.toString,e.toLocaleString=$.toString,e.toJSON=$.toJSON,e.equals=$.equals,e.compare=$.compare,e.copy=$.copy,e.slice=$.slice,e.readUInt8=$.readUInt8,e.readUInt16LE=$.readUInt16LE,e.readUInt16BE=$.readUInt16BE,e.readUInt32LE=$.readUInt32LE,e.readUInt32BE=$.readUInt32BE,e.readInt8=$.readInt8,e.readInt16LE=$.readInt16LE,e.readInt16BE=$.readInt16BE,e.readInt32LE=$.readInt32LE,e.readInt32BE=$.readInt32BE,e.readFloatLE=$.readFloatLE,e.readFloatBE=$.readFloatBE,e.readDoubleLE=$.readDoubleLE,e.readDoubleBE=$.readDoubleBE,e.writeUInt8=$.writeUInt8,e.writeUInt16LE=$.writeUInt16LE,e.writeUInt16BE=$.writeUInt16BE,e.writeUInt32LE=$.writeUInt32LE,e.writeUInt32BE=$.writeUInt32BE,e.writeInt8=$.writeInt8,e.writeInt16LE=$.writeInt16LE,e.writeInt16BE=$.writeInt16BE,e.writeInt32LE=$.writeInt32LE,e.writeInt32BE=$.writeInt32BE,e.writeFloatLE=$.writeFloatLE,e.writeFloatBE=$.writeFloatBE,e.writeDoubleLE=$.writeDoubleLE,e.writeDoubleBE=$.writeDoubleBE,e.fill=$.fill,e.inspect=$.inspect,e.toArrayBuffer=$.toArrayBuffer,e};var V=/[^+\/0-9A-z]/g},{"base64-js":2,ieee754:3}],2:[function(e,t,n){var r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";!function(e){"use strict";function t(e){var t=e.charCodeAt(0);return t===s?62:t===a?63:u>t?-1:u+10>t?t-u+26+26:h+26>t?t-h:c+26>t?t-c+26:void 0}function n(e){function n(e){c[f++]=e}var r,i,s,a,u,c;if(e.length%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var h=e.length;u="="===e.charAt(h-2)?2:"="===e.charAt(h-1)?1:0,c=new o(3*e.length/4-u),s=u>0?e.length-4:e.length;var f=0;for(r=0,i=0;s>r;r+=4,i+=3)a=t(e.charAt(r))<<18|t(e.charAt(r+1))<<12|t(e.charAt(r+2))<<6|t(e.charAt(r+3)),n((16711680&a)>>16),n((65280&a)>>8),n(255&a);return 2===u?(a=t(e.charAt(r))<<2|t(e.charAt(r+1))>>4,n(255&a)):1===u&&(a=t(e.charAt(r))<<10|t(e.charAt(r+1))<<4|t(e.charAt(r+2))>>2,n(a>>8&255),n(255&a)),c}function i(e){function t(e){return r.charAt(e)}function n(e){return t(e>>18&63)+t(e>>12&63)+t(e>>6&63)+t(63&e)}var i,o,s,a=e.length%3,u="";for(i=0,s=e.length-a;s>i;i+=3)o=(e[i]<<16)+(e[i+1]<<8)+e[i+2],u+=n(o);switch(a){case 1:o=e[e.length-1],u+=t(o>>2),u+=t(o<<4&63),u+="==";break;case 2:o=(e[e.length-2]<<8)+e[e.length-1],u+=t(o>>10),u+=t(o>>4&63),u+=t(o<<2&63),u+="="}return u}var o="undefined"!=typeof Uint8Array?Uint8Array:Array,s="+".charCodeAt(0),a="/".charCodeAt(0),u="0".charCodeAt(0),c="a".charCodeAt(0),h="A".charCodeAt(0);e.toByteArray=n,e.fromByteArray=i}("undefined"==typeof n?this.base64js={}:n)},{}],3:[function(e,t,n){n.read=function(e,t,n,r,i){var o,s,a=8*i-r-1,u=(1<>1,h=-7,f=n?i-1:0,l=n?-1:1,d=e[t+f];for(f+=l,o=d&(1<<-h)-1,d>>=-h,h+=a;h>0;o=256*o+e[t+f],f+=l,h-=8);for(s=o&(1<<-h)-1,o>>=-h,h+=r;h>0;s=256*s+e[t+f],f+=l,h-=8);if(0===o)o=1-c;else{if(o===u)return s?0/0:1/0*(d?-1:1);s+=Math.pow(2,r),o-=c}return(d?-1:1)*s*Math.pow(2,o-r)},n.write=function(e,t,n,r,i,o){var s,a,u,c=8*o-i-1,h=(1<>1,l=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,d=r?0:o-1,p=r?1:-1,g=0>t||0===t&&0>1/t?1:0;for(t=Math.abs(t),isNaN(t)||1/0===t?(a=isNaN(t)?1:0,s=h):(s=Math.floor(Math.log(t)/Math.LN2),t*(u=Math.pow(2,-s))<1&&(s--,u*=2),t+=s+f>=1?l/u:l*Math.pow(2,1-f),t*u>=2&&(s++,u/=2),s+f>=h?(a=0,s=h):s+f>=1?(a=(t*u-1)*Math.pow(2,i),s+=f):(a=t*Math.pow(2,f-1)*Math.pow(2,i),s=0));i>=8;e[n+d]=255&a,d+=p,a/=256,i-=8);for(s=s<0;e[n+d]=255&s,d+=p,s/=256,c-=8);e[n+d-p]|=128*g}},{}],4:[function(e,t){function n(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function r(e){return"function"==typeof e}function i(e){return"number"==typeof e}function o(e){return"object"==typeof e&&null!==e}function s(e){return void 0===e}t.exports=n,n.EventEmitter=n,n.prototype._events=void 0,n.prototype._maxListeners=void 0,n.defaultMaxListeners=10,n.prototype.setMaxListeners=function(e){if(!i(e)||0>e||isNaN(e))throw TypeError("n must be a positive number");return this._maxListeners=e,this},n.prototype.emit=function(e){var t,n,i,a,u,c;if(this._events||(this._events={}),"error"===e&&(!this._events.error||o(this._events.error)&&!this._events.error.length))throw t=arguments[1],t instanceof Error?t:TypeError('Uncaught, unspecified "error" event.');if(n=this._events[e],s(n))return!1;if(r(n))switch(arguments.length){case 1:n.call(this);break;case 2:n.call(this,arguments[1]);break;case 3:n.call(this,arguments[1],arguments[2]);break;default:for(i=arguments.length,a=new Array(i-1),u=1;i>u;u++)a[u-1]=arguments[u];n.apply(this,a)}else if(o(n)){for(i=arguments.length,a=new Array(i-1),u=1;i>u;u++)a[u-1]=arguments[u];for(c=n.slice(),i=c.length,u=0;i>u;u++)c[u].apply(this,a)}return!0},n.prototype.addListener=function(e,t){var i;if(!r(t))throw TypeError("listener must be a function");if(this._events||(this._events={}),this._events.newListener&&this.emit("newListener",e,r(t.listener)?t.listener:t),this._events[e]?o(this._events[e])?this._events[e].push(t):this._events[e]=[this._events[e],t]:this._events[e]=t,o(this._events[e])&&!this._events[e].warned){var i;i=s(this._maxListeners)?n.defaultMaxListeners:this._maxListeners,i&&i>0&&this._events[e].length>i&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace())}return this},n.prototype.on=n.prototype.addListener,n.prototype.once=function(e,t){function n(){this.removeListener(e,n),i||(i=!0,t.apply(this,arguments))}if(!r(t))throw TypeError("listener must be a function");var i=!1;return n.listener=t,this.on(e,n),this},n.prototype.removeListener=function(e,t){var n,i,s,a;if(!r(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(n=this._events[e],s=n.length,i=-1,n===t||r(n.listener)&&n.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(o(n)){for(a=s;a-->0;)if(n[a]===t||n[a].listener&&n[a].listener===t){i=a;break}if(0>i)return this;1===n.length?(n.length=0,delete this._events[e]):n.splice(i,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},n.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(n=this._events[e],r(n))this.removeListener(e,n);else for(;n.length;)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},n.prototype.listeners=function(e){var t;return t=this._events&&this._events[e]?r(this._events[e])?[this._events[e]]:this._events[e].slice():[]},n.listenerCount=function(e,t){var n;return n=e._events&&e._events[t]?r(e._events[t])?1:e._events[t].length:0}},{}],5:[function(e,t){t.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},{}],6:[function(e,t){function n(){}var r=t.exports={};r.nextTick=function(){var e="undefined"!=typeof window&&window.setImmediate,t="undefined"!=typeof window&&window.postMessage&&window.addEventListener;if(e)return function(e){return window.setImmediate(e)};if(t){var n=[];return window.addEventListener("message",function(e){var t=e.source;if((t===window||null===t)&&"process-tick"===e.data&&(e.stopPropagation(),n.length>0)){var r=n.shift();r()}},!0),function(e){n.push(e),window.postMessage("process-tick","*")}}return function(e){setTimeout(e,0)}}(),r.title="browser",r.browser=!0,r.env={},r.argv=[],r.on=n,r.addListener=n,r.once=n,r.off=n,r.removeListener=n,r.removeAllListeners=n,r.emit=n,r.binding=function(){throw new Error("process.binding is not supported")},r.cwd=function(){return"/"},r.chdir=function(){throw new Error("process.chdir is not supported")}},{}],7:[function(e,t){t.exports=e("./lib/_stream_duplex.js")},{"./lib/_stream_duplex.js":8}],8:[function(e,t){(function(n){function r(e){return this instanceof r?(u.call(this,e),c.call(this,e),e&&e.readable===!1&&(this.readable=!1),e&&e.writable===!1&&(this.writable=!1),this.allowHalfOpen=!0,e&&e.allowHalfOpen===!1&&(this.allowHalfOpen=!1),void this.once("end",i)):new r(e)}function i(){this.allowHalfOpen||this._writableState.ended||n.nextTick(this.end.bind(this))}function o(e,t){for(var n=0,r=e.length;r>n;n++)t(e[n],n)}t.exports=r;var s=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t},a=e("core-util-is");a.inherits=e("inherits");var u=e("./_stream_readable"),c=e("./_stream_writable");a.inherits(r,u),o(s(c.prototype),function(e){r.prototype[e]||(r.prototype[e]=c.prototype[e])})}).call(this,e("_process"))},{"./_stream_readable":10,"./_stream_writable":12,_process:6,"core-util-is":13,inherits:25}],9:[function(e,t){function n(e){return this instanceof n?void r.call(this,e):new n(e)}t.exports=n;var r=e("./_stream_transform"),i=e("core-util-is");i.inherits=e("inherits"),i.inherits(n,r),n.prototype._transform=function(e,t,n){n(null,e)}},{"./_stream_transform":11,"core-util-is":13,inherits:25}],10:[function(e,t){(function(n){function r(t){t=t||{};var n=t.highWaterMark;this.highWaterMark=n||0===n?n:16384,this.highWaterMark=~~this.highWaterMark,this.buffer=[],this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=!1,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.calledRead=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.objectMode=!!t.objectMode,this.defaultEncoding=t.defaultEncoding||"utf8",this.ranOut=!1,this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,t.encoding&&(A||(A=e("string_decoder/").StringDecoder),this.decoder=new A(t.encoding),this.encoding=t.encoding)}function i(e){return this instanceof i?(this._readableState=new r(e,this),this.readable=!0,void k.call(this)):new i(e)}function o(e,t,n,r,i){var o=c(t,n);if(o)e.emit("error",o);else if(null===n||void 0===n)t.reading=!1,t.ended||h(e,t);else if(t.objectMode||n&&n.length>0)if(t.ended&&!i){var a=new Error("stream.push() after EOF");e.emit("error",a)}else if(t.endEmitted&&i){var a=new Error("stream.unshift() after end event");e.emit("error",a)}else!t.decoder||i||r||(n=t.decoder.write(n)),t.length+=t.objectMode?1:n.length,i?t.buffer.unshift(n):(t.reading=!1,t.buffer.push(n)),t.needReadable&&f(e),d(e,t);else i||(t.reading=!1);return s(t)}function s(e){return!e.ended&&(e.needReadable||e.length=M)e=M;else{e--;for(var t=1;32>t;t<<=1)e|=e>>t;e++}return e}function u(e,t){return 0===t.length&&t.ended?0:t.objectMode?0===e?0:1:isNaN(e)||null===e?t.flowing&&t.buffer.length?t.buffer[0].length:t.length:0>=e?0:(e>t.highWaterMark&&(t.highWaterMark=a(e)),e>t.length?t.ended?t.length:(t.needReadable=!0,0):e)}function c(e,t){var n=null;return L.isBuffer(t)||"string"==typeof t||null===t||void 0===t||e.objectMode||n||(n=new TypeError("Invalid non-string/buffer chunk")),n}function h(e,t){if(t.decoder&&!t.ended){var n=t.decoder.end();n&&n.length&&(t.buffer.push(n),t.length+=t.objectMode?1:n.length)}t.ended=!0,t.length>0?f(e):w(e)}function f(e){var t=e._readableState;t.needReadable=!1,t.emittedReadable||(t.emittedReadable=!0,t.sync?n.nextTick(function(){l(e)
2 | }):l(e))}function l(e){e.emit("readable")}function d(e,t){t.readingMore||(t.readingMore=!0,n.nextTick(function(){p(e,t)}))}function p(e,t){for(var n=t.length;!t.reading&&!t.flowing&&!t.ended&&t.length0)return;return 0===r.pipesCount?(r.flowing=!1,void(C.listenerCount(e,"data")>0&&b(e))):void(r.ranOut=!0)}function v(){this._readableState.ranOut&&(this._readableState.ranOut=!1,m(this))}function b(e,t){var r=e._readableState;if(r.flowing)throw new Error("Cannot switch to old mode now.");var i=t||!1,o=!1;e.readable=!0,e.pipe=k.prototype.pipe,e.on=e.addListener=k.prototype.on,e.on("readable",function(){o=!0;for(var t;!i&&null!==(t=e.read());)e.emit("data",t);null===t&&(o=!1,e._readableState.needReadable=!0)}),e.pause=function(){i=!0,this.emit("pause")},e.resume=function(){i=!1,o?n.nextTick(function(){e.emit("readable")}):this.read(0),this.emit("resume")},e.emit("readable")}function y(e,t){var n,r=t.buffer,i=t.length,o=!!t.decoder,s=!!t.objectMode;if(0===r.length)return null;if(0===i)n=null;else if(s)n=r.shift();else if(!e||e>=i)n=o?r.join(""):L.concat(r,i),r.length=0;else if(ec&&e>u;c++){var a=r[0],f=Math.min(e-u,a.length);o?n+=a.slice(0,f):a.copy(n,u,0,f),f0)throw new Error("endReadable called on non-empty stream");!t.endEmitted&&t.calledRead&&(t.ended=!0,n.nextTick(function(){t.endEmitted||0!==t.length||(t.endEmitted=!0,e.readable=!1,e.emit("end"))}))}function _(e,t){for(var n=0,r=e.length;r>n;n++)t(e[n],n)}function E(e,t){for(var n=0,r=e.length;r>n;n++)if(e[n]===t)return n;return-1}t.exports=i;var S=e("isarray"),L=e("buffer").Buffer;i.ReadableState=r;var C=e("events").EventEmitter;C.listenerCount||(C.listenerCount=function(e,t){return e.listeners(t).length});var k=e("stream"),I=e("core-util-is");I.inherits=e("inherits");var A;I.inherits(i,k),i.prototype.push=function(e,t){var n=this._readableState;return"string"!=typeof e||n.objectMode||(t=t||n.defaultEncoding,t!==n.encoding&&(e=new L(e,t),t="")),o(this,n,e,t,!1)},i.prototype.unshift=function(e){var t=this._readableState;return o(this,t,e,"",!0)},i.prototype.setEncoding=function(t){A||(A=e("string_decoder/").StringDecoder),this._readableState.decoder=new A(t),this._readableState.encoding=t};var M=8388608;i.prototype.read=function(e){var t=this._readableState;t.calledRead=!0;var n=e;if(("number"!=typeof e||e>0)&&(t.emittedReadable=!1),0===e&&t.needReadable&&(t.length>=t.highWaterMark||t.ended))return f(this),null;if(e=u(e,t),0===e&&t.ended)return 0===t.length&&w(this),null;var r=t.needReadable;t.length-e<=t.highWaterMark&&(r=!0),(t.ended||t.reading)&&(r=!1),r&&(t.reading=!0,t.sync=!0,0===t.length&&(t.needReadable=!0),this._read(t.highWaterMark),t.sync=!1),r&&!t.reading&&(e=u(n,t));var i;return i=e>0?y(e,t):null,null===i&&(t.needReadable=!0,e=0),t.length-=e,0!==t.length||t.ended||(t.needReadable=!0),t.ended&&!t.endEmitted&&0===t.length&&w(this),i},i.prototype._read=function(){this.emit("error",new Error("not implemented"))},i.prototype.pipe=function(e,t){function r(e){e===h&&o()}function i(){e.end()}function o(){e.removeListener("close",a),e.removeListener("finish",u),e.removeListener("drain",p),e.removeListener("error",s),e.removeListener("unpipe",r),h.removeListener("end",i),h.removeListener("end",o),(!e._writableState||e._writableState.needDrain)&&p()}function s(t){c(),e.removeListener("error",s),0===C.listenerCount(e,"error")&&e.emit("error",t)}function a(){e.removeListener("finish",u),c()}function u(){e.removeListener("close",a),c()}function c(){h.unpipe(e)}var h=this,f=this._readableState;switch(f.pipesCount){case 0:f.pipes=e;break;case 1:f.pipes=[f.pipes,e];break;default:f.pipes.push(e)}f.pipesCount+=1;var l=(!t||t.end!==!1)&&e!==n.stdout&&e!==n.stderr,d=l?i:o;f.endEmitted?n.nextTick(d):h.once("end",d),e.on("unpipe",r);var p=g(h);return e.on("drain",p),e._events&&e._events.error?S(e._events.error)?e._events.error.unshift(s):e._events.error=[s,e._events.error]:e.on("error",s),e.once("close",a),e.once("finish",u),e.emit("pipe",h),f.flowing||(this.on("readable",v),f.flowing=!0,n.nextTick(function(){m(h)})),e},i.prototype.unpipe=function(e){var t=this._readableState;if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes?this:(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,this.removeListener("readable",v),t.flowing=!1,e&&e.emit("unpipe",this),this);if(!e){var n=t.pipes,r=t.pipesCount;t.pipes=null,t.pipesCount=0,this.removeListener("readable",v),t.flowing=!1;for(var i=0;r>i;i++)n[i].emit("unpipe",this);return this}var i=E(t.pipes,e);return-1===i?this:(t.pipes.splice(i,1),t.pipesCount-=1,1===t.pipesCount&&(t.pipes=t.pipes[0]),e.emit("unpipe",this),this)},i.prototype.on=function(e,t){var n=k.prototype.on.call(this,e,t);if("data"!==e||this._readableState.flowing||b(this),"readable"===e&&this.readable){var r=this._readableState;r.readableListening||(r.readableListening=!0,r.emittedReadable=!1,r.needReadable=!0,r.reading?r.length&&f(this,r):this.read(0))}return n},i.prototype.addListener=i.prototype.on,i.prototype.resume=function(){b(this),this.read(0),this.emit("resume")},i.prototype.pause=function(){b(this,!0),this.emit("pause")},i.prototype.wrap=function(e){var t=this._readableState,n=!1,r=this;e.on("end",function(){if(t.decoder&&!t.ended){var e=t.decoder.end();e&&e.length&&r.push(e)}r.push(null)}),e.on("data",function(i){if(t.decoder&&(i=t.decoder.write(i)),i&&(t.objectMode||i.length)){var o=r.push(i);o||(n=!0,e.pause())}});for(var i in e)"function"==typeof e[i]&&"undefined"==typeof this[i]&&(this[i]=function(t){return function(){return e[t].apply(e,arguments)}}(i));var o=["error","close","destroy","pause","resume"];return _(o,function(t){e.on(t,r.emit.bind(r,t))}),r._read=function(){n&&(n=!1,e.resume())},r},i._fromList=y}).call(this,e("_process"))},{_process:6,buffer:1,"core-util-is":13,events:4,inherits:25,isarray:5,stream:19,"string_decoder/":14}],11:[function(e,t){function n(e,t){this.afterTransform=function(e,n){return r(t,e,n)},this.needTransform=!1,this.transforming=!1,this.writecb=null,this.writechunk=null}function r(e,t,n){var r=e._transformState;r.transforming=!1;var i=r.writecb;if(!i)return e.emit("error",new Error("no writecb in Transform class"));r.writechunk=null,r.writecb=null,null!==n&&void 0!==n&&e.push(n),i&&i(t);var o=e._readableState;o.reading=!1,(o.needReadable||o.length=this.charLength-this.charReceived?this.charLength-this.charReceived:e.length;if(e.copy(this.charBuffer,this.charReceived,n,r),this.charReceived+=r-n,n=r,this.charReceived=55296&&56319>=i)){if(this.charReceived=this.charLength=0,r==e.length)return t;e=e.slice(r,e.length);break}this.charLength+=this.surrogateSize,t=""}var o=this.detectIncompleteChar(e),s=e.length;this.charLength&&(e.copy(this.charBuffer,0,e.length-o,s),this.charReceived=o,s-=o),t+=e.toString(this.encoding,0,s);var s=t.length-1,i=t.charCodeAt(s);if(i>=55296&&56319>=i){var a=this.surrogateSize;return this.charLength+=a,this.charReceived+=a,this.charBuffer.copy(this.charBuffer,a,0,a),this.charBuffer.write(t.charAt(t.length-1),this.encoding),t.substring(0,s)}return t},c.prototype.detectIncompleteChar=function(e){for(var t=e.length>=3?3:e.length;t>0;t--){var n=e[e.length-t];if(1==t&&n>>5==6){this.charLength=2;break}if(2>=t&&n>>4==14){this.charLength=3;break}if(3>=t&&n>>3==30){this.charLength=4;break}}return t},c.prototype.end=function(e){var t="";if(e&&e.length&&(t=this.write(e)),this.charReceived){var n=this.charReceived,r=this.charBuffer,i=this.encoding;t+=r.slice(0,n).toString(i)}return t}},{buffer:1}],15:[function(e,t){t.exports=e("./lib/_stream_passthrough.js")},{"./lib/_stream_passthrough.js":9}],16:[function(e,t,n){n=t.exports=e("./lib/_stream_readable.js"),n.Readable=n,n.Writable=e("./lib/_stream_writable.js"),n.Duplex=e("./lib/_stream_duplex.js"),n.Transform=e("./lib/_stream_transform.js"),n.PassThrough=e("./lib/_stream_passthrough.js")},{"./lib/_stream_duplex.js":8,"./lib/_stream_passthrough.js":9,"./lib/_stream_readable.js":10,"./lib/_stream_transform.js":11,"./lib/_stream_writable.js":12}],17:[function(e,t){t.exports=e("./lib/_stream_transform.js")},{"./lib/_stream_transform.js":11}],18:[function(e,t){t.exports=e("./lib/_stream_writable.js")},{"./lib/_stream_writable.js":12}],19:[function(e,t){function n(){r.call(this)}t.exports=n;var r=e("events").EventEmitter,i=e("inherits");i(n,r),n.Readable=e("readable-stream/readable.js"),n.Writable=e("readable-stream/writable.js"),n.Duplex=e("readable-stream/duplex.js"),n.Transform=e("readable-stream/transform.js"),n.PassThrough=e("readable-stream/passthrough.js"),n.Stream=n,n.prototype.pipe=function(e,t){function n(t){e.writable&&!1===e.write(t)&&c.pause&&c.pause()}function i(){c.readable&&c.resume&&c.resume()}function o(){h||(h=!0,e.end())}function s(){h||(h=!0,"function"==typeof e.destroy&&e.destroy())}function a(e){if(u(),0===r.listenerCount(this,"error"))throw e}function u(){c.removeListener("data",n),e.removeListener("drain",i),c.removeListener("end",o),c.removeListener("close",s),c.removeListener("error",a),e.removeListener("error",a),c.removeListener("end",u),c.removeListener("close",u),e.removeListener("close",u)}var c=this;c.on("data",n),e.on("drain",i),e._isStdio||t&&t.end===!1||(c.on("end",o),c.on("close",s));var h=!1;return c.on("error",a),e.on("error",a),c.on("end",u),c.on("close",u),e.on("close",u),e.emit("pipe",c),e}},{events:4,inherits:25,"readable-stream/duplex.js":7,"readable-stream/passthrough.js":15,"readable-stream/readable.js":16,"readable-stream/transform.js":17,"readable-stream/writable.js":18}],20:[function(e,t,n){function r(){return"WebkitAppearance"in document.documentElement.style||window.console&&(console.firebug||console.exception&&console.table)||navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31}function i(){var e=arguments,t=this.useColors;if(e[0]=(t?"%c":"")+this.namespace+(t?" %c":" ")+e[0]+(t?"%c ":" ")+"+"+n.humanize(this.diff),!t)return e;var r="color: "+this.color;e=[e[0],r,"color: inherit"].concat(Array.prototype.slice.call(e,1));var i=0,o=0;return e[0].replace(/%[a-z%]/g,function(e){"%%"!==e&&(i++,"%c"===e&&(o=i))}),e.splice(o,0,r),e}function o(){return"object"==typeof console&&"function"==typeof console.log&&Function.prototype.apply.call(console.log,console,arguments)}function s(e){try{null==e?localStorage.removeItem("debug"):localStorage.debug=e}catch(t){}}function a(){var e;try{e=localStorage.debug}catch(t){}return e}n=t.exports=e("./debug"),n.log=o,n.formatArgs=i,n.save=s,n.load=a,n.useColors=r,n.colors=["lightseagreen","forestgreen","goldenrod","dodgerblue","darkorchid","crimson"],n.formatters.j=function(e){return JSON.stringify(e)},n.enable(a())},{"./debug":21}],21:[function(e,t,n){function r(){return n.colors[h++%n.colors.length]}function i(e){function t(){}function i(){var e=i,t=+new Date,o=t-(c||t);e.diff=o,e.prev=c,e.curr=t,c=t,null==e.useColors&&(e.useColors=n.useColors()),null==e.color&&e.useColors&&(e.color=r());var s=Array.prototype.slice.call(arguments);s[0]=n.coerce(s[0]),"string"!=typeof s[0]&&(s=["%o"].concat(s));var a=0;s[0]=s[0].replace(/%([a-z%])/g,function(t,r){if("%%"===t)return t;a++;var i=n.formatters[r];if("function"==typeof i){var o=s[a];t=i.call(e,o),s.splice(a,1),a--}return t}),"function"==typeof n.formatArgs&&(s=n.formatArgs.apply(e,s));var u=i.log||n.log||console.log.bind(console);u.apply(e,s)}t.enabled=!1,i.enabled=!0;var o=n.enabled(e)?i:t;return o.namespace=e,o}function o(e){n.save(e);for(var t=(e||"").split(/[\s,]+/),r=t.length,i=0;r>i;i++)t[i]&&(e=t[i].replace(/\*/g,".*?"),"-"===e[0]?n.skips.push(new RegExp("^"+e.substr(1)+"$")):n.names.push(new RegExp("^"+e+"$")))}function s(){n.enable("")}function a(e){var t,r;for(t=0,r=n.skips.length;r>t;t++)if(n.skips[t].test(e))return!1;for(t=0,r=n.names.length;r>t;t++)if(n.names[t].test(e))return!0;return!1}function u(e){return e instanceof Error?e.stack||e.message:e}n=t.exports=i,n.coerce=u,n.disable=s,n.enable=o,n.enabled=a,n.humanize=e("ms"),n.names=[],n.skips=[],n.formatters={};var c,h=0},{ms:22}],22:[function(e,t){function n(e){var t=/^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(e);if(t){var n=parseFloat(t[1]),r=(t[2]||"ms").toLowerCase();switch(r){case"years":case"year":case"y":return n*h;case"days":case"day":case"d":return n*c;case"hours":case"hour":case"h":return n*u;case"minutes":case"minute":case"m":return n*a;case"seconds":case"second":case"s":return n*s;case"ms":return n}}}function r(e){return e>=c?Math.round(e/c)+"d":e>=u?Math.round(e/u)+"h":e>=a?Math.round(e/a)+"m":e>=s?Math.round(e/s)+"s":e+"ms"}function i(e){return o(e,c,"day")||o(e,u,"hour")||o(e,a,"minute")||o(e,s,"second")||e+" ms"}function o(e,t,n){return t>e?void 0:1.5*t>e?Math.floor(e/t)+" "+n:Math.ceil(e/t)+" "+n+"s"}var s=1e3,a=60*s,u=60*a,c=24*u,h=365.25*c;t.exports=function(e,t){return t=t||{},"string"==typeof e?n(e):t.long?i(e):r(e)}},{}],23:[function(e,t){t.exports=function(e){for(var t,n=[].slice.call(arguments,1),r=0,i=n.length;i>r;r++){t=n[r];for(var o in t)e[o]=t[o]}return e}},{}],24:[function(e,t){var n=t.exports=function(e,t){if(t||(t=16),void 0===e&&(e=128),0>=e)return"0";for(var r=Math.log(Math.pow(2,e))/Math.log(t),i=2;1/0===r;i*=2)r=Math.log(Math.pow(2,e/i))/Math.log(t)*i;for(var o=r-Math.floor(r),s="",i=0;i=Math.pow(2,e)?n(e,t):s};n.rack=function(e,t,r){var i=function(i){var s=0;do{if(s++>10){if(!r)throw new Error("too many ID collisions, use more bits");e+=r}var a=n(e,t)}while(Object.hasOwnProperty.call(o,a));return o[a]=i,a},o=i.hats={};return i.get=function(e){return i.hats[e]},i.set=function(e,t){return i.hats[e]=t,i},i.bits=e||128,i.base=t||16,i}},{}],25:[function(e,t){t.exports="function"==typeof Object.create?function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}:function(e,t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}},{}],26:[function(e,t){function n(e){return r(e)||i(e)}function r(e){return e instanceof Int8Array||e instanceof Int16Array||e instanceof Int32Array||e instanceof Uint8Array||e instanceof Uint16Array||e instanceof Uint32Array||e instanceof Float32Array||e instanceof Float64Array}function i(e){return s[o.call(e)]}t.exports=n,n.strict=r,n.loose=i;var o=Object.prototype.toString,s={"[object Int8Array]":!0,"[object Int16Array]":!0,"[object Int32Array]":!0,"[object Uint8Array]":!0,"[object Uint16Array]":!0,"[object Uint32Array]":!0,"[object Float32Array]":!0,"[object Float64Array]":!0}},{}],27:[function(e,t){function n(e){var t=function(){return t.called?t.value:(t.called=!0,t.value=e.apply(this,arguments))};return t.called=!1,t}t.exports=n,n.proto=n(function(){Object.defineProperty(Function.prototype,"once",{value:function(){return n(this)},configurable:!0})})},{}],28:[function(e,t){(function(e){t.exports=function(t){return"function"==typeof e._augment&&e._useTypedArrays?e._augment(t):new e(t)}}).call(this,e("buffer").Buffer)},{buffer:1}]},{},[])("./")});
--------------------------------------------------------------------------------