merge develop
This commit is contained in:
commit
ae79912dab
|
|
@ -0,0 +1,32 @@
|
|||
ActiveAdmin.register JamRuby::LatencyTester, :as => 'LatencyTester' do
|
||||
|
||||
config.filters = true
|
||||
config.per_page = 50
|
||||
config.clear_action_items!
|
||||
config.sort_order = "client_id"
|
||||
menu :parent => 'Operations'
|
||||
|
||||
controller do
|
||||
def scoped_collection
|
||||
@latency_testers ||= end_of_association_chain
|
||||
.order('client_id')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
index :as => :block do |latency_tester|
|
||||
div :for => latency_tester do
|
||||
h3 "#{latency_tester.client_id}"
|
||||
columns do
|
||||
column do
|
||||
panel 'Details' do
|
||||
attributes_table_for(latency_tester) do
|
||||
row :connection do |latency_tester| latency_tester.connection ? "last updated at: #{latency_tester.connection.updated_at}" : "no connection" end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -28,6 +28,17 @@ FactoryGirl.define do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
factory :connection, :class => JamRuby::Connection do
|
||||
sequence(:client_id) { |n| "Client#{n}" }
|
||||
ip_address "1.1.1.1"
|
||||
as_musician true
|
||||
addr 0
|
||||
locidispid 0
|
||||
client_type 'client'
|
||||
association :user, factory: :user
|
||||
end
|
||||
|
||||
factory :artifact_update, :class => JamRuby::ArtifactUpdate do
|
||||
sequence(:version) { |n| "0.1.#{n}" }
|
||||
uri { "http://somewhere/jkclient.msi" }
|
||||
|
|
@ -173,4 +184,18 @@ FactoryGirl.define do
|
|||
end
|
||||
end
|
||||
|
||||
factory :latency_tester, :class => JamRuby::LatencyTester do
|
||||
ignore do
|
||||
connection nil
|
||||
make_connection true
|
||||
end
|
||||
|
||||
sequence(:client_id) { |n| "LatencyTesterClientId-#{n}" }
|
||||
|
||||
after(:create) do |latency_tester, evaluator|
|
||||
latency_tester.connection = evaluator.connection if evaluator.connection
|
||||
latency_tester.connection = FactoryGirl.create(:connection, client_type: Connection::TYPE_LATENCY_TESTER, client_id: latency_tester.client_id) if evaluator.make_connection
|
||||
latency_tester.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Feeds' do
|
||||
|
||||
subject { page }
|
||||
|
||||
before(:each) do
|
||||
end
|
||||
|
||||
|
||||
describe "latency_tester with connection" do
|
||||
let!(:latency_tester) {FactoryGirl.create(:latency_tester)}
|
||||
|
||||
before(:each) do
|
||||
visit admin_latency_testers_path
|
||||
end
|
||||
|
||||
it "shows connection info" do
|
||||
should have_selector('td', text: "last updated at: #{latency_tester.connection.updated_at}")
|
||||
end
|
||||
end
|
||||
|
||||
describe "latency_tester with no connection" do
|
||||
let!(:latency_tester) {FactoryGirl.create(:latency_tester, client_id: 'abc', make_connection: false)}
|
||||
|
||||
before(:each) do
|
||||
visit admin_latency_testers_path
|
||||
end
|
||||
|
||||
it "shows no connection" do
|
||||
should have_selector('td', text: "no connection")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
@ -168,3 +168,4 @@ update_get_work_for_larger_radius.sql
|
|||
periodic_emails.sql
|
||||
remember_extra_scoring_data.sql
|
||||
indexing_for_regions.sql
|
||||
latency_tester.sql
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
CREATE TABLE latency_testers (
|
||||
id VARCHAR(64) PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
|
||||
client_id VARCHAR(64) UNIQUE NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
ALTER TABLE connections ALTER COLUMN user_id DROP NOT NULL;
|
||||
|
|
@ -17,7 +17,6 @@ describe "Deployed site at #{www}", :js => true, :type => :feature, :capybara_fe
|
|||
Capybara.current_driver = Capybara.javascript_driver
|
||||
Capybara.app_host = www
|
||||
Capybara.run_server = false
|
||||
Capybara.default_wait_time = 10
|
||||
end
|
||||
|
||||
TestUser = Class.new do
|
||||
|
|
|
|||
|
|
@ -28,8 +28,15 @@ RSpec.configure do |config|
|
|||
config.order = 'random'
|
||||
|
||||
config.include Capybara::DSL
|
||||
|
||||
config.before(:each) do
|
||||
page.driver.headers = { 'User-Agent' => 'monitor' }
|
||||
end
|
||||
end
|
||||
|
||||
#Capybara.register_driver :poltergeist do |app|
|
||||
# Capybara::Poltergeist::Driver.new(app, { phantomjs_logger: File.open('console.log', 'w') })
|
||||
#end
|
||||
Capybara.javascript_driver = :poltergeist
|
||||
Capybara.default_driver = :poltergeist
|
||||
Capybara.run_server = false # since we're testing an app outside this project
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ group :test do
|
|||
gem 'faker'
|
||||
gem 'resque_spec' #, :path => "/home/jam/src/resque_spec/"
|
||||
gem 'timecop'
|
||||
gem 'rspec-prof'
|
||||
end
|
||||
|
||||
# Specify your gem's dependencies in jam_ruby.gemspec
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ require "jam_ruby/models/band_invitation"
|
|||
require "jam_ruby/models/band_musician"
|
||||
require "jam_ruby/models/connection"
|
||||
require "jam_ruby/models/diagnostic"
|
||||
require "jam_ruby/models/latency_tester"
|
||||
require "jam_ruby/models/friendship"
|
||||
require "jam_ruby/models/active_music_session"
|
||||
require "jam_ruby/models/music_session_comment"
|
||||
|
|
|
|||
|
|
@ -153,11 +153,11 @@ SQL
|
|||
# NOTE this is only used for testing purposes;
|
||||
# actual deletes will be processed in the websocket context which cleans up dependencies
|
||||
def expire_stale_connections()
|
||||
self.stale_connection_client_ids().each { |client| self.delete_connection(client[:client_id]) }
|
||||
self.stale_connection_client_ids.each { |client| self.delete_connection(client[:client_id]) }
|
||||
end
|
||||
|
||||
# expiring connections in stale state, which deletes them
|
||||
def stale_connection_client_ids()
|
||||
def stale_connection_client_ids
|
||||
clients = []
|
||||
ConnectionManager.active_record_transaction do |connection_manager|
|
||||
conn = connection_manager.pg_conn
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ module ValidationMessages
|
|||
INVALID_FPFILE = "is not valid"
|
||||
|
||||
#connection
|
||||
|
||||
USER_OR_LATENCY_TESTER_PRESENT = "user or latency_tester must be present"
|
||||
SELECT_AT_LEAST_ONE = "Please select at least one track" # DO NOT CHANGE THIS TEXT MESSAGE UNLESS YOU CHANGE createSession.js.erb, which is looking for it
|
||||
FAN_CAN_NOT_JOIN_AS_MUSICIAN = "A fan can not join a music session as a musician"
|
||||
MUSIC_SESSION_MUST_BE_SPECIFIED = "A music session must be specified"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,20 @@ module JamRuby
|
|||
Jampb::ClientMessage.parse(payload)
|
||||
end
|
||||
|
||||
# create a login message using client_id (used by latency_tester)
|
||||
def login_with_client_id(client_id)
|
||||
login = Jampb::Login.new(
|
||||
:client_id => client_id,
|
||||
:client_type => Connection::TYPE_LATENCY_TESTER
|
||||
)
|
||||
|
||||
Jampb::ClientMessage.new(
|
||||
:type => ClientMessage::Type::LOGIN,
|
||||
:route_to => SERVER_TARGET,
|
||||
:login => login
|
||||
)
|
||||
end
|
||||
|
||||
# create a login message using user/pass
|
||||
def login_with_user_pass(username, password, options = {})
|
||||
login = Jampb::Login.new(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ module JamRuby
|
|||
# client_types
|
||||
TYPE_CLIENT = 'client'
|
||||
TYPE_BROWSER = 'browser'
|
||||
TYPE_LATENCY_TESTER = 'latency_tester'
|
||||
|
||||
attr_accessor :joining_session
|
||||
|
||||
|
|
@ -13,11 +14,14 @@ module JamRuby
|
|||
|
||||
belongs_to :user, :class_name => "JamRuby::User"
|
||||
belongs_to :music_session, :class_name => "JamRuby::ActiveMusicSession", foreign_key: :music_session_id
|
||||
has_one :latency_tester, class_name: 'JamRuby::LatencyTester', foreign_key: :client_id, primary_key: :client_id
|
||||
has_many :tracks, :class_name => "JamRuby::Track", :inverse_of => :connection, :foreign_key => 'connection_id', :dependent => :delete_all
|
||||
|
||||
validates :as_musician, :inclusion => {:in => [true, false]}
|
||||
validates :client_type, :inclusion => {:in => [TYPE_CLIENT, TYPE_BROWSER]}
|
||||
validates :client_type, :inclusion => {:in => [TYPE_CLIENT, TYPE_BROWSER, TYPE_LATENCY_TESTER]}
|
||||
validate :can_join_music_session, :if => :joining_session?
|
||||
validate :user_or_latency_tester_present
|
||||
|
||||
after_save :require_at_least_one_track_when_in_session, :if => :joining_session?
|
||||
after_create :did_create
|
||||
after_save :report_add_participant
|
||||
|
|
@ -190,5 +194,12 @@ module JamRuby
|
|||
end
|
||||
end
|
||||
|
||||
def user_or_latency_tester_present
|
||||
if user.nil? && client_type != TYPE_LATENCY_TESTER
|
||||
puts client_type
|
||||
errors.add(:connection, ValidationMessages::USER_OR_LATENCY_TESTER_PRESENT)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ module JamRuby
|
|||
# this implies a coding error
|
||||
MISSING_CLIENT_STATE = 'MISSING_CLIENT_STATE'
|
||||
|
||||
# the underlying database connection is gone when the heartbeat comes in
|
||||
MISSING_CONNECTION = 'MISSING_CONNECTION'
|
||||
|
||||
# websocket gateway did not recognize message. indicates out-of-date websocket-gateway
|
||||
UNKNOWN_MESSAGE_TYPE = 'UNKNOWN_MESSAGE_TYPE'
|
||||
|
||||
|
|
@ -26,9 +29,15 @@ module JamRuby
|
|||
# websocket gateway got a client with the same client_id as an already-connected client
|
||||
DUPLICATE_CLIENT = 'DUPLICATE_CLIENT'
|
||||
|
||||
# info about how the test went
|
||||
NETWORK_TEST_RESULT = 'NETWORK_TEST_RESULT'
|
||||
|
||||
# step 2 of the FTUE... could the user select their gear?
|
||||
GEAR_SELECTION = 'GEAR_SELECTION'
|
||||
|
||||
DIAGNOSTIC_TYPES = [NO_HEARTBEAT_ACK, WEBSOCKET_CLOSED_REMOTELY, EXPIRED_STALE_CONNECTION,
|
||||
MISSING_CLIENT_STATE, UNKNOWN_MESSAGE_TYPE, MISSING_ROUTE_TO,
|
||||
DUPLICATE_CLIENT, WEBSOCKET_CLOSED_LOCALLY]
|
||||
DUPLICATE_CLIENT, WEBSOCKET_CLOSED_LOCALLY, NETWORK_TEST_RESULT, GEAR_SELECTION]
|
||||
|
||||
# creator types #
|
||||
CLIENT = 'client'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
module JamRuby
|
||||
class LatencyTester < ActiveRecord::Base
|
||||
|
||||
belongs_to :connection, class_name: 'JamRuby::Connection', foreign_key: :client_id, primary_key: :client_id
|
||||
|
||||
def heartbeat_interval_client
|
||||
nil
|
||||
end
|
||||
|
||||
def connection_expire_time_client
|
||||
nil
|
||||
end
|
||||
|
||||
def self.select_latency_tester
|
||||
LatencyTester.joins(:connection).first!
|
||||
end
|
||||
|
||||
# we need to find that latency_tester with the specified connection (and reconnect it)
|
||||
# or bootstrap a new latency_tester
|
||||
def self.connect(options)
|
||||
client_id = options[:client_id]
|
||||
ip_address = options[:ip_address]
|
||||
connection_stale_time = options[:connection_stale_time]
|
||||
connection_expire_time = options[:connection_expire_time]
|
||||
# first try to find a LatencyTester with that client_id
|
||||
latency_tester = LatencyTester.find_by_client_id(client_id)
|
||||
|
||||
if latency_tester
|
||||
if latency_tester.connection
|
||||
connection = latency_tester.connection
|
||||
else
|
||||
connection = Connection.new
|
||||
connection.client_id = client_id
|
||||
latency_tester.connection = connection
|
||||
end
|
||||
else
|
||||
latency_tester = LatencyTester.new
|
||||
latency_tester.client_id = client_id
|
||||
unless latency_tester.save
|
||||
return latency_tester
|
||||
end
|
||||
connection = Connection.new
|
||||
connection.latency_tester = latency_tester
|
||||
connection.client_id = client_id
|
||||
end
|
||||
|
||||
if ip_address and !ip_address.eql?(connection.ip_address)
|
||||
# locidispid stuff
|
||||
addr = JamIsp.ip_to_num(ip_address)
|
||||
isp = JamIsp.lookup(addr)
|
||||
if isp.nil? then ispid = 0 else ispid = isp.coid end
|
||||
block = GeoIpBlocks.lookup(addr)
|
||||
if block.nil? then locid = 0 else locid = block.locid end
|
||||
location = GeoIpLocations.lookup(locid)
|
||||
if location.nil?
|
||||
# todo what's a better default location?
|
||||
locidispid = 0
|
||||
else
|
||||
locidispid = locid*1000000+ispid
|
||||
end
|
||||
|
||||
connection.ip_address = ip_address
|
||||
connection.addr = addr
|
||||
connection.locidispid = locidispid
|
||||
end
|
||||
|
||||
connection.client_type = 'latency_tester'
|
||||
connection.aasm_state = Connection::CONNECT_STATE.to_s
|
||||
connection.stale_time = connection_stale_time
|
||||
connection.expire_time = connection_expire_time
|
||||
connection.as_musician = false
|
||||
unless connection.save
|
||||
return connection
|
||||
end
|
||||
|
||||
return latency_tester
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -103,6 +103,18 @@ module JamRuby
|
|||
|
||||
DISTANCE_OPTS = B_DISTANCE_OPTS = M_DISTANCE_OPTS = [['Any', 0], [1000.to_s, 1000], [500.to_s, 500], [250.to_s, 250], [100.to_s, 100], [50.to_s, 50], [25.to_s, 25]]
|
||||
|
||||
# the values for score ranges are raw roundtrip scores. david often talks of one way scores (<= 20 is good), but
|
||||
# the client reports scores as roundtrip and the server uses those values throughout
|
||||
GOOD_SCORE = '.-40'
|
||||
MODERATE_SCORE = '40-80'
|
||||
POOR_SCORE = '80-120'
|
||||
UNACCEPTABLE_SCORE = '120-.'
|
||||
SCORED_SCORE = '.-.' # does not appear in menu choices
|
||||
TEST_SCORE = '.-60' # does not appear in menu choices
|
||||
ANY_SCORE = ''
|
||||
M_SCORE_OPTS = [['Any', ANY_SCORE], ['Good', GOOD_SCORE], ['Moderate', MODERATE_SCORE], ['Poor', POOR_SCORE], ['Unacceptable', UNACCEPTABLE_SCORE]]
|
||||
M_SCORE_DEFAULT = ANY_SCORE
|
||||
|
||||
F_SORT_RECENT = ['Most Recent', :date]
|
||||
F_SORT_OLDEST = ['Most Liked', :likes]
|
||||
F_SORT_LENGTH = ['Most Played', :plays]
|
||||
|
|
@ -123,16 +135,16 @@ module JamRuby
|
|||
# produce a list of musicians (users where musician is true)
|
||||
# params:
|
||||
# instrument - instrument to search for or blank
|
||||
# score_limit - score must be <= this to be included in the result
|
||||
# score_limit - a range specification for score, see M_SCORE_OPTS above.
|
||||
# handled by relation_pagination:
|
||||
# page - page number to fetch (origin 1)
|
||||
# per_page - number of entries per page
|
||||
# handled by order_param:
|
||||
# orderby - ??? (followed, plays, playing)
|
||||
# handled by where_latlng:
|
||||
# distance - defunct
|
||||
# city - defunct
|
||||
# remote_ip - defunct
|
||||
# orderby - what sort of search, also defines order (followed, plays, playing)
|
||||
# previously handled by where_latlng:
|
||||
# distance - defunct!
|
||||
# city - defunct!
|
||||
# remote_ip - defunct!
|
||||
def self.musician_filter(params={}, user=nil, conn=nil)
|
||||
# puts "================ params #{params.inspect}"
|
||||
# puts "================ user #{user.inspect}"
|
||||
|
|
@ -151,11 +163,11 @@ module JamRuby
|
|||
# weeded out
|
||||
|
||||
# filter on scores using selections from params
|
||||
# todo scott what is the real limit?
|
||||
score_limit = 60
|
||||
# see M_SCORE_OPTS
|
||||
score_limit = TEST_SCORE
|
||||
l = params[:score_limit]
|
||||
unless l.nil? or l.to_i <= 0
|
||||
score_limit = l.to_i
|
||||
unless l.nil?
|
||||
score_limit = l
|
||||
end
|
||||
|
||||
# puts "================ score_limit #{score_limit}"
|
||||
|
|
@ -165,9 +177,47 @@ module JamRuby
|
|||
# puts "================ locidispid #{locidispid}"
|
||||
|
||||
unless locidispid.nil?
|
||||
rel = rel.joins('inner join scores on scores.alocidispid = users.last_jam_locidispid')
|
||||
# score_join of left allows for null scores, whereas score_join of inner requires a score however good or bad
|
||||
# this is ANY_SCORE:
|
||||
score_join = 'left' # or 'inner'
|
||||
score_min = nil
|
||||
score_max = nil
|
||||
case score_limit
|
||||
when GOOD_SCORE
|
||||
score_join = 'inner'
|
||||
score_min = nil
|
||||
score_max = 40
|
||||
when MODERATE_SCORE
|
||||
score_join = 'inner'
|
||||
score_min = 40
|
||||
score_max = 80
|
||||
when POOR_SCORE
|
||||
score_join = 'inner'
|
||||
score_min = 80
|
||||
score_max = 120
|
||||
when UNACCEPTABLE_SCORE
|
||||
score_join = 'inner'
|
||||
score_min = 120
|
||||
score_max = nil
|
||||
when SCORED_SCORE
|
||||
score_join = 'inner'
|
||||
score_min = nil
|
||||
score_max = nil
|
||||
when TEST_SCORE
|
||||
score_join = 'inner'
|
||||
score_min = nil
|
||||
score_max = 60
|
||||
when ANY_SCORE
|
||||
# the default of ANY setup above applies
|
||||
else
|
||||
# the default of ANY setup above applies
|
||||
end
|
||||
|
||||
rel = rel.joins("#{score_join} join scores on scores.alocidispid = users.last_jam_locidispid")
|
||||
.where(['scores.blocidispid = ?', locidispid])
|
||||
.where(['scores.score <= ?', score_limit])
|
||||
|
||||
rel = rel.where(['scores.score > ?', score_min]) unless score_min.nil?
|
||||
rel = rel.where(['scores.score <= ?', score_max]) unless score_max.nil?
|
||||
|
||||
rel = rel.select('scores.score')
|
||||
rel = rel.group('scores.score')
|
||||
|
|
@ -178,10 +228,10 @@ module JamRuby
|
|||
case ordering
|
||||
when :plays # FIXME: double counting?
|
||||
# sel_str = "COUNT(records)+COUNT(sessions) AS play_count, #{sel_str}"
|
||||
rel = rel.select('COUNT(records.id)+COUNT(sessions.id) AS play_count')
|
||||
rel = rel.select('COUNT(records.id)+COUNT(sessions.id) AS search_play_count')
|
||||
rel = rel.joins("LEFT JOIN music_sessions AS sessions ON sessions.user_id = users.id")
|
||||
rel = rel.joins("LEFT JOIN recordings AS records ON records.owner_id = users.id")
|
||||
rel = rel.order("play_count DESC")
|
||||
rel = rel.order("search_play_count DESC")
|
||||
when :followed
|
||||
rel = rel.joins('left outer join follows on follows.followable_id = users.id')
|
||||
rel = rel.select('count(follows.user_id) as search_follow_count')
|
||||
|
|
@ -192,7 +242,7 @@ module JamRuby
|
|||
end
|
||||
|
||||
unless locidispid.nil?
|
||||
rel = rel.order('scores.score')
|
||||
rel = rel.order('scores.score ASC NULLS LAST')
|
||||
end
|
||||
|
||||
rel = rel.order('users.created_at DESC')
|
||||
|
|
@ -201,9 +251,13 @@ module JamRuby
|
|||
rel, page = self.relation_pagination(rel, params)
|
||||
rel = rel.includes([:instruments, :followings, :friends])
|
||||
|
||||
# puts "======================== sql #{rel.to_sql}"
|
||||
objs = rel.all
|
||||
|
||||
# puts "======================== objs #{objs.inspect}"
|
||||
# if objs.length > 0
|
||||
# puts "======================== attributes #{objs[0].attributes}"
|
||||
# puts "======================== score #{objs[0].score}"
|
||||
# end
|
||||
|
||||
srch = Search.new
|
||||
srch.search_type = :musicians_filter
|
||||
|
|
|
|||
|
|
@ -290,6 +290,11 @@ module JamRuby
|
|||
self.music_sessions.size
|
||||
end
|
||||
|
||||
def joined_score
|
||||
nil unless has_attribute?(:score)
|
||||
read_attribute(:score).to_i
|
||||
end
|
||||
|
||||
# mods comes back as text; so give ourselves a parsed version
|
||||
def mods_json
|
||||
@mods_json ||= mods ? JSON.parse(mods, symbolize_names: true) : {}
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ FactoryGirl.define do
|
|||
addr 0
|
||||
locidispid 0
|
||||
client_type 'client'
|
||||
association :user, factory: :user
|
||||
end
|
||||
|
||||
factory :invitation, :class => JamRuby::Invitation do
|
||||
|
|
@ -491,4 +492,19 @@ FactoryGirl.define do
|
|||
factory :rsvp_request_slot, class: JamRuby::RsvpRequestRsvpSlot do
|
||||
chosen false
|
||||
end
|
||||
|
||||
factory :latency_tester, :class => JamRuby::LatencyTester do
|
||||
ignore do
|
||||
connection nil
|
||||
make_connection true
|
||||
end
|
||||
|
||||
sequence(:client_id) { |n| "LatencyTesterClientId-#{n}" }
|
||||
|
||||
after(:create) do |latency_tester, evaluator|
|
||||
latency_tester.connection = evaluator.connection if evaluator.connection
|
||||
latency_tester.connection = FactoryGirl.create(:connection, client_type: Connection::TYPE_LATENCY_TESTER, client_id: latency_tester.client_id) if evaluator.make_connection
|
||||
latency_tester.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe LatencyTester do
|
||||
|
||||
let(:params) {{client_id: 'abc', ip_address: '10.1.1.1', connection_stale_time:40, connection_expire_time:60} }
|
||||
|
||||
it "success" do
|
||||
latency_tester = FactoryGirl.create(:latency_tester)
|
||||
latency_tester.connection.should_not be_nil
|
||||
latency_tester.connection.latency_tester.should_not be_nil
|
||||
|
||||
end
|
||||
|
||||
describe "connect" do
|
||||
it "no existing latency tester" do
|
||||
latency_tester = LatencyTester.connect(params)
|
||||
latency_tester.errors.any?.should be_false
|
||||
latency_tester.connection.ip_address.should == params[:ip_address]
|
||||
latency_tester.connection.stale_time.should == params[:connection_stale_time]
|
||||
latency_tester.connection.expire_time.should == params[:connection_expire_time]
|
||||
end
|
||||
|
||||
it "existing latency tester, no connection" do
|
||||
latency_tester = FactoryGirl.create(:latency_tester, client_id: params[:client_id], make_connection: false)
|
||||
latency_tester.connection.should be_nil
|
||||
|
||||
latency_tester.client_id = params[:client_id]
|
||||
latency_tester.save!
|
||||
|
||||
found = LatencyTester.connect(params)
|
||||
found.should == latency_tester
|
||||
found.connection.should_not be_nil
|
||||
found.connection.aasm_state.should == Connection::CONNECT_STATE.to_s
|
||||
found.connection.client_id.should == latency_tester.client_id
|
||||
end
|
||||
|
||||
it "existing latency tester, existing connection" do
|
||||
latency_tester = FactoryGirl.create(:latency_tester)
|
||||
|
||||
latency_tester.connection.aasm_state = Connection::STALE_STATE.to_s
|
||||
latency_tester.save!
|
||||
set_updated_at(latency_tester.connection, 1.days.ago)
|
||||
|
||||
params[:client_id] = latency_tester.connection.client_id
|
||||
|
||||
found = LatencyTester.connect(params)
|
||||
found.should == latency_tester
|
||||
found.connection.should == latency_tester.connection
|
||||
# state should have refreshed from stale to connected
|
||||
found.connection.aasm_state.should == Connection::CONNECT_STATE.to_s
|
||||
# updated_at needs to be poked on connection to keep stale non-stale
|
||||
(found.connection.updated_at - latency_tester.connection.updated_at).to_i.should == 60 * 60 * 24 # 1 day
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -6,7 +6,13 @@ class SpecDb
|
|||
def self.recreate_database
|
||||
conn = PG::Connection.open("dbname=postgres user=postgres password=postgres host=localhost")
|
||||
conn.exec("DROP DATABASE IF EXISTS #{TEST_DB_NAME}")
|
||||
if ENV['TABLESPACE']
|
||||
conn.exec("CREATE DATABASE #{TEST_DB_NAME} WITH TABLESPACE=#{ENV['TABLESPACE']}")
|
||||
else
|
||||
conn.exec("CREATE DATABASE #{TEST_DB_NAME}")
|
||||
end
|
||||
|
||||
|
||||
JamDb::Migrator.new.migrate(:dbname => TEST_DB_NAME, :user => "postgres", :password => "postgres", :host => "localhost")
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ ENV["RAILS_ENV"] = "test"
|
|||
|
||||
require 'simplecov'
|
||||
require 'support/utilities'
|
||||
require 'support/profile'
|
||||
require 'active_record'
|
||||
require 'jam_db'
|
||||
require 'spec_db'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
if ENV['PROFILE']
|
||||
require 'ruby-prof'
|
||||
RSpec.configure do |c|
|
||||
def profile
|
||||
result = RubyProf.profile { yield }
|
||||
name = example.metadata[:full_description].downcase.gsub(/[^a-z0-9_-]/, "-").gsub(/-+/, "-")
|
||||
printer = RubyProf::CallTreePrinter.new(result)
|
||||
Dir.mkdir('tmp/performance')
|
||||
open("tmp/performance/callgrind.#{name}.#{Time.now.to_i}.trace", "w") do |f|
|
||||
printer.print(f)
|
||||
end
|
||||
end
|
||||
|
||||
c.around(:each) do |example|
|
||||
if ENV['PROFILE'] == 'all' or (example.metadata[:profile] and ENV['PROFILE'])
|
||||
profile { example.run }
|
||||
else
|
||||
example.run
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -71,7 +71,7 @@ gem 'resque_mailer'
|
|||
#gem 'typescript-src', path: '../../typescript-src-ruby'
|
||||
#gem 'typescript-node', path: '../../typescript-node-ruby'
|
||||
#gem 'typescript-rails', path: '../../typescript-rails'
|
||||
|
||||
gem 'netaddr'
|
||||
gem 'quiet_assets', :group => :development
|
||||
gem 'bugsnag'
|
||||
gem 'multi_json', '1.9.0'
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 196 B |
Binary file not shown.
|
After Width: | Height: | Size: 181 B |
Binary file not shown.
|
After Width: | Height: | Size: 181 B |
Binary file not shown.
|
After Width: | Height: | Size: 181 B |
Binary file not shown.
|
After Width: | Height: | Size: 181 B |
|
|
@ -19,6 +19,14 @@
|
|||
'log':null, 'debug':null, 'info':null, 'warn':null, 'error':null, 'assert':null, 'trace':null, 'exception':null
|
||||
}
|
||||
|
||||
var backend_methods = {
|
||||
"log" : 4,
|
||||
"debug" : 4,
|
||||
"info" : 3,
|
||||
"warn" : 2,
|
||||
"error" : 1
|
||||
}
|
||||
|
||||
var logCache = [];
|
||||
|
||||
if ('undefined' === typeof(context.console)) {
|
||||
|
|
@ -33,15 +41,28 @@
|
|||
context.console.debug = function() { console.log(arguments); }
|
||||
}
|
||||
|
||||
console.proxy_logs_to_backend = false;
|
||||
|
||||
// http://tobyho.com/2012/07/27/taking-over-console-log/
|
||||
function takeOverConsole(){
|
||||
var console = window.console
|
||||
if (!console) return
|
||||
if (!console) return;
|
||||
var i = null;
|
||||
function intercept(method){
|
||||
var original = console[method]
|
||||
console[method] = function(){
|
||||
|
||||
logCache.push([method].concat(arguments));
|
||||
var logAsString = [];
|
||||
for(i in arguments) {
|
||||
var arg = arguments[i];
|
||||
try {
|
||||
logAsString.push(JSON.stringify(arg));
|
||||
}
|
||||
catch(e) {
|
||||
logAsString.push("unable to parse node: " + e.toString());
|
||||
}
|
||||
}
|
||||
logCache.push([method].concat(logAsString));
|
||||
if(logCache.length > 50) {
|
||||
// keep the cache size 50 or lower
|
||||
logCache.pop();
|
||||
|
|
@ -55,9 +76,15 @@
|
|||
var message = Array.prototype.slice.apply(arguments).join(' ')
|
||||
original(message)
|
||||
}
|
||||
if(console.proxy_logs_to_backend && context.jamClient) {
|
||||
var backendLevel = backend_methods[method];
|
||||
if(backendLevel) {
|
||||
context.jamClient.log(backendLevel, logAsString.join(', '));
|
||||
}
|
||||
}
|
||||
var methods = ['log', 'warn', 'error']
|
||||
}
|
||||
}
|
||||
var methods = ['log', 'warn', 'error', 'debug', 'info']
|
||||
for (var i = 0; i < methods.length; i++)
|
||||
intercept(methods[i])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,6 +135,18 @@
|
|||
return client_container(msg.LOGIN, route_to.SERVER, login);
|
||||
};
|
||||
|
||||
// create a login message using only the client_id. only valid for latency_tester
|
||||
factory.login_with_client_id = function(client_id, client_type) {
|
||||
if(client_type != 'latency_tester') {
|
||||
throw "client_type must be latency_tester in login_with_client_id";
|
||||
}
|
||||
var login = {
|
||||
client_id : client_id,
|
||||
client_type : client_type
|
||||
};
|
||||
return client_container(msg.LOGIN, route_to.SERVER, login);
|
||||
};
|
||||
|
||||
// create a music session login message
|
||||
factory.login_music_session = function(music_session) {
|
||||
var login_music_session = { music_session : music_session };
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
// uniquely identify the websocket connection
|
||||
var channelId = null;
|
||||
var clientType = null;
|
||||
var mode = null;
|
||||
|
||||
// heartbeat
|
||||
var heartbeatInterval = null;
|
||||
|
|
@ -156,10 +157,11 @@
|
|||
// for debugging purposes, see if the last time we've sent a heartbeat is way off (500ms) of the target interval
|
||||
var now = new Date();
|
||||
|
||||
if (lastHeartbeatSentTime) {
|
||||
|
||||
if(lastHeartbeatSentTime) {
|
||||
var drift = new Date().getTime() - lastHeartbeatSentTime.getTime() - heartbeatMS;
|
||||
if (drift > 500) {
|
||||
logger.error("significant drift between heartbeats: " + drift + 'ms beyond target interval')
|
||||
logger.warn("significant drift between heartbeats: " + drift + 'ms beyond target interval')
|
||||
}
|
||||
}
|
||||
lastHeartbeatSentTime = now;
|
||||
|
|
@ -168,6 +170,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
function isClientMode() {
|
||||
return mode == "client";
|
||||
}
|
||||
|
||||
function loggedIn(header, payload) {
|
||||
|
||||
if (!connectTimeout) {
|
||||
|
|
@ -179,21 +185,21 @@
|
|||
|
||||
app.clientId = payload.client_id;
|
||||
|
||||
if(isClientMode()) {
|
||||
// tell the backend that we have logged in
|
||||
context.jamClient.OnLoggedIn(payload.user_id, payload.token);
|
||||
|
||||
$.cookie('client_id', payload.client_id);
|
||||
|
||||
}
|
||||
|
||||
heartbeatMS = payload.heartbeat_interval * 1000;
|
||||
connection_expire_time = payload.connection_expire_time * 1000;
|
||||
logger.debug("jamkazam.js.loggedIn(): clientId=" + app.clientId + ", heartbeat=" + payload.heartbeat_interval + "s, expire_time=" + payload.connection_expire_time + 's');
|
||||
logger.info("jamkazam.js.loggedIn(): clientId=" + app.clientId + ", heartbeat=" + payload.heartbeat_interval + "s, expire_time=" + payload.connection_expire_time + 's');
|
||||
heartbeatInterval = context.setInterval(_heartbeat, heartbeatMS);
|
||||
heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000);
|
||||
lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat
|
||||
connectDeferred.resolve();
|
||||
activeElementEvent('afterConnect', payload);
|
||||
|
||||
activeElementEvent('afterConnect', payload);
|
||||
}
|
||||
|
||||
function heartbeatAck(header, payload) {
|
||||
|
|
@ -264,7 +270,7 @@
|
|||
.always(function() {
|
||||
if ($currentDisplay.is('.no-websocket-connection')) {
|
||||
// this path is the 'not in session path'; so there is nothing else to do
|
||||
$currentDisplay.hide();
|
||||
$currentDisplay.removeClass('active');
|
||||
|
||||
// TODO: tell certain elements that we've reconnected
|
||||
}
|
||||
|
|
@ -298,7 +304,7 @@
|
|||
$inSituContent.find('.reconnect-countdown').html(formatDelaySecs(reconnectDelaySecs()));
|
||||
$messageContents.empty();
|
||||
$messageContents.append($inSituContent);
|
||||
$inSituBannerHolder.show();
|
||||
$inSituBannerHolder.addClass('active');
|
||||
content = $inSituBannerHolder;
|
||||
}
|
||||
|
||||
|
|
@ -446,6 +452,10 @@
|
|||
if(!clientType) {
|
||||
clientType = context.JK.clientType();
|
||||
}
|
||||
if(!mode) {
|
||||
mode = context.jamClient.getOperatingMode();
|
||||
}
|
||||
|
||||
connectDeferred = new $.Deferred();
|
||||
channelId = context.JK.generateUUID(); // create a new channel ID for every websocket connection
|
||||
|
||||
|
|
@ -460,7 +470,8 @@
|
|||
|
||||
connectTimeout = setTimeout(function () {
|
||||
connectTimeout = null;
|
||||
if (connectDeferred.state() === 'pending') {
|
||||
|
||||
if(connectDeferred.state() === 'pending') {
|
||||
server.close(true);
|
||||
connectDeferred.reject();
|
||||
}
|
||||
|
|
@ -470,7 +481,7 @@
|
|||
};
|
||||
|
||||
server.close = function (in_error) {
|
||||
logger.log("closing websocket");
|
||||
logger.info("closing websocket");
|
||||
|
||||
clientClosedConnection = true;
|
||||
server.socket.close();
|
||||
|
|
@ -486,9 +497,20 @@
|
|||
server.send(loginMessage);
|
||||
};
|
||||
|
||||
server.latencyTesterLogin = function() {
|
||||
var loginMessage = msg_factory.login_with_client_id(context.jamClient.clientID, 'latency_tester');
|
||||
server.send(loginMessage);
|
||||
}
|
||||
|
||||
server.onOpen = function () {
|
||||
logger.log("server.onOpen");
|
||||
logger.debug("server.onOpen");
|
||||
|
||||
if(isClientMode()) {
|
||||
server.rememberLogin();
|
||||
}
|
||||
else {
|
||||
server.latencyTesterLogin();
|
||||
}
|
||||
};
|
||||
|
||||
server.onMessage = function (e) {
|
||||
|
|
@ -498,7 +520,7 @@
|
|||
callbacks = server.dispatchTable[message.type];
|
||||
|
||||
if (message.type != context.JK.MessageType.HEARTBEAT_ACK && message.type != context.JK.MessageType.PEER_MESSAGE) {
|
||||
logger.log("server.onMessage:" + messageType + " payload:" + JSON.stringify(payload));
|
||||
logger.info("server.onMessage:" + messageType + " payload:" + JSON.stringify(payload));
|
||||
}
|
||||
|
||||
if (callbacks !== undefined) {
|
||||
|
|
@ -513,13 +535,13 @@
|
|||
}
|
||||
}
|
||||
else {
|
||||
logger.log("Unexpected message type %s.", message.type);
|
||||
logger.info("Unexpected message type %s.", message.type);
|
||||
}
|
||||
};
|
||||
|
||||
// onClose is called if either client or server closes connection
|
||||
server.onClose = function () {
|
||||
logger.log("Socket to server closed.");
|
||||
logger.info("Socket to server closed.");
|
||||
|
||||
if (connectDeferred.state() === "pending") {
|
||||
connectDeferred.reject();
|
||||
|
|
@ -533,12 +555,12 @@
|
|||
var jsMessage = JSON.stringify(message);
|
||||
|
||||
if (message.type != context.JK.MessageType.HEARTBEAT && message.type != context.JK.MessageType.PEER_MESSAGE) {
|
||||
logger.log("server.send(" + jsMessage + ")");
|
||||
logger.info("server.send(" + jsMessage + ")");
|
||||
}
|
||||
if (server !== undefined && server.socket !== undefined && server.socket.send !== undefined) {
|
||||
server.socket.send(jsMessage);
|
||||
} else {
|
||||
logger.log("Dropped message because server connection is closed.");
|
||||
logger.warn("Dropped message because server connection is closed.");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -546,7 +568,7 @@
|
|||
var loginMessage;
|
||||
|
||||
if (!server.signedIn) {
|
||||
logger.log("Not signed in!");
|
||||
logger.warn("Not signed in!");
|
||||
// TODO: surface the error
|
||||
return;
|
||||
}
|
||||
|
|
@ -599,7 +621,7 @@
|
|||
server.connected = true;
|
||||
|
||||
if (context.jamClient !== undefined) {
|
||||
logger.debug("... (handling LOGIN_ACK) Updating backend client, connected to true and clientID to " +
|
||||
logger.info("... (handling LOGIN_ACK) Updating backend client, connected to true and clientID to " +
|
||||
payload.client_id);
|
||||
context.jamClient.connected = true;
|
||||
context.jamClient.clientID = server.clientID;
|
||||
|
|
|
|||
|
|
@ -29,16 +29,19 @@
|
|||
var validProfiles = prettyPrintAudioProfiles(context.JK.getGoodConfigMap());
|
||||
var invalidProfiles = prettyPrintAudioProfiles(context.JK.getBadConfigMap());
|
||||
|
||||
var template = context.JK.fillTemplate($('#template-account-main').html(), {
|
||||
var $template = $(context._.template($('#template-account-main').html(), {
|
||||
email: userDetail.email,
|
||||
name: userDetail.name,
|
||||
location : userDetail.location,
|
||||
instruments : prettyPrintInstruments(userDetail.instruments),
|
||||
photoUrl : context.JK.resolveAvatarUrl(userDetail.photo_url),
|
||||
validProfiles : validProfiles,
|
||||
invalidProfiles : invalidProfiles
|
||||
});
|
||||
$('#account-content-scroller').html(template);
|
||||
invalidProfiles : invalidProfiles,
|
||||
isNativeClient: gon.isNativeClient,
|
||||
musician: context.JK.currentUserMusician
|
||||
} , { variable: 'data' }));
|
||||
|
||||
$('#account-content-scroller').html($template);
|
||||
}
|
||||
|
||||
function prettyPrintAudioProfiles(profileMap) {
|
||||
|
|
|
|||
|
|
@ -35,18 +35,30 @@
|
|||
}
|
||||
|
||||
function populateAccountAudio() {
|
||||
var all = context.jamClient.FTUEGetAllAudioConfigurations();
|
||||
var good = context.jamClient.FTUEGetGoodAudioConfigurations();
|
||||
var current = context.jamClient.FTUEGetMusicProfileName();
|
||||
|
||||
// load Audio Driver dropdown
|
||||
var devices = context.jamClient.TrackGetDevices();
|
||||
var profiles = [];
|
||||
context._.each(all, function(item) {
|
||||
profiles.push({id: item, good: false, class:'bad', current: current == item, active_text: current == item ? '(active)' : ''})
|
||||
});
|
||||
|
||||
var options = {
|
||||
devices: devices
|
||||
if(good) {
|
||||
for(var i = 0; i < good.length; i++) {
|
||||
for(var j = 0; j < profiles.length; j++) {
|
||||
if(good[i] == profiles[j].id) {
|
||||
profiles[j].good = true;
|
||||
profiles[j].class = 'good';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var template = context._.template($('#template-account-audio').html(), options, {variable: 'data'});
|
||||
var template = context._.template($('#template-account-audio').html(), {profiles: profiles}, {variable: 'data'});
|
||||
|
||||
appendAudio(template);
|
||||
|
||||
}
|
||||
|
||||
function appendAudio(template) {
|
||||
|
|
@ -65,9 +77,29 @@
|
|||
populateAccountAudio();
|
||||
}
|
||||
|
||||
function handleActivateAudioProfile(audioProfileId) {
|
||||
logger.debug("activating audio profile: " + audioProfileId);
|
||||
|
||||
if(audioProfileId == context.jamClient.FTUEGetMusicProfileName()) {
|
||||
context.JK.Banner.showAlert("This profile is already active.");
|
||||
return;
|
||||
}
|
||||
|
||||
var result = context.jamClient.FTUELoadAudioConfiguration(audioProfileId);
|
||||
|
||||
if(!result) {
|
||||
logger.error("unable to activate audio configuration: " + audioProfileId);
|
||||
context.JK.alertSupportedNeeded("Unable to activate audio configuration for profile named: " + audioProfileId);
|
||||
}
|
||||
|
||||
// redraw after activation of profile
|
||||
populateAccountAudio();
|
||||
}
|
||||
function handleStartAudioQualification() {
|
||||
|
||||
if(true) {
|
||||
app.afterFtue = function() { populateAccountAudio() };
|
||||
app.cancelFtue = function() { populateAccountAudio() };
|
||||
app.layout.startNewFtue();
|
||||
}
|
||||
else {
|
||||
|
|
@ -91,12 +123,27 @@
|
|||
// events for main screen
|
||||
function events() {
|
||||
// wire up main panel clicks
|
||||
$('#account-audio-content-scroller').on('click', 'a[data-purpose=delete-audio-profile]', function (evt) {
|
||||
var $root = $('#account-audio-content-scroller');
|
||||
$root.on('click', 'a[data-purpose=delete-audio-profile]', function (evt) {
|
||||
evt.stopPropagation();
|
||||
handleDeleteAudioProfile($(this).attr('data-id'));
|
||||
return false;
|
||||
});
|
||||
$('#account-audio-content-scroller').on('click', 'a[data-purpose=add-profile]', function (evt) {
|
||||
|
||||
$root.on('click', 'a[data-purpose=activate-audio-profile]', function (evt) {
|
||||
evt.stopPropagation();
|
||||
var $btn = $(this);
|
||||
var status = $btn.closest('tr').attr('data-status');
|
||||
if(status == "good") {
|
||||
handleActivateAudioProfile($btn.attr('data-id'));
|
||||
}
|
||||
else {
|
||||
context.JK.Banner.showAlert("Unable to activate this profile. Please verify that the devices associated are connected.");
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
$root.on('click', 'a[data-purpose=add-profile]', function (evt) {
|
||||
evt.stopPropagation();
|
||||
handleStartAudioQualification();
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
//= require jquery.monkeypatch
|
||||
//= require jquery_ujs
|
||||
//= require jquery.ui.draggable
|
||||
//= require jquery.ui.droppable
|
||||
//= require jquery.bt
|
||||
//= require jquery.icheck
|
||||
//= require jquery.color
|
||||
|
|
@ -39,3 +40,6 @@
|
|||
//= require utils
|
||||
//= require custom_controls
|
||||
//= require_directory .
|
||||
//= require_directory ./wizard
|
||||
//= require_directory ./wizard/gear
|
||||
//= require_directory ./wizard/loopback
|
||||
|
|
|
|||
|
|
@ -344,7 +344,7 @@
|
|||
var instrument = musician.instruments[j];
|
||||
var inst = '../assets/content/icon_instrument_default24.png';
|
||||
if (instrument.instrument_id in instrument_logo_map) {
|
||||
inst = instrument_logo_map[instrument.instrument_id];
|
||||
inst = instrument_logo_map[instrument.instrument_id].asset;
|
||||
}
|
||||
instrumentLogoHtml += '<img src="' + inst + '" width="24" height="24" /> ';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
context.JK.Banner = (function () {
|
||||
var self = this;
|
||||
var logger = context.JK.logger;
|
||||
var $banner = $('#banner');
|
||||
var $banner = null;
|
||||
|
||||
function showAlert(options) {
|
||||
if (typeof options == 'string' || options instanceof String) {
|
||||
|
|
@ -35,13 +35,33 @@
|
|||
throw "unable to show banner for empty message";
|
||||
}
|
||||
|
||||
if(options.type == "alert" || options.close) {
|
||||
var $closeBtn = $('#banner').find('.close-btn');
|
||||
var $closeBtn = $banner.find('.close-btn');
|
||||
|
||||
$closeBtn.click(function() {
|
||||
if((options.type == "alert" && !options.buttons) || options.close) {
|
||||
|
||||
$closeBtn.show().click(function() {
|
||||
hide();
|
||||
return false;
|
||||
}).show();
|
||||
});
|
||||
}
|
||||
else {
|
||||
$closeBtn.hide();
|
||||
}
|
||||
|
||||
if(options.buttons) {
|
||||
var $buttons = $banner.find('.buttons')
|
||||
context._.each(options.buttons, function(button) {
|
||||
if(!button.name) throw "button.name must be specified";
|
||||
if(!button.click) throw "button.click must be specified";
|
||||
|
||||
var $btn = $('<a class="button-orange user-btn">' + button.name + '</a>');
|
||||
$btn.click(function() {
|
||||
button.click();
|
||||
hide();
|
||||
return false;
|
||||
});
|
||||
$buttons.append($btn);
|
||||
});
|
||||
}
|
||||
|
||||
$('#banner').attr('data-type', options.type).show()
|
||||
|
|
@ -52,13 +72,15 @@
|
|||
}
|
||||
|
||||
function hide() {
|
||||
$('#banner').hide();
|
||||
$banner.hide();
|
||||
$banner.find('.user-btn').remove();
|
||||
$('#banner_overlay .dialog-inner').html("");
|
||||
$('#banner_overlay').hide();
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
|
||||
$banner = $('#banner');
|
||||
return self;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,15 @@
|
|||
function RestartApplication() {
|
||||
|
||||
}
|
||||
function FTUECancel() {
|
||||
|
||||
}
|
||||
function FTUEGetMusicProfileName() {
|
||||
return "default"
|
||||
}
|
||||
function FTUESetMusicProfileName() {
|
||||
|
||||
}
|
||||
function FTUEGetInputLatency() {
|
||||
dbg("FTUEGetInputLatency");
|
||||
return 2;
|
||||
|
|
@ -85,6 +93,9 @@
|
|||
function FTUEGetStatus() { return ftueStatus; }
|
||||
function FTUESetStatus(b) { ftueStatus = b; }
|
||||
function FTUESetMusicDevice(id) { dbg("FTUESetMusicDevice"); }
|
||||
function FTUEGetAudioDevices() {
|
||||
return {"devices":[{"display_name":"Built-in","guid":"Built-in","input_count":1,"name":"Built-in","output_count":1,"port_audio_name":"Built-in"},{"display_name":"JamKazam Virtual Monitor","guid":"JamKazam Virtual Monitor","input_count":0,"name":"JamKazam Virtual Monitor","output_count":1,"port_audio_name":"JamKazam Virtual Monitor"}]}
|
||||
}
|
||||
function FTUEGetDevices() {
|
||||
dbg('FTUEGetMusicDevices');
|
||||
return {
|
||||
|
|
@ -113,6 +124,88 @@
|
|||
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
|
||||
};
|
||||
}
|
||||
function FTUEGetChannels() {
|
||||
return {
|
||||
"inputs": [
|
||||
{
|
||||
"assignment": 1,
|
||||
"chat": false,
|
||||
"device_id": "Built-in Microph",
|
||||
"device_type": 5,
|
||||
"id": "i~5~Built-in Microph~0~0~Built-in",
|
||||
"name": "Built-in Microph - Left",
|
||||
"number": 0
|
||||
},
|
||||
{
|
||||
"assignment": 0,
|
||||
"chat": false,
|
||||
"device_id": "Built-in Microph",
|
||||
"device_type": 5,
|
||||
"id": "i~5~Built-in Microph~1~0~Built-in",
|
||||
"name": "Built-in Microph - Right",
|
||||
"number": 1
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"assignment": -1,
|
||||
"chat": false,
|
||||
"device_id": "Built-in Output",
|
||||
"device_type": 5,
|
||||
"id": "o~5~Built-in Output~0~0~Built-in",
|
||||
"name": "Built-in Output - Left",
|
||||
"number": 0
|
||||
},
|
||||
{
|
||||
"assignment": -1,
|
||||
"chat": false,
|
||||
"device_id": "Built-in Output",
|
||||
"device_type": 5,
|
||||
"id": "o~5~Built-in Output~1~0~Built-in",
|
||||
"name": "Built-in Output - Right",
|
||||
"number": 1
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
function FTUEGetAudioDevices() {
|
||||
return {
|
||||
"devices": [
|
||||
{
|
||||
"display_name": "Built-in",
|
||||
"guid": "Built-in",
|
||||
"input_count": 1,
|
||||
"name": "Built-in",
|
||||
"output_count": 1,
|
||||
"port_audio_name": "Built-in"
|
||||
},
|
||||
{
|
||||
"display_name": "JamKazam Virtual Monitor",
|
||||
"guid": "JamKazam Virtual Monitor",
|
||||
"input_count": 0,
|
||||
"name": "JamKazam Virtual Monitor",
|
||||
"output_count": 1,
|
||||
"port_audio_name": "JamKazam Virtual Monitor"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
function FTUEStartIoPerfTest() {}
|
||||
function FTUEGetIoPerfData() {
|
||||
return {
|
||||
"in_var" : 0.15,
|
||||
"out_var" : 0.25,
|
||||
"in_median" : 399.9,
|
||||
"out_median" : 400.3,
|
||||
"in_target" : 400,
|
||||
"out_target" : 400
|
||||
};
|
||||
}
|
||||
function FTUESetInputMusicDevice() { }
|
||||
function FTUESetOutputMusicDevice() { }
|
||||
function FTUEGetInputMusicDevice() { return 'Built-in'; }
|
||||
function FTUEGetOutputMusicDevice() { return 'Built-in'; }
|
||||
|
||||
function FTUESetMusicInput() { dbg('FTUESetMusicInput'); }
|
||||
function FTUESetChatInput() { dbg('FTUESetChatInput'); }
|
||||
function FTUESetMusicOutput() { dbg('FTUESetMusicOutput'); }
|
||||
|
|
@ -250,6 +343,19 @@
|
|||
eval(js);
|
||||
}
|
||||
|
||||
function IsMyNetworkWireless() {
|
||||
// 1=true, 0 = false, -1=unknown
|
||||
return 1;
|
||||
}
|
||||
|
||||
function SetNetworkTestScore(numClients) {
|
||||
|
||||
}
|
||||
|
||||
function GetNetworkTestScore() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
function GetASIODevices() {
|
||||
var response =[{"device_id":0,"device_name":"Realtek High Definition Audio","device_type": 0,"interfaces":[{"interface_id":0,"interface_name":"Realtek HDA SPDIF Out","pins":[{"is_input":false,"pin_id":0,"pin_name":"PC Speaker"}]},{"interface_id":1,"interface_name":"Realtek HD Audio rear output","pins":[{"is_input":false,"pin_id":0,"pin_name":"PC Speaker"}]},{"interface_id":2,"interface_name":"Realtek HD Audio Mic input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]},{"interface_id":3,"interface_name":"Realtek HD Audio Line input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]},{"interface_id":4,"interface_name":"Realtek HD Digital input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"}]},{"interface_id":5,"interface_name":"Realtek HD Audio Stereo input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]}],"wavert_supported":false},{"device_id":1,"device_name":"M-Audio FW Audiophile","device_type": 1,"interfaces":[{"interface_id":0,"interface_name":"FW AP Multi","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":1,"interface_name":"FW AP 1/2","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":2,"interface_name":"FW AP SPDIF","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":3,"interface_name":"FW AP 3/4","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"}]}],"wavert_supported":false},{"device_id":2,"device_name":"Virtual Audio Cable","device_type": 2,"interfaces":[{"interface_id":0,"interface_name":"Virtual Cable 2","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"},{"is_input":false,"pin_id":1,"pin_name":"Output"}]},{"interface_id":1,"interface_name":"Virtual Cable 1","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"},{"is_input":false,"pin_id":1,"pin_name":"Output"}]}],"wavert_supported":false},{"device_id":3,"device_name":"WebCamDV WDM Audio Capture","device_type": 3,"interfaces":[{"interface_id":0,"interface_name":"WebCamDV Audio","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"},{"is_input":false,"pin_id":1,"pin_name":"Volume Control"}]}],"wavert_supported":false}];
|
||||
return response;
|
||||
|
|
@ -351,6 +457,8 @@
|
|||
function SessionStartRecording() {}
|
||||
function SessionStopPlay() {}
|
||||
function SessionStopRecording() {}
|
||||
function SessionAddPlayTrack() {return true;}
|
||||
function SessionRemoveAllPlayTracks(){}
|
||||
function isSessionTrackPlaying() { return false; }
|
||||
function SessionCurrrentPlayPosMs() { return 0; }
|
||||
function SessionGetTracksPlayDurationMs() { return 0; }
|
||||
|
|
@ -580,16 +688,51 @@
|
|||
fakeJamClientRecordings = fakeRecordingsImpl;
|
||||
}
|
||||
|
||||
function OnLoggedIn(userId, sessionToken) {
|
||||
function TestNetworkPktBwRate(targetClientId, successCallback, timeoutCallback, testType, duration, numClients, payloadSize) {
|
||||
var progress = {progress:true,
|
||||
upthroughput:.95,
|
||||
downthroughput:.95,
|
||||
upjitter: 2.3,
|
||||
downjitter: 2.3
|
||||
}
|
||||
var count = 0;
|
||||
var interval = setInterval(function() {
|
||||
|
||||
eval(successCallback + "(" + JSON.stringify(progress) + ");");
|
||||
|
||||
if(progress.upthroughput < 1) {
|
||||
progress.upthroughput += .05;
|
||||
}
|
||||
if(progress.downthroughput < 1) {
|
||||
progress.downthroughput += .05;
|
||||
}
|
||||
|
||||
function OnLoggedOut() {
|
||||
count++;
|
||||
if(count == duration) {
|
||||
clearInterval(interval);
|
||||
|
||||
delete progress['progress']
|
||||
progress.pass = true;
|
||||
eval(successCallback + "(" + JSON.stringify(progress) + ");");
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
function StopNetworkTest(targetClientId) {}
|
||||
function OnLoggedIn(userId, sessionToken) {}
|
||||
function OnLoggedOut() {}
|
||||
function UserAttention(option) {}
|
||||
|
||||
function log(level, message) {
|
||||
console.log("beep : " + message)
|
||||
}
|
||||
|
||||
function UserAttention(option) {
|
||||
|
||||
function getOperatingMode() {
|
||||
if (location.pathname == '/latency_tester') {
|
||||
return 'server';
|
||||
}
|
||||
else {
|
||||
return 'client';
|
||||
}
|
||||
}
|
||||
|
||||
// passed an array of recording objects from the server
|
||||
|
|
@ -653,13 +796,27 @@
|
|||
this.StopRecording = StopRecording;
|
||||
this.TestASIOLatency = TestASIOLatency;
|
||||
this.TestLatency = TestLatency;
|
||||
this.IsMyNetworkWireless = IsMyNetworkWireless;
|
||||
this.SetNetworkTestScore = SetNetworkTestScore;
|
||||
this.GetNetworkTestScore = GetNetworkTestScore;
|
||||
this.connected = true;
|
||||
|
||||
// FTUE (round 3)
|
||||
this.FTUESetInputMusicDevice = FTUESetInputMusicDevice;
|
||||
this.FTUESetOutputMusicDevice = FTUESetOutputMusicDevice;
|
||||
this.FTUEGetInputMusicDevice = FTUEGetInputMusicDevice;
|
||||
this.FTUEGetOutputMusicDevice = FTUEGetOutputMusicDevice;
|
||||
this.FTUEGetChatInputVolume = FTUEGetChatInputVolume;
|
||||
this.FTUEGetChatInputs = FTUEGetChatInputs;
|
||||
this.FTUEGetChannels = FTUEGetChannels;
|
||||
this.FTUEGetAudioDevices = FTUEGetAudioDevices;
|
||||
this.FTUEStartIoPerfTest = FTUEStartIoPerfTest;
|
||||
this.FTUEGetIoPerfData = FTUEGetIoPerfData;
|
||||
this.FTUEGetDevices = FTUEGetDevices;
|
||||
this.FTUEGetFrameSize = FTUEGetFrameSize;
|
||||
this.FTUECancel = FTUECancel;
|
||||
this.FTUEGetMusicProfileName = FTUEGetMusicProfileName;
|
||||
this.FTUESetMusicProfileName = FTUESetMusicProfileName;
|
||||
this.FTUEGetInputLatency = FTUEGetInputLatency;
|
||||
this.FTUEGetInputVolume = FTUEGetInputVolume;
|
||||
this.FTUEGetMusicInputs = FTUEGetMusicInputs;
|
||||
|
|
@ -708,6 +865,8 @@
|
|||
this.SessionStartPlay = SessionStartPlay;
|
||||
this.SessionStartRecording = SessionStartRecording;
|
||||
this.SessionStopPlay = SessionStopPlay;
|
||||
this.SessionAddPlayTrack = SessionAddPlayTrack;
|
||||
this.SessionRemoveAllPlayTracks = SessionRemoveAllPlayTracks;
|
||||
this.SessionStopRecording = SessionStopRecording;
|
||||
this.isSessionTrackPlaying = isSessionTrackPlaying;
|
||||
this.SessionCurrrentPlayPosMs = SessionCurrrentPlayPosMs;
|
||||
|
|
@ -780,6 +939,13 @@
|
|||
// fake calls; not a part of the actual jam client
|
||||
this.RegisterP2PMessageCallbacks = RegisterP2PMessageCallbacks;
|
||||
this.SetFakeRecordingImpl = SetFakeRecordingImpl;
|
||||
|
||||
// network test
|
||||
this.TestNetworkPktBwRate = TestNetworkPktBwRate;
|
||||
this.StopNetworkTest = StopNetworkTest;
|
||||
this.log = log;
|
||||
this.getOperatingMode = getOperatingMode;
|
||||
this.clientID = "devtester";
|
||||
};
|
||||
|
||||
})(window,jQuery);
|
||||
|
|
@ -97,7 +97,7 @@
|
|||
for (var kk=0, klen=iter_pinstruments.length; kk<klen; kk++) {
|
||||
var pinstr = iter_pinstruments[kk];
|
||||
if (pinstr in instrument_logo_map) {
|
||||
instr = instrument_logo_map[pinstr];
|
||||
instr = instrument_logo_map[pinstr].asset;
|
||||
}
|
||||
player_instrs += '<img src="' + instr + '"/>';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,14 +41,10 @@
|
|||
queryString += "instrument=" + instrument + '&';
|
||||
}
|
||||
|
||||
// distance filter
|
||||
var query_param = $('#musician_query_distance').val();
|
||||
if (query_param !== null && query_param.length > 0) {
|
||||
var matches = query_param.match(/(\d+)/);
|
||||
if (0 < matches.length) {
|
||||
var distance = matches[0];
|
||||
queryString += "distance=" + distance + '&';
|
||||
}
|
||||
// score filter
|
||||
var query_param = $('#musician_query_score').val();
|
||||
if (query_param !== null) {
|
||||
queryString += "score_limit=" + query_param + '&';
|
||||
}
|
||||
loadMusicians(queryString);
|
||||
}
|
||||
|
|
@ -100,7 +96,7 @@
|
|||
instr_logos = '';
|
||||
for (var jj=0, ilen=musician['instruments'].length; jj<ilen; jj++) {
|
||||
if (musician['instruments'][jj].instrument_id in instrument_logo_map) {
|
||||
instr = instrument_logo_map[musician['instruments'][jj].instrument_id];
|
||||
instr = instrument_logo_map[musician['instruments'][jj].instrument_id].asset;
|
||||
}
|
||||
instr_logos += '<img src="' + instr + '"/>';
|
||||
}
|
||||
|
|
@ -228,7 +224,7 @@
|
|||
}
|
||||
|
||||
function events() {
|
||||
$('#musician_query_distance').change(refreshDisplay);
|
||||
$('#musician_query_score').change(refreshDisplay);
|
||||
$('#musician_instrument').change(refreshDisplay);
|
||||
$('#musician_order_by').change(refreshDisplay);
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,8 @@
|
|||
register : "Register",
|
||||
download : "DownloadClient",
|
||||
audioTest : "AudioTest",
|
||||
trackConfig : "AudioTrackConfig",
|
||||
networkTest : "NetworkTest",
|
||||
sessionCount : "SessionCount",
|
||||
sessionMusicians : "SessionMusicians",
|
||||
sessionQuality : "SessionQuality",
|
||||
|
|
@ -281,6 +283,23 @@
|
|||
context.ga('send', 'event', category, target, data);
|
||||
}
|
||||
|
||||
function trackNetworkTest(platform, numUsers) {
|
||||
var normalizedPlatform = translatePlatformForGA(platform);
|
||||
|
||||
context.ga('send', 'event', categories.networkTest, 'Passed', normalizedPlatform, numUsers);
|
||||
}
|
||||
|
||||
function trackAudioTestCompletion(platform) {
|
||||
var normalizedPlatform = translatePlatformForGA(platform);
|
||||
|
||||
context.ga('send', 'event', categories.audioTest, 'Passed', normalizedPlatform);
|
||||
}
|
||||
|
||||
function trackConfigureTracksCompletion(platform) {
|
||||
var normalizedPlatform = translatePlatformForGA(platform);
|
||||
|
||||
context.ga('send', 'event', categories.trackConfig, 'Passed', normalizedPlatform);
|
||||
}
|
||||
|
||||
var GA = {};
|
||||
GA.Categories = categories;
|
||||
|
|
@ -294,6 +313,9 @@
|
|||
GA.trackRegister = trackRegister;
|
||||
GA.trackDownload = trackDownload;
|
||||
GA.trackFTUECompletion = trackFTUECompletion;
|
||||
GA.trackNetworkTest = trackNetworkTest;
|
||||
GA.trackAudioTestCompletion = trackAudioTestCompletion;
|
||||
GA.trackConfigureTracksCompletion = trackConfigureTracksCompletion;
|
||||
GA.trackSessionCount = trackSessionCount;
|
||||
GA.trackSessionMusicians = trackSessionMusicians;
|
||||
GA.trackSessionQuality = trackSessionQuality;
|
||||
|
|
|
|||
|
|
@ -1,909 +0,0 @@
|
|||
(function (context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.GearWizard = function (app) {
|
||||
|
||||
var ASSIGNMENT = context.JK.ASSIGNMENT;
|
||||
var VOICE_CHAT = context.JK.VOICE_CHAT;
|
||||
|
||||
var $dialog = null;
|
||||
var $wizardSteps = null;
|
||||
var $currentWizardStep = null;
|
||||
var step = 0;
|
||||
var $templateSteps = null;
|
||||
var $templateButtons = null;
|
||||
var $templateAudioPort = null;
|
||||
var $ftueButtons = null;
|
||||
var self = null;
|
||||
var operatingSystem = null;
|
||||
|
||||
// populated by loadDevices
|
||||
var deviceInformation = null;
|
||||
var musicPorts = null;
|
||||
|
||||
|
||||
var validLatencyScore = false;
|
||||
var validIOScore = false;
|
||||
|
||||
// SELECT TRACKS STATE
|
||||
|
||||
var TOTAL_STEPS = 7;
|
||||
var STEP_INTRO = 0;
|
||||
var STEP_SELECT_DEVICE = 1;
|
||||
var STEP_SELECT_TRACKS = 2;
|
||||
var STEP_SELECT_CHAT = 3;
|
||||
var STEP_DIRECT_MONITOR = 4;
|
||||
var STEP_ROUTER_NETWORK = 5;
|
||||
var STEP_SUCCESS = 6;
|
||||
|
||||
var PROFILE_DEV_SEP_TOKEN = '^';
|
||||
|
||||
var iCheckIgnore = false;
|
||||
|
||||
var audioDeviceBehavior = {
|
||||
MacOSX_builtin: {
|
||||
display: 'MacOSX Built-In',
|
||||
videoURL: undefined
|
||||
},
|
||||
MacOSX_interface: {
|
||||
display: 'MacOSX external interface',
|
||||
videoURL: undefined
|
||||
},
|
||||
Win32_wdm: {
|
||||
display: 'Windows WDM',
|
||||
videoURL: undefined
|
||||
},
|
||||
Win32_asio: {
|
||||
display: 'Windows ASIO',
|
||||
videoURL: undefined
|
||||
},
|
||||
Win32_asio4all: {
|
||||
display: 'Windows ASIO4ALL',
|
||||
videoURL: undefined
|
||||
},
|
||||
Linux: {
|
||||
display: 'Linux',
|
||||
videoURL: undefined
|
||||
}
|
||||
}
|
||||
|
||||
function beforeShowIntro() {
|
||||
var $watchVideo = $currentWizardStep.find('.watch-video');
|
||||
var videoUrl = 'https://www.youtube.com/watch?v=VexH4834o9I';
|
||||
if (operatingSystem == "Win32") {
|
||||
$watchVideo.attr('href', 'https://www.youtube.com/watch?v=VexH4834o9I');
|
||||
}
|
||||
$watchVideo.attr('href', videoUrl);
|
||||
}
|
||||
|
||||
function beforeSelectDevice() {
|
||||
|
||||
var $watchVideoInput = $currentWizardStep.find('.watch-video.audio-input');
|
||||
var $watchVideoOutput = $currentWizardStep.find('.watch-video.audio-output');
|
||||
var $audioInput = $currentWizardStep.find('.select-audio-input-device');
|
||||
var $audioOutput = $currentWizardStep.find('.select-audio-output-device');
|
||||
var $bufferIn = $currentWizardStep.find('.select-buffer-in');
|
||||
var $bufferOut = $currentWizardStep.find('.select-buffer-out');
|
||||
var $frameSize = $currentWizardStep.find('.select-frame-size');
|
||||
var $inputChannels = $currentWizardStep.find('.input-ports');
|
||||
var $outputChannels = $currentWizardStep.find('.output-ports');
|
||||
var $scoreReport = $currentWizardStep.find('.results');
|
||||
var $latencyScoreSection = $scoreReport.find('.latency-score-section');
|
||||
var $latencyScore = $scoreReport.find('.latency-score');
|
||||
var $ioScoreSection = $scoreReport.find('.io-score-section');
|
||||
var $ioRateScore = $scoreReport.find('.io-rate-score');
|
||||
var $ioVarScore = $scoreReport.find('.io-var-score');
|
||||
var $ioCountdown = $scoreReport.find('.io-countdown');
|
||||
var $ioCountdownSecs = $scoreReport.find('.io-countdown .secs');
|
||||
var $nextButton = $ftueButtons.find('.btn-next');
|
||||
var $asioControlPanelBtn = $currentWizardStep.find('.asio-settings-btn');
|
||||
var $resyncBtn = $currentWizardStep.find('resync-btn')
|
||||
|
||||
// should return one of:
|
||||
// * MacOSX_builtin
|
||||
// * MACOSX_interface
|
||||
// * Win32_wdm
|
||||
// * Win32_asio
|
||||
// * Win32_asio4all
|
||||
// * Linux
|
||||
function determineDeviceType(deviceId, displayName) {
|
||||
if (operatingSystem == "MacOSX") {
|
||||
if (displayName.toLowerCase().trim() == "built-in") {
|
||||
return "MacOSX_builtin";
|
||||
}
|
||||
else {
|
||||
return "MacOSX_interface";
|
||||
}
|
||||
}
|
||||
else if (operatingSystem == "Win32") {
|
||||
if (context.jamClient.FTUEIsMusicDeviceWDM(deviceId)) {
|
||||
return "Win32_wdm";
|
||||
}
|
||||
else if (displayName.toLowerCase().indexOf("asio4all") > -1) {
|
||||
return "Win32_asio4all"
|
||||
}
|
||||
else {
|
||||
return "Win32_asio";
|
||||
}
|
||||
}
|
||||
else {
|
||||
return "Linux";
|
||||
}
|
||||
}
|
||||
|
||||
function loadDevices() {
|
||||
|
||||
var oldDevices = context.jamClient.FTUEGetDevices(false);
|
||||
var devices = context.jamClient.FTUEGetAudioDevices();
|
||||
console.log("oldDevices: " + JSON.stringify(oldDevices));
|
||||
console.log("devices: " + JSON.stringify(devices));
|
||||
|
||||
var loadedDevices = {};
|
||||
|
||||
// augment these devices by determining their type
|
||||
context._.each(devices.devices, function (device) {
|
||||
|
||||
if(device.name == "JamKazam Virtual Monitor") {
|
||||
return;
|
||||
}
|
||||
|
||||
var deviceInfo = {};
|
||||
|
||||
deviceInfo.id = device.guid;
|
||||
deviceInfo.type = determineDeviceType(device.guid, device.display_name);
|
||||
console.log("deviceInfo.type: " + deviceInfo.type)
|
||||
deviceInfo.displayType = audioDeviceBehavior[deviceInfo.type].display;
|
||||
deviceInfo.displayName = device.display_name;
|
||||
|
||||
loadedDevices[device.guid] = deviceInfo;
|
||||
|
||||
logger.debug("loaded device: ", deviceInfo);
|
||||
})
|
||||
|
||||
deviceInformation = loadedDevices;
|
||||
|
||||
logger.debug(context.JK.dlen(deviceInformation) + " devices loaded.", deviceInformation);
|
||||
}
|
||||
|
||||
// returns a deviceInfo hash for the device matching the deviceId, or undefined.
|
||||
function findDevice(deviceId) {
|
||||
return deviceInformation[deviceId];
|
||||
}
|
||||
|
||||
function selectedAudioInput() {
|
||||
return $audioInput.val();
|
||||
}
|
||||
|
||||
function selectedAudioOutput() {
|
||||
return $audioOutput.val();
|
||||
}
|
||||
|
||||
function selectedFramesize() {
|
||||
return parseFloat($frameSize.val());
|
||||
}
|
||||
|
||||
function selectedBufferIn() {
|
||||
return parseFloat($frameSize.val());
|
||||
}
|
||||
|
||||
function selectedBufferOut() {
|
||||
return parseFloat($frameSize.val());
|
||||
}
|
||||
|
||||
function initializeNextButtonState() {
|
||||
$nextButton.removeClass('button-orange button-grey');
|
||||
|
||||
if (validLatencyScore) $nextButton.addClass('button-orange');
|
||||
else $nextButton.addClass('button-grey');
|
||||
}
|
||||
|
||||
function initializeAudioInput() {
|
||||
var optionsHtml = '';
|
||||
optionsHtml = '<option selected="selected" value="">Choose...</option>';
|
||||
context._.each(deviceInformation, function (deviceInfo, deviceId) {
|
||||
|
||||
console.log(arguments)
|
||||
optionsHtml += '<option title="' + deviceInfo.displayName + '" value="' + deviceId + '">' + deviceInfo.displayName + '</option>';
|
||||
});
|
||||
$audioInput.html(optionsHtml);
|
||||
context.JK.dropdown($audioInput);
|
||||
|
||||
initializeAudioInputChanged();
|
||||
}
|
||||
|
||||
function initializeAudioOutput() {
|
||||
var optionsHtml = '';
|
||||
optionsHtml = '<option selected="selected" value="">Same as Input...</option>';
|
||||
context._.each(deviceInformation, function (deviceInfo, deviceId) {
|
||||
optionsHtml += '<option title="' + deviceInfo.displayName + '" value="' + deviceId + '">' + deviceInfo.displayName + '</option>';
|
||||
});
|
||||
$audioOutput.html(optionsHtml);
|
||||
context.JK.dropdown($audioOutput);
|
||||
|
||||
initializeAudioOutputChanged();
|
||||
}
|
||||
|
||||
function initializeFramesize() {
|
||||
context.JK.dropdown($frameSize);
|
||||
}
|
||||
|
||||
function initializeBuffers() {
|
||||
context.JK.dropdown($bufferIn);
|
||||
context.JK.dropdown($bufferOut);
|
||||
}
|
||||
|
||||
|
||||
// reloads the backend's channel state for the currently selected audio devices,
|
||||
// and update's the UI accordingly
|
||||
function initializeChannels() {
|
||||
musicPorts = jamClient.FTUEGetChannels();
|
||||
console.log("musicPorts: %o", JSON.stringify(musicPorts));
|
||||
|
||||
initializeInputPorts(musicPorts);
|
||||
initializeOutputPorts(musicPorts);
|
||||
}
|
||||
|
||||
// during this phase of the FTUE, we have to assign selected input channels
|
||||
// to tracks. The user, however, does not have a way to indicate which channel
|
||||
// goes to which track (that's not until the next step of the wizard).
|
||||
// so, we just auto-generate a valid assignment
|
||||
function newInputAssignment() {
|
||||
var assigned = 0;
|
||||
context._.each(musicPorts.inputs, function(inputChannel) {
|
||||
if(isChannelAssigned(inputChannel)) {
|
||||
assigned += 1;
|
||||
}
|
||||
});
|
||||
|
||||
var newAssignment = Math.floor(assigned / 2) + 1;
|
||||
return newAssignment;
|
||||
}
|
||||
|
||||
function inputChannelChanged() {
|
||||
if(iCheckIgnore) return;
|
||||
|
||||
var $checkbox = $(this);
|
||||
var channelId = $checkbox.attr('data-id');
|
||||
var isChecked = $checkbox.is(':checked');
|
||||
|
||||
if(isChecked) {
|
||||
var newAssignment = newInputAssignment();
|
||||
logger.debug("assigning input channel %o to track: %o", channelId, newAssignment);
|
||||
context.jamClient.TrackSetAssignment(channelId, true, newAssignment);
|
||||
}
|
||||
else {
|
||||
logger.debug("unassigning input channel %o", channelId);
|
||||
context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.UNASSIGNED);
|
||||
// unassigning creates a hole in our auto-assigned tracks. reassign them all to keep it consistent
|
||||
var $assignedInputs = $inputChannels.find('input[type="checkbox"]:checked');
|
||||
var assigned = 0;
|
||||
context._.each($assignedInputs, function(assignedInput) {
|
||||
var $assignedInput = $(assignedInput);
|
||||
var assignedChannelId = $assignedInput.attr('data-id');
|
||||
var newAssignment = Math.floor(assigned / 2) + 1;
|
||||
logger.debug("re-assigning input channel %o to track: %o", assignedChannelId, newAssignment);
|
||||
context.jamClient.TrackSetAssignment(assignedChannelId, true, newAssignment);
|
||||
assigned += 1;
|
||||
});
|
||||
}
|
||||
|
||||
initializeChannels();
|
||||
}
|
||||
|
||||
// should be called in a ifChanged callback if you want to cancel.
|
||||
// you have to use this instead of 'return false' like a typical input 'change' event.
|
||||
function cancelICheckChange($checkbox) {
|
||||
iCheckIgnore = true;
|
||||
var checked = $checkbox.is(':checked');
|
||||
setTimeout(function() {
|
||||
if(checked) $checkbox.iCheck('uncheck').removeAttr('checked');
|
||||
else $checkbox.iCheck('check').attr('checked', 'checked');
|
||||
iCheckIgnore = false;
|
||||
}, 1);
|
||||
}
|
||||
|
||||
function outputChannelChanged() {
|
||||
if(iCheckIgnore) return;
|
||||
var $checkbox = $(this);
|
||||
var channelId = $checkbox.attr('data-id');
|
||||
var isChecked = $checkbox.is(':checked');
|
||||
|
||||
// don't allow more than 2 output channels selected at once
|
||||
if($outputChannels.find('input[type="checkbox"]:checked').length > 2) {
|
||||
context.JK.Banner.showAlert('You can only have a maximum of 2 output ports selected.');
|
||||
// can't allow uncheck of last output
|
||||
cancelICheckChange($checkbox);
|
||||
return;
|
||||
}
|
||||
|
||||
if(isChecked) {
|
||||
logger.debug("assigning output channel %o", channelId);
|
||||
context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.OUTPUT);
|
||||
}
|
||||
else {
|
||||
logger.debug("unassigning output channel %o", channelId);
|
||||
context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.UNASSIGNED);
|
||||
}
|
||||
|
||||
initializeChannels();
|
||||
}
|
||||
|
||||
// checks if it's an assigned OUTPUT or ASSIGNED CHAT
|
||||
function isChannelAssigned(channel) {
|
||||
return channel.assignment == ASSIGNMENT.CHAT || channel.assignment == ASSIGNMENT.OUTPUT || channel.assignment > 0;
|
||||
}
|
||||
|
||||
function initializeInputPorts(musicPorts) {
|
||||
$inputChannels.empty();
|
||||
var inputPorts = musicPorts.inputs;
|
||||
context._.each(inputPorts, function(inputChannel) {
|
||||
var $inputChannel = $(context._.template($templateAudioPort.html(), inputChannel, { variable: 'data' }));
|
||||
var $checkbox = $inputChannel.find('input');
|
||||
if(isChannelAssigned(inputChannel)) {
|
||||
$checkbox.attr('checked', 'checked');
|
||||
}
|
||||
context.JK.checkbox($checkbox);
|
||||
$checkbox.on('ifChanged', inputChannelChanged);
|
||||
$inputChannels.append($inputChannel);
|
||||
});
|
||||
}
|
||||
|
||||
function initializeOutputPorts(musicPorts) {
|
||||
$outputChannels.empty();
|
||||
var outputChannels = musicPorts.outputs;
|
||||
context._.each(outputChannels, function(outputChannel) {
|
||||
var $outputPort = $(context._.template($templateAudioPort.html(), outputChannel, { variable: 'data' }));
|
||||
var $checkbox = $outputPort.find('input');
|
||||
if(isChannelAssigned(outputChannel)) {
|
||||
$checkbox.attr('checked', 'checked');
|
||||
}
|
||||
context.JK.checkbox($checkbox);
|
||||
$checkbox.on('ifChanged', outputChannelChanged);
|
||||
$outputChannels.append($outputPort);
|
||||
});
|
||||
}
|
||||
|
||||
function initializeFormElements() {
|
||||
if (!deviceInformation) throw "devices are not initialized";
|
||||
|
||||
initializeAudioInput();
|
||||
initializeAudioOutput();
|
||||
initializeFramesize();
|
||||
initializeBuffers();
|
||||
}
|
||||
|
||||
function resetFrameBuffers() {
|
||||
$frameSize.val('2.5');
|
||||
$bufferIn.val('0');
|
||||
$bufferOut.val('0');
|
||||
}
|
||||
|
||||
function clearInputPorts() {
|
||||
$inputChannels.empty();
|
||||
}
|
||||
|
||||
function clearOutputPorts() {
|
||||
$outputChannels.empty();
|
||||
}
|
||||
|
||||
function resetScoreReport() {
|
||||
$ioRateScore.empty();
|
||||
$ioVarScore.empty();
|
||||
$latencyScore.empty();
|
||||
}
|
||||
|
||||
function renderLatencyScore(latencyValue, latencyClass) {
|
||||
if(latencyValue) {
|
||||
$latencyScore.text(latencyValue + ' ms');
|
||||
}
|
||||
else {
|
||||
$latencyScore.text('');
|
||||
}
|
||||
$latencyScoreSection.removeClass('good acceptable bad unknown starting').addClass(latencyClass);
|
||||
}
|
||||
|
||||
// std deviation is the worst value between in/out
|
||||
// media is the worst value between in/out
|
||||
// io is the value returned by the backend, which has more info
|
||||
// ioClass is the pre-computed rollup class describing the result in simple terms of 'good', 'acceptable', bad'
|
||||
function renderIOScore(std, median, ioData, ioClass) {
|
||||
$ioRateScore.text(median ? median : '');
|
||||
$ioVarScore.text(std ? std : '');
|
||||
$ioScoreSection.removeClass('good acceptable bad unknown starting skip').addClass(ioClass);
|
||||
// TODO: show help bubble of all data in IO data
|
||||
}
|
||||
|
||||
function updateScoreReport(latencyResult) {
|
||||
var latencyClass = "neutral";
|
||||
var latencyValue = 'N/A';
|
||||
var validLatency = false;
|
||||
if (latencyResult && latencyResult.latencyknown) {
|
||||
var latencyValue = latencyResult.latency;
|
||||
latencyValue = Math.round(latencyValue * 100) / 100;
|
||||
if (latencyValue <= 10) {
|
||||
latencyClass = "good";
|
||||
validLatency = true;
|
||||
} else if (latencyValue <= 20) {
|
||||
latencyClass = "acceptable";
|
||||
validLatency = true;
|
||||
} else {
|
||||
latencyClass = "bad";
|
||||
}
|
||||
}
|
||||
else {
|
||||
latencyClass = 'unknown';
|
||||
}
|
||||
|
||||
validLatencyScore = validLatency;
|
||||
|
||||
renderLatencyScore(latencyValue, latencyClass);
|
||||
}
|
||||
|
||||
function audioInputDeviceUnselected() {
|
||||
validLatencyScore = false;
|
||||
initializeNextButtonState();
|
||||
resetFrameBuffers();
|
||||
clearInputPorts();
|
||||
}
|
||||
|
||||
function renderScoringStarted() {
|
||||
validLatencyScore = false;
|
||||
initializeNextButtonState();
|
||||
resetScoreReport();
|
||||
freezeAudioInteraction();
|
||||
renderLatencyScore(null, 'starting');
|
||||
}
|
||||
|
||||
function renderScoringStopped() {
|
||||
initializeNextButtonState();
|
||||
unfreezeAudioInteraction();
|
||||
}
|
||||
|
||||
|
||||
function freezeAudioInteraction() {
|
||||
$audioInput.attr("disabled", "disabled").easyDropDown('disable');
|
||||
$audioOutput.attr("disabled", "disabled").easyDropDown('disable');
|
||||
$frameSize.attr("disabled", "disabled").easyDropDown('disable');
|
||||
$bufferIn.attr("disabled", "disabled").easyDropDown('disable');
|
||||
$bufferOut.attr("disabled", "disabled").easyDropDown('disable');
|
||||
$asioControlPanelBtn.on("click", false);
|
||||
$resyncBtn.on('click', false);
|
||||
iCheckIgnore = true;
|
||||
$inputChannels.find('input[type="checkbox"]').iCheck('disable');
|
||||
$outputChannels.find('input[type="checkbox"]').iCheck('disable');
|
||||
}
|
||||
|
||||
function unfreezeAudioInteraction() {
|
||||
$audioInput.removeAttr("disabled").easyDropDown('enable');
|
||||
$audioOutput.removeAttr("disabled").easyDropDown('enable');
|
||||
$frameSize.removeAttr("disabled").easyDropDown('enable');
|
||||
$bufferIn.removeAttr("disabled").easyDropDown('enable');
|
||||
$bufferOut.removeAttr("disabled").easyDropDown('enable');
|
||||
$asioControlPanelBtn.off("click", false);
|
||||
$resyncBtn.off('click', false);
|
||||
$inputChannels.find('input[type="checkbox"]').iCheck('enable');
|
||||
$outputChannels.find('input[type="checkbox"]').iCheck('enable');
|
||||
iCheckIgnore = false;
|
||||
}
|
||||
|
||||
// Given a latency structure, update the view.
|
||||
function newFtueUpdateLatencyView(latency) {
|
||||
var $report = $('.ftue-new .latency .report');
|
||||
var $instructions = $('.ftue-new .latency .instructions');
|
||||
var latencyClass = "neutral";
|
||||
var latencyValue = "N/A";
|
||||
var $saveButton = $('#btn-ftue-2-save');
|
||||
if (latency && latency.latencyknown) {
|
||||
latencyValue = latency.latency;
|
||||
// Round latency to two decimal places.
|
||||
latencyValue = Math.round(latencyValue * 100) / 100;
|
||||
if (latency.latency <= 10) {
|
||||
latencyClass = "good";
|
||||
setSaveButtonState($saveButton, true);
|
||||
} else if (latency.latency <= 20) {
|
||||
latencyClass = "acceptable";
|
||||
setSaveButtonState($saveButton, true);
|
||||
} else {
|
||||
latencyClass = "bad";
|
||||
setSaveButtonState($saveButton, false);
|
||||
}
|
||||
} else {
|
||||
latencyClass = "unknown";
|
||||
setSaveButtonState($saveButton, false);
|
||||
}
|
||||
|
||||
$('.ms-label', $report).html(latencyValue);
|
||||
$('p', $report).html('milliseconds');
|
||||
|
||||
$report.removeClass('good acceptable bad unknown');
|
||||
$report.addClass(latencyClass);
|
||||
|
||||
var instructionClasses = ['neutral', 'good', 'acceptable', 'unknown', 'bad', 'start', 'loading'];
|
||||
$.each(instructionClasses, function (idx, val) {
|
||||
$('p.' + val, $instructions).hide();
|
||||
});
|
||||
if (latency === 'loading') {
|
||||
$('p.loading', $instructions).show();
|
||||
} else {
|
||||
$('p.' + latencyClass, $instructions).show();
|
||||
renderStopNewFtueLatencyTesting();
|
||||
}
|
||||
}
|
||||
|
||||
function initializeWatchVideo() {
|
||||
$watchVideoInput.unbind('click').click(function () {
|
||||
|
||||
var audioDevice = findDevice(selectedAudioInput());
|
||||
if (!audioDevice) {
|
||||
context.JK.Banner.showAlert('You must first choose an Audio Input Device so that we can determine which video to show you.');
|
||||
}
|
||||
else {
|
||||
var videoURL = audioDeviceBehavior[audioDevice.type].videoURL;
|
||||
|
||||
if (videoURL) {
|
||||
$(this).attr('href', videoURL);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
context.JK.Banner.showAlert('No help video for this type of device (' + audioDevice.displayType + ')');
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$watchVideoOutput.unbind('click').click(function () {
|
||||
|
||||
var audioDevice = findDevice(selectedAudioOutput());
|
||||
if (!audioDevice) {
|
||||
throw "this button should be hidden";
|
||||
}
|
||||
else {
|
||||
var videoURL = audioDeviceBehavior[audioDevice.type].videoURL;
|
||||
|
||||
if (videoURL) {
|
||||
$(this).attr('href', videoURL);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
context.JK.Banner.showAlert('No help video for this type of device (' + audioDevice.displayType + ')');
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function renderIOScoringStarted(secondsLeft) {
|
||||
$ioCountdownSecs.text(secondsLeft);
|
||||
$ioCountdown.show();
|
||||
}
|
||||
|
||||
function renderIOScoringStopped() {
|
||||
$ioCountdown.hide();
|
||||
}
|
||||
|
||||
function renderIOCountdown(secondsLeft) {
|
||||
$ioCountdownSecs.text(secondsLeft);
|
||||
}
|
||||
|
||||
function attemptScore() {
|
||||
var audioInputDeviceId = selectedAudioInput();
|
||||
var audioOutputDeviceId = selectedAudioOutput();
|
||||
if (!audioInputDeviceId) {
|
||||
audioInputDeviceUnselected();
|
||||
return false;
|
||||
}
|
||||
|
||||
var audioInputDevice = findDevice(audioInputDeviceId);
|
||||
if (!audioInputDevice) {
|
||||
context.JK.alertSupportedNeeded('Unable to find information for input device: ' + audioInputDeviceId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!audioOutputDeviceId) {
|
||||
audioOutputDeviceId = audioInputDeviceId;
|
||||
}
|
||||
var audioOutputDevice = findDevice(audioOutputDeviceId);
|
||||
if (!audioInputDevice) {
|
||||
context.JK.alertSupportedNeeded('Unable to find information for output device: ' + audioOutputDeviceId);
|
||||
return false;
|
||||
}
|
||||
|
||||
jamClient.FTUESetInputMusicDevice(audioInputDeviceId);
|
||||
jamClient.FTUESetOutputMusicDevice(audioOutputDeviceId);
|
||||
|
||||
initializeChannels();
|
||||
|
||||
jamClient.FTUESetInputLatency(selectedBufferIn());
|
||||
jamClient.FTUESetOutputLatency(selectedBufferOut());
|
||||
jamClient.FTUESetFrameSize(selectedFramesize());
|
||||
|
||||
renderScoringStarted();
|
||||
logger.debug("Calling FTUESave(false)");
|
||||
jamClient.FTUESave(false);
|
||||
|
||||
var latency = jamClient.FTUEGetExpectedLatency();
|
||||
console.log("FTUEGetExpectedLatency: %o", latency);
|
||||
|
||||
updateScoreReport(latency);
|
||||
|
||||
// if there was a valid latency score, go on to the next step
|
||||
if(validLatencyScore) {
|
||||
renderIOScore(null, null, null, 'starting');
|
||||
var testTimeSeconds = 10; // allow 10 seconds for IO to establish itself
|
||||
context.jamClient.FTUEStartIoPerfTest();
|
||||
renderIOScoringStarted(testTimeSeconds);
|
||||
renderIOCountdown(testTimeSeconds);
|
||||
var interval = setInterval(function() {
|
||||
testTimeSeconds -= 1;
|
||||
renderIOCountdown(testTimeSeconds);
|
||||
if(testTimeSeconds == 0) {
|
||||
clearInterval(interval);
|
||||
renderIOScoringStopped();
|
||||
var io = context.jamClient.FTUEGetIoPerfData();
|
||||
|
||||
console.log("io: ", io);
|
||||
|
||||
// take the higher variance, which is apparently actually std dev
|
||||
var std = io.in_var > io.out_var ? io.in_var : io.out_var;
|
||||
std = Math.round(std * 100) / 100;
|
||||
// take the furthest-off-from-target io rate
|
||||
var median = Math.abs(io.in_median - io.in_target ) > Math.abs(io.out_median - io.out_target ) ? [io.in_median, io.in_target] : [io.out_median, io.out_target];
|
||||
var medianTarget = median[1];
|
||||
median = Math.round(median[0]);
|
||||
|
||||
var stdIOClass = 'bad';
|
||||
if(std <= 0.50) {
|
||||
stdIOClass = 'good';
|
||||
}
|
||||
else if(std <= 1.00) {
|
||||
stdIOClass = 'acceptable';
|
||||
}
|
||||
|
||||
var medianIOClass = 'bad';
|
||||
if(Math.abs(median - medianTarget) <= 1) {
|
||||
medianIOClass = 'good';
|
||||
}
|
||||
else if(Math.abs(median - medianTarget) <= 2) {
|
||||
medianIOClass = 'acceptable';
|
||||
}
|
||||
|
||||
// now base the overall IO score based on both values.
|
||||
renderIOScore(std, median, io, ioClass);
|
||||
|
||||
// lie for now until IO questions finalize
|
||||
validIOScore = true;
|
||||
|
||||
renderScoringStopped();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
else {
|
||||
renderIOScore(null, null, null, 'skip');
|
||||
renderScoringStopped();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function initializeAudioInputChanged() {
|
||||
$audioInput.unbind('change').change(attemptScore);
|
||||
}
|
||||
|
||||
function initializeAudioOutputChanged() {
|
||||
|
||||
}
|
||||
|
||||
function initializeStep() {
|
||||
loadDevices();
|
||||
initializeFormElements();
|
||||
initializeNextButtonState();
|
||||
initializeWatchVideo();
|
||||
}
|
||||
|
||||
initializeStep();
|
||||
}
|
||||
|
||||
function beforeSelectTracks() {
|
||||
|
||||
}
|
||||
|
||||
function beforeSelectChat() {
|
||||
|
||||
}
|
||||
|
||||
function beforeDirectMonitor() {
|
||||
|
||||
}
|
||||
|
||||
function beforeTestNetwork() {
|
||||
|
||||
}
|
||||
|
||||
function beforeSuccess() {
|
||||
|
||||
}
|
||||
|
||||
var STEPS = {
|
||||
0: {
|
||||
beforeShow: beforeShowIntro
|
||||
},
|
||||
1: {
|
||||
beforeShow: beforeSelectDevice
|
||||
},
|
||||
2: {
|
||||
beforeShow: beforeSelectTracks
|
||||
},
|
||||
3: {
|
||||
beforeShow: beforeSelectChat
|
||||
},
|
||||
4: {
|
||||
beforeShow: beforeDirectMonitor
|
||||
},
|
||||
5: {
|
||||
beforeShow: beforeTestNetwork
|
||||
},
|
||||
6: {
|
||||
beforeShow: beforeSuccess
|
||||
}
|
||||
}
|
||||
|
||||
function beforeShowStep($step) {
|
||||
var stepInfo = STEPS[step];
|
||||
|
||||
if (!stepInfo) {
|
||||
throw "unknown step: " + step;
|
||||
}
|
||||
|
||||
stepInfo.beforeShow.call(self);
|
||||
}
|
||||
|
||||
function moveToStep() {
|
||||
var $nextWizardStep = $wizardSteps.filter($('[layout-wizard-step=' + step + ']'));
|
||||
|
||||
$wizardSteps.hide();
|
||||
|
||||
$currentWizardStep = $nextWizardStep;
|
||||
|
||||
var $ftueSteps = $(context._.template($templateSteps.html(), {}, { variable: 'data' }));
|
||||
var $activeStep = $ftueSteps.find('.ftue-stepnumber[data-step-number="' + step + '"]');
|
||||
$activeStep.addClass('.active');
|
||||
$activeStep.next().show(); // show the .ftue-step-title
|
||||
$currentWizardStep.find('.ftuesteps').replaceWith($ftueSteps);
|
||||
beforeShowStep($currentWizardStep);
|
||||
$currentWizardStep.show();
|
||||
|
||||
// update buttons
|
||||
var $ftueButtonsContent = $(context._.template($templateButtons.html(), {}, {variable: 'data'}));
|
||||
|
||||
|
||||
var $btnBack = $ftueButtonsContent.find('.btn-back');
|
||||
var $btnNext = $ftueButtonsContent.find('.btn-next');
|
||||
var $btnClose = $ftueButtonsContent.find('.btn-close');
|
||||
var $btnCancel = $ftueButtonsContent.find('.btn-cancel');
|
||||
|
||||
// hide back button if 1st step or last step
|
||||
if (step == 0 && step == TOTAL_STEPS - 1) {
|
||||
$btnBack.hide();
|
||||
}
|
||||
// hide next button if not on last step
|
||||
if (step == TOTAL_STEPS - 1) {
|
||||
$btnNext.hide();
|
||||
}
|
||||
// hide close if on last step
|
||||
if (step != TOTAL_STEPS - 1) {
|
||||
$btnClose.hide();
|
||||
}
|
||||
// hide cancel if not on last step
|
||||
if (step == TOTAL_STEPS - 1) {
|
||||
$btnCancel.hide();
|
||||
}
|
||||
|
||||
$btnNext.on('click', next);
|
||||
$btnBack.on('click', back);
|
||||
$btnClose.on('click', closeDialog);
|
||||
$btnCancel.on('click', closeDialog);
|
||||
|
||||
$ftueButtons.empty();
|
||||
$ftueButtons.append($ftueButtonsContent);
|
||||
}
|
||||
|
||||
function reset() {
|
||||
$currentWizardStep = null;
|
||||
}
|
||||
|
||||
// checks if we already have a profile called 'FTUE...'; if not, create one. if so, re-use it.
|
||||
function findOrCreateFTUEProfile() {
|
||||
var profileName = context.jamClient.FTUEGetMusicProfileName();
|
||||
|
||||
logger.debug("current profile name: " + profileName);
|
||||
|
||||
if(profileName && profileName.indexOf('FTUE') == 0) {
|
||||
|
||||
}
|
||||
else {
|
||||
var newProfileName = 'FTUEAttempt-' + new Date().getTime().toString();
|
||||
logger.debug("setting FTUE-prefixed profile name to: " + newProfileName);
|
||||
context.jamClient.FTUESetMusicProfileName(newProfileName);
|
||||
}
|
||||
|
||||
var profileName = context.jamClient.FTUEGetMusicProfileName();
|
||||
|
||||
logger.debug("name on exit: " + profileName);
|
||||
|
||||
}
|
||||
|
||||
function beforeShow(args) {
|
||||
context.jamClient.FTUECancel();
|
||||
findOrCreateFTUEProfile();
|
||||
|
||||
step = args.d1;
|
||||
if (!step) step = 0;
|
||||
step = parseInt(step);
|
||||
moveToStep();
|
||||
}
|
||||
|
||||
function afterShow() {
|
||||
|
||||
}
|
||||
|
||||
function afterHide() {
|
||||
context.jamClient.FTUECancel();
|
||||
}
|
||||
|
||||
function back() {
|
||||
if ($(this).is('.button-grey')) return;
|
||||
step = step - 1;
|
||||
moveToStep();
|
||||
return false;
|
||||
}
|
||||
|
||||
function next() {
|
||||
if ($(this).is('.button-grey')) return;
|
||||
|
||||
step = step + 1;
|
||||
|
||||
moveToStep();
|
||||
return false;
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
app.layout.closeDialog('gear-wizard');
|
||||
return false;
|
||||
}
|
||||
|
||||
function events() {
|
||||
}
|
||||
|
||||
function route() {
|
||||
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
|
||||
var dialogBindings = { beforeShow: beforeShow, afterShow: afterShow, afterHide: afterHide };
|
||||
|
||||
app.bindDialog('gear-wizard', dialogBindings);
|
||||
|
||||
$dialog = $('#gear-wizard-dialog');
|
||||
$wizardSteps = $dialog.find('.wizard-step');
|
||||
$templateSteps = $('#template-ftuesteps');
|
||||
$templateButtons = $('#template-ftue-buttons');
|
||||
$templateAudioPort = $('#template-audio-port');
|
||||
$ftueButtons = $dialog.find('.ftue-buttons');
|
||||
|
||||
operatingSystem = context.jamClient.GetOSAsString();
|
||||
|
||||
events();
|
||||
}
|
||||
|
||||
this.initialize = initialize;
|
||||
|
||||
self = this;
|
||||
return this;
|
||||
};
|
||||
|
||||
})(window, jQuery);
|
||||
|
|
@ -27,6 +27,8 @@
|
|||
CHAT: "1"
|
||||
};
|
||||
|
||||
context.JK.MAX_TRACKS = 6;
|
||||
|
||||
// TODO: store these client_id values in instruments table, or store
|
||||
// server_id as the client_id to prevent maintenance nightmares. As it's
|
||||
// set up now, we will have to deploy each time we add new instruments.
|
||||
|
|
@ -102,4 +104,43 @@
|
|||
context.JK.entityToPrintable = {
|
||||
music_session: "music session"
|
||||
}
|
||||
|
||||
context.JK.AUDIO_DEVICE_BEHAVIOR = {
|
||||
MacOSX_builtin: {
|
||||
display: 'MacOSX Built-In',
|
||||
videoURL: undefined,
|
||||
showKnobs: false,
|
||||
showASIO: false
|
||||
},
|
||||
MacOSX_interface: {
|
||||
display: 'MacOSX external interface',
|
||||
videoURL: undefined,
|
||||
showKnobs: false,
|
||||
showASIO: false
|
||||
},
|
||||
Win32_wdm: {
|
||||
display: 'Windows WDM',
|
||||
videoURL: undefined,
|
||||
showKnobs: true,
|
||||
showASIO: false
|
||||
},
|
||||
Win32_asio: {
|
||||
display: 'Windows ASIO',
|
||||
videoURL: undefined,
|
||||
showKnobs: false,
|
||||
showASIO: true
|
||||
},
|
||||
Win32_asio4all: {
|
||||
display: 'Windows ASIO4ALL',
|
||||
videoURL: undefined,
|
||||
showKnobs: false,
|
||||
showASIO: true
|
||||
},
|
||||
Linux: {
|
||||
display: 'Linux',
|
||||
videoURL: undefined,
|
||||
showKnobs: true,
|
||||
showASIO: false
|
||||
}
|
||||
}
|
||||
})(window,jQuery);
|
||||
|
|
@ -1062,11 +1062,28 @@
|
|||
};
|
||||
|
||||
function createDiagnostic(options) {
|
||||
var data = null;
|
||||
try {
|
||||
data = JSON.stringify(options)
|
||||
}
|
||||
catch(e) {
|
||||
data = JSON.stringify({data_error: "unable to JSON.stringify debug data:" + e.toString()})
|
||||
}
|
||||
return $.ajax({
|
||||
type: "POST",
|
||||
url: '/api/diagnostics',
|
||||
dataType: "json",
|
||||
contentType: 'application/json',
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
function getLatencyTester(options) {
|
||||
return $.ajax({
|
||||
type: "GET",
|
||||
url: '/api/latency_testers',
|
||||
dataType: "json",
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(options)
|
||||
});
|
||||
}
|
||||
|
|
@ -1164,6 +1181,7 @@
|
|||
this.createChatMessage = createChatMessage;
|
||||
this.getChatMessages = getChatMessages;
|
||||
this.createDiagnostic = createDiagnostic;
|
||||
this.getLatencyTester = getLatencyTester;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
(function(context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
|
||||
|
||||
// creates an iconic/graphical instrument selector. useful when there is minimal real-estate
|
||||
|
||||
$.fn.instrumentSelector = function(options) {
|
||||
|
||||
return this.each(function(index) {
|
||||
|
||||
function select(instrument_id) {
|
||||
if(instrument_id == null) {
|
||||
$currentInstrument.text('?');
|
||||
$currentInstrument.addClass('none');
|
||||
$select.data('instrument_id', null);
|
||||
}
|
||||
else {
|
||||
$currentInstrument.empty();
|
||||
$currentInstrument.removeClass('none');
|
||||
$currentInstrument.append('<img src="' + context.JK.getInstrumentIconMap24()[instrument_id].asset + '" />');
|
||||
$select.data('instrument_id', instrument_id);
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
$currentInstrument.btOff();
|
||||
$currentInstrument.focus();
|
||||
}
|
||||
|
||||
function onInstrumentSelected() {
|
||||
var $li = $(this);
|
||||
var instrument_id = $li.attr('data-instrument-id');
|
||||
|
||||
select(instrument_id);
|
||||
close();
|
||||
$select.triggerHandler('instrument_selected', {instrument_id: instrument_id});
|
||||
return false;
|
||||
};
|
||||
|
||||
var $select = $(context._.template($('#template-icon-instrument-select').html(), {instruments:context.JK.getInstrumentIconMap24()}, { variable: 'data' }));
|
||||
var $ul = $select.find('ul');
|
||||
var $currentInstrument = $select.find('.current-instrument');
|
||||
|
||||
context.JK.hoverBubble($currentInstrument, $ul.html(), {
|
||||
trigger:'click',
|
||||
cssClass: 'icon-instrument-selector-popup',
|
||||
spikeGirth:0,
|
||||
spikeLength:0,
|
||||
width:150,
|
||||
closeWhenOthersOpen: true,
|
||||
preShow: function() {
|
||||
},
|
||||
postShow:function(container) {
|
||||
$(container).find('li').click(onInstrumentSelected)
|
||||
}
|
||||
});
|
||||
|
||||
$currentInstrument.text('?');
|
||||
|
||||
$(this).append($select);
|
||||
|
||||
this.instrumentSelectorClose = close;
|
||||
this.instrumentSelectorSet = select;
|
||||
});
|
||||
}
|
||||
|
||||
$.fn.instrumentSelectorClose = function() {
|
||||
return this.each(function(index){
|
||||
if (jQuery.isFunction(this.instrumentSelectorClose)) {
|
||||
this.instrumentSelectorClose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$.fn.instrumentSelectorSet = function(instrumentId) {
|
||||
return this.each(function(index){
|
||||
if (jQuery.isFunction(this.instrumentSelectorSet)) {
|
||||
this.instrumentSelectorSet(instrumentId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})(window, jQuery);
|
||||
|
|
@ -677,10 +677,10 @@
|
|||
}
|
||||
|
||||
function startNewFtue() {
|
||||
var step = 0;
|
||||
setWizardStep(step);
|
||||
wizardShowFunctions[step]();
|
||||
showDialog('ftue');
|
||||
// var step = 0;
|
||||
//setWizardStep(step);
|
||||
//wizardShowFunctions[step]();
|
||||
showDialog('gear-wizard');
|
||||
}
|
||||
|
||||
function setWizardStep(targetStepId) {
|
||||
|
|
|
|||
|
|
@ -614,7 +614,7 @@
|
|||
var instrument = musician.instruments[j];
|
||||
var inst = '/assets/content/icon_instrument_default24.png';
|
||||
if (instrument.instrument_id in instrument_logo_map) {
|
||||
inst = instrument_logo_map[instrument.instrument_id];
|
||||
inst = instrument_logo_map[instrument.instrument_id].asset;
|
||||
}
|
||||
instrumentLogoHtml += '<img src="' + inst + '" width="24" height="24" /> ';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@
|
|||
for (var i=0; i < instruments.length; i++) {
|
||||
var inst = '../assets/content/icon_instrument_default24.png';
|
||||
if (instruments[i].instrument_id in instrument_logo_map) {
|
||||
inst = instrument_logo_map[instruments[i].instrument_id];
|
||||
inst = instrument_logo_map[instruments[i].instrument_id].asset;
|
||||
instrumentLogoHtml += '<img src="' + inst + '" width="24" height="24" /> ';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1545,6 +1545,10 @@
|
|||
'beforeDisconnect' : beforeDisconnect,
|
||||
};
|
||||
app.bindScreen('session', screenBindings);
|
||||
|
||||
// make sure no previous plays are still going on by accident
|
||||
context.jamClient.SessionStopPlay();
|
||||
context.jamClient.SessionRemoveAllPlayTracks();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@
|
|||
logger.debug("Find:Finding instruments. Participant tracks:", participant.tracks);
|
||||
var inst = '../assets/content/icon_instrument_default24.png';
|
||||
if (track.instrument_id in instrument_logo_map) {
|
||||
inst = instrument_logo_map[track.instrument_id];
|
||||
inst = instrument_logo_map[track.instrument_id].asset;
|
||||
}
|
||||
instrumentLogoHtml += '<img src="' + inst + '" width="24" height="24" /> ';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,11 +201,13 @@
|
|||
}
|
||||
|
||||
function renderNotConnected() {
|
||||
console.log("RENDER NOT CONNECTED!!!!!!!!!")
|
||||
$interactionBlocker.addClass('active');
|
||||
$disconnectedMsg.addClass('active');
|
||||
}
|
||||
|
||||
function renderConnected() {
|
||||
console.log("RENDER CONNECTED!!!!!!!!!")
|
||||
$interactionBlocker.removeClass('active');
|
||||
$disconnectedMsg.removeClass('active');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,13 @@
|
|||
for (i=0; i < localMusicTracks.length; i++) {
|
||||
var track = {};
|
||||
track.client_track_id = localMusicTracks[i].id;
|
||||
|
||||
if(localMusicTracks[i].instrument_id === 0) {
|
||||
track.instrument_id = context.JK.server_to_client_instrument_map["Other"].server_id;
|
||||
}
|
||||
else {
|
||||
track.instrument_id = context.JK.client_to_server_instrument_map[localMusicTracks[i].instrument_id].server_id;
|
||||
}
|
||||
if (localMusicTracks[i].stereo) {
|
||||
track.sound = "stereo";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
context.JK = context.JK || {};
|
||||
var logger = context.JK.logger;
|
||||
var AUDIO_DEVICE_BEHAVIOR = context.JK.AUDIO_DEVICE_BEHAVIOR;
|
||||
|
||||
var days = new Array("Sun", "Mon", "Tue",
|
||||
"Wed", "Thu", "Fri", "Sat");
|
||||
|
|
@ -15,6 +16,7 @@
|
|||
"April", "May", "June", "July", "August", "September",
|
||||
"October", "November", "December");
|
||||
|
||||
var os = null;
|
||||
|
||||
context.JK.stringToBool = function (s) {
|
||||
switch (s.toLowerCase()) {
|
||||
|
|
@ -85,9 +87,9 @@
|
|||
|
||||
$.each(context._.keys(icon_map_base), function (index, instrumentId) {
|
||||
var icon = icon_map_base[instrumentId];
|
||||
instrumentIconMap24[instrumentId] = "/assets/content/icon_instrument_" + icon + "24.png";
|
||||
instrumentIconMap45[instrumentId] = "/assets/content/icon_instrument_" + icon + "45.png";
|
||||
instrumentIconMap256[instrumentId] = "/assets/content/icon_instrument_" + icon + "256.png";
|
||||
instrumentIconMap24[instrumentId] = {asset: "/assets/content/icon_instrument_" + icon + "24.png", name: instrumentId};
|
||||
instrumentIconMap45[instrumentId] = {asset: "/assets/content/icon_instrument_" + icon + "45.png", name: instrumentId};
|
||||
instrumentIconMap256[instrumentId] = {asset: "/assets/content/icon_instrument_" + icon + "256.png", name: instrumentId};
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -96,12 +98,12 @@
|
|||
* @param templateName the name of the help template (without the '#template-help' prefix). Add to _help.html.erb
|
||||
* @param data (optional) data for your template, if applicable
|
||||
* @param options (optional) You can override the default BeautyTips options: https://github.com/dillon-sellars/BeautyTips
|
||||
*
|
||||
*/
|
||||
context.JK.helpBubble = function ($element, templateName, data, options) {
|
||||
if (!data) {
|
||||
data = {}
|
||||
}
|
||||
|
||||
var helpText = context._.template($('#template-help-' + templateName).html(), data, { variable: 'data' });
|
||||
|
||||
var holder = $('<div class="hover-bubble help-bubble"></div>');
|
||||
|
|
@ -110,6 +112,37 @@
|
|||
context.JK.hoverBubble($element, helpText, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates a help bubble immediately with the specified $element, using jquery.bt.js (BeautyTips)
|
||||
* By 'prod' it means to literally prod the user, to make them aware of something important because they did something else
|
||||
*
|
||||
* This will open a bubble immediately and show it for 4 seconds,
|
||||
* if you call it again before the 4 second timer is up, it will renew the 4 second timer.
|
||||
* @param $element The element that should show the help when hovered
|
||||
* @param templateName the name of the help template (without the '#template-help' prefix). Add to _help.html.erb
|
||||
* @param data (optional) data for your template, if applicable
|
||||
* @param options (optional) You can override the default BeautyTips options: https://github.com/dillon-sellars/BeautyTips
|
||||
*/
|
||||
context.JK.prodBubble = function($element, templateName, data, options) {
|
||||
if(!options) options = {};
|
||||
options['trigger'] = 'none';
|
||||
options['clickAnywhereToClose'] = false
|
||||
if(!options['duration']) options['duration'] = 4000;
|
||||
|
||||
var existingTimer = $element.data("prodTimer");
|
||||
if(existingTimer) {
|
||||
clearTimeout(existingTimer);
|
||||
$element.btOn();
|
||||
}
|
||||
else {
|
||||
context.JK.helpBubble($element, templateName, data, options);
|
||||
$element.btOn();
|
||||
}
|
||||
$element.data("prodTimer", setTimeout(function() {
|
||||
$element.data("prodTimer", null);
|
||||
$element.btOff();
|
||||
}, options['duration']));
|
||||
}
|
||||
/**
|
||||
* Associates a bubble on hover (by default) with the specified $element, using jquery.bt.js (BeautyTips)
|
||||
* @param $element The element that should show the bubble when hovered
|
||||
|
|
@ -345,26 +378,26 @@
|
|||
|
||||
context.JK.getInstrumentIcon24 = function (instrument) {
|
||||
if (instrument in instrumentIconMap24) {
|
||||
return instrumentIconMap24[instrument];
|
||||
return instrumentIconMap24[instrument].asset;
|
||||
}
|
||||
|
||||
return instrumentIconMap24["default"];
|
||||
return instrumentIconMap24["default"].asset;
|
||||
};
|
||||
|
||||
context.JK.getInstrumentIcon45 = function (instrument) {
|
||||
if (instrument in instrumentIconMap45) {
|
||||
return instrumentIconMap45[instrument];
|
||||
return instrumentIconMap45[instrument].asset;
|
||||
}
|
||||
|
||||
return instrumentIconMap45["default"];
|
||||
return instrumentIconMap45["default"].asset;
|
||||
};
|
||||
|
||||
context.JK.getInstrumentIcon256 = function (instrument) {
|
||||
if (instrument in instrumentIconMap256) {
|
||||
return instrumentIconMap256[instrument];
|
||||
return instrumentIconMap256[instrument].asset;
|
||||
}
|
||||
|
||||
return instrumentIconMap256["default"];
|
||||
return instrumentIconMap256["default"].asset;
|
||||
};
|
||||
|
||||
// meant to pass in a bunch of images with an instrument-id attribute on them.
|
||||
|
|
@ -493,6 +526,13 @@
|
|||
return $item;
|
||||
}
|
||||
|
||||
context.JK.GetOSAsString = function() {
|
||||
if(!os) {
|
||||
os = context.jamClient.GetOSAsString();
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
context.JK.search = function (query, app, callback) {
|
||||
//logger.debug("search: "+ query)
|
||||
$.ajax({
|
||||
|
|
@ -534,6 +574,111 @@
|
|||
return keys;
|
||||
};
|
||||
|
||||
context.JK.createProfileName = function(deviceInfo, chatName) {
|
||||
var isSameInOut = deviceInfo.input.id == deviceInfo.output.id;
|
||||
|
||||
var name = null;
|
||||
if(isSameInOut) {
|
||||
name = "In/Out: " + deviceInfo.input.info.displayName;
|
||||
}
|
||||
else {
|
||||
name = "In: " + deviceInfo.input.info.displayName + ", Out: " + deviceInfo.output.info.displayName
|
||||
}
|
||||
|
||||
logger.debug("creating profile name: " + name);
|
||||
return name;
|
||||
}
|
||||
|
||||
context.JK.selectedDeviceInfo = function(audioInputDeviceId, audioOutputDeviceId, deviceInformation) {
|
||||
|
||||
if(!deviceInformation) {
|
||||
deviceInformation = context.JK.loadDeviceInfo();
|
||||
}
|
||||
|
||||
var input = deviceInformation[audioInputDeviceId];
|
||||
var output = deviceInformation[audioOutputDeviceId];
|
||||
|
||||
var inputBehavior = AUDIO_DEVICE_BEHAVIOR[input.type];
|
||||
var outputBehavior = AUDIO_DEVICE_BEHAVIOR[output.type];
|
||||
|
||||
return {
|
||||
input: {
|
||||
id: audioInputDeviceId,
|
||||
info: input,
|
||||
behavior: inputBehavior
|
||||
},
|
||||
output: {
|
||||
id: audioOutputDeviceId,
|
||||
info: output,
|
||||
behavior: outputBehavior
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.JK.loadDeviceInfo = function() {
|
||||
|
||||
var operatingSystem = context.JK.GetOSAsString();
|
||||
// should return one of:
|
||||
// * MacOSX_builtin
|
||||
// * MACOSX_interface
|
||||
// * Win32_wdm
|
||||
// * Win32_asio
|
||||
// * Win32_asio4all
|
||||
// * Linux
|
||||
function determineDeviceType(deviceId, displayName) {
|
||||
if (operatingSystem == "MacOSX") {
|
||||
if (displayName.toLowerCase().trim() == "built-in") {
|
||||
return "MacOSX_builtin";
|
||||
}
|
||||
else {
|
||||
return "MacOSX_interface";
|
||||
}
|
||||
}
|
||||
else if (operatingSystem == "Win32") {
|
||||
if (context.jamClient.FTUEIsMusicDeviceWDM(deviceId)) {
|
||||
return "Win32_wdm";
|
||||
}
|
||||
else if (displayName.toLowerCase().indexOf("asio4all") > -1) {
|
||||
return "Win32_asio4all"
|
||||
}
|
||||
else {
|
||||
return "Win32_asio";
|
||||
}
|
||||
}
|
||||
else {
|
||||
return "Linux";
|
||||
}
|
||||
}
|
||||
|
||||
var devices = context.jamClient.FTUEGetAudioDevices();
|
||||
logger.debug("FTUEGetAudioDevices: " + JSON.stringify(devices));
|
||||
|
||||
var loadedDevices = {};
|
||||
|
||||
// augment these devices by determining their type
|
||||
context._.each(devices.devices, function (device) {
|
||||
|
||||
if (device.name == "JamKazam Virtual Monitor") {
|
||||
return;
|
||||
}
|
||||
|
||||
var deviceInfo = {};
|
||||
|
||||
deviceInfo.id = device.guid;
|
||||
deviceInfo.type = determineDeviceType(device.guid, device.display_name);
|
||||
deviceInfo.displayType = AUDIO_DEVICE_BEHAVIOR[deviceInfo.type].display;
|
||||
deviceInfo.displayName = device.display_name;
|
||||
deviceInfo.inputCount = device.input_count;
|
||||
deviceInfo.outputCount = device.output_count;
|
||||
|
||||
loadedDevices[device.guid] = deviceInfo;
|
||||
})
|
||||
|
||||
logger.debug(context.JK.dlen(loadedDevices) + " devices loaded.", loadedDevices);
|
||||
|
||||
return loadedDevices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first error associated with the field.
|
||||
* @param fieldName the name of the field
|
||||
|
|
@ -656,6 +801,15 @@
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
// pass in 'arguments' in a fail callback of a $.ajax
|
||||
context.JK.isNetworkError = function(failArgs) {
|
||||
if(failArgs.length != 3) throw "expected 3 arguments from .fail of $ajax in isNetworkError"
|
||||
var xhr = failArgs[0];
|
||||
|
||||
return xhr.status == 0;
|
||||
}
|
||||
|
||||
context.JK.clientType = function () {
|
||||
if (context.jamClient) {
|
||||
return context.jamClient.IsNativeClient() ? 'client' : 'browser';
|
||||
|
|
@ -729,7 +883,7 @@
|
|||
}
|
||||
|
||||
context.JK.checkbox = function ($checkbox) {
|
||||
$checkbox.iCheck({
|
||||
return $checkbox.iCheck({
|
||||
checkboxClass: 'icheckbox_minimal',
|
||||
radioClass: 'iradio_minimal',
|
||||
inheritClass: true
|
||||
|
|
|
|||
|
|
@ -0,0 +1,187 @@
|
|||
(function (context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.GearWizard = function (app) {
|
||||
|
||||
var logger = context.JK.logger;
|
||||
|
||||
var $dialog = null;
|
||||
var wizard = null;
|
||||
var $wizardSteps = null;
|
||||
var $templateSteps = null;
|
||||
|
||||
var self = this;
|
||||
|
||||
var STEP_INTRO = 0;
|
||||
var STEP_SELECT_DEVICE = 1;
|
||||
var STEP_SELECT_TRACKS = 2;
|
||||
var STEP_SELECT_CHAT = 3;
|
||||
var STEP_DIRECT_MONITOR = 4;
|
||||
var STEP_ROUTER_NETWORK = 5;
|
||||
var STEP_SUCCESS = 6;
|
||||
|
||||
var stepUnderstandGear = new context.JK.StepUnderstandGear(app, this);
|
||||
var stepSelectGear = new context.JK.StepSelectGear(app, this);
|
||||
var stepConfigureTracks = new context.JK.StepConfigureTracks(app, this);
|
||||
var stepConfigureVoiceChat = new context.JK.StepConfigureVoiceChat(app, this);
|
||||
var stepDirectMonitoring = new context.JK.StepDirectMonitoring(app, this);
|
||||
var stepNetworkTest = new context.JK.StepNetworkTest(app, this);
|
||||
var stepSuccess = new context.JK.StepSuccess(app, this);
|
||||
|
||||
var STEPS = {
|
||||
0: stepUnderstandGear,
|
||||
1: stepSelectGear,
|
||||
2: stepConfigureTracks,
|
||||
3: stepConfigureVoiceChat,
|
||||
4: stepDirectMonitoring,
|
||||
5: stepNetworkTest,
|
||||
6: stepSuccess
|
||||
}
|
||||
|
||||
function newSession() {
|
||||
context._.each(STEPS, function(stepInfo, stepNumber) {
|
||||
if(stepInfo.newSession) {
|
||||
stepInfo.newSession.call(stepInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onStepChanged(e, data) {
|
||||
var step = wizard.getCurrentStep();
|
||||
var $currentWizardStep = wizard.getCurrentWizardStep();
|
||||
|
||||
// update ftue step numbers
|
||||
var $ftueSteps = $(context._.template($templateSteps.html(), {}, { variable: 'data' }));
|
||||
var $activeStep = $ftueSteps.find('.ftue-stepnumber[data-step-number="' + step + '"]');
|
||||
$activeStep.addClass('.active');
|
||||
$activeStep.next().show(); // show the .ftue-step-title
|
||||
$currentWizardStep.find('.ftuesteps').replaceWith($ftueSteps);
|
||||
}
|
||||
|
||||
// checks if we already have a profile called 'FTUE...'; if not, create one. if so, re-use it.
|
||||
function createFTUEProfile() {
|
||||
var profileName = context.jamClient.FTUEGetMusicProfileName();
|
||||
|
||||
logger.debug("current profile name: " + profileName);
|
||||
|
||||
if(profileName && profileName.indexOf('FTUE') == 0) {
|
||||
// remove junk
|
||||
context.jamClient.TrackDeleteProfile(profileName);
|
||||
}
|
||||
|
||||
var newProfileName = 'FTUEAttempt-' + new Date().getTime().toString();
|
||||
logger.debug("setting FTUE-prefixed profile name to: " + newProfileName);
|
||||
context.jamClient.FTUESetMusicProfileName(newProfileName);
|
||||
newSession();
|
||||
|
||||
var profileName = context.jamClient.FTUEGetMusicProfileName();
|
||||
|
||||
logger.debug("name on exit: " + profileName);
|
||||
|
||||
}
|
||||
|
||||
function beforeShow(args) {
|
||||
|
||||
context.jamClient.FTUECancel();
|
||||
context.jamClient.FTUESetStatus(false);
|
||||
createFTUEProfile();
|
||||
|
||||
wizard.onBeforeShow(args);
|
||||
}
|
||||
|
||||
function afterShow() {
|
||||
|
||||
}
|
||||
|
||||
function afterHide() {
|
||||
wizard.onAfterHide();
|
||||
|
||||
context.jamClient.FTUESetStatus(true);
|
||||
context.jamClient.FTUECancel();
|
||||
}
|
||||
|
||||
|
||||
function onCanceled() {
|
||||
if (app.cancelFtue) {
|
||||
app.cancelFtue();
|
||||
app.afterFtue = null;
|
||||
app.cancelFtue = null;
|
||||
}
|
||||
|
||||
return closeDialog();
|
||||
}
|
||||
|
||||
function onClosed() {
|
||||
if (app.afterFtue) {
|
||||
// If there's a function to invoke, invoke it.
|
||||
app.afterFtue();
|
||||
app.afterFtue = null;
|
||||
app.cancelFtue = null;
|
||||
}
|
||||
|
||||
return closeDialog();
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
wizard.onCloseDialog();
|
||||
app.layout.closeDialog('gear-wizard');
|
||||
return false;
|
||||
}
|
||||
|
||||
function events() {
|
||||
$(wizard).on('step_changed', onStepChanged);
|
||||
$(wizard).on('wizard_cancel', onCanceled);
|
||||
$(wizard).on('wizard_close', onClosed);
|
||||
}
|
||||
|
||||
function setNextState(enabled) {
|
||||
wizard.setNextState(enabled);
|
||||
}
|
||||
|
||||
function setBackState(enabled) {
|
||||
wizard.setBackState(enabled);
|
||||
}
|
||||
|
||||
|
||||
function initialize() {
|
||||
|
||||
// on initial page load, we are not in the FTUE. so cancel the FTUE and call FTUESetStatus(true) if needed
|
||||
if(context.jamClient.FTUEGetStatus() == false) {
|
||||
context.jamClient.FTUESetStatus(true);
|
||||
}
|
||||
context.jamClient.FTUECancel();
|
||||
|
||||
var dialogBindings = { beforeShow: beforeShow, afterShow: afterShow, afterHide: afterHide };
|
||||
|
||||
app.bindDialog('gear-wizard', dialogBindings);
|
||||
|
||||
$dialog = $('#gear-wizard-dialog');
|
||||
$wizardSteps = $dialog.find('.wizard-step');
|
||||
$templateSteps = $('#template-ftuesteps');
|
||||
|
||||
stepUnderstandGear.initialize($wizardSteps.filter($('[layout-wizard-step=0]')));
|
||||
stepSelectGear.initialize($wizardSteps.filter($('[layout-wizard-step=1]')));
|
||||
stepConfigureTracks.initialize($wizardSteps.filter($('[layout-wizard-step=2]')));
|
||||
stepConfigureVoiceChat.initialize($wizardSteps.filter($('[layout-wizard-step=3]')));
|
||||
stepDirectMonitoring.initialize($wizardSteps.filter($('[layout-wizard-step=4]')));
|
||||
stepNetworkTest.initialize($wizardSteps.filter($('[layout-wizard-step=5]')));
|
||||
stepSuccess.initialize($wizardSteps.filter($('[layout-wizard-step=6]')));
|
||||
|
||||
wizard = new context.JK.Wizard(app);
|
||||
wizard.initialize($dialog, $wizardSteps, STEPS);
|
||||
|
||||
events();
|
||||
}
|
||||
|
||||
this.setNextState = setNextState;
|
||||
this.setBackState = setBackState;
|
||||
this.initialize = initialize;
|
||||
this.createFTUEProfile = createFTUEProfile;
|
||||
|
||||
self = this;
|
||||
return this;
|
||||
};
|
||||
|
||||
})(window, jQuery);
|
||||
|
|
@ -0,0 +1,270 @@
|
|||
(function (context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.StepConfigureTracks = function (app) {
|
||||
|
||||
var ASSIGNMENT = context.JK.ASSIGNMENT;
|
||||
var VOICE_CHAT = context.JK.VOICE_CHAT;
|
||||
var MAX_TRACKS = context.JK.MAX_TRACKS;
|
||||
|
||||
var logger = context.JK.logger;
|
||||
|
||||
var $step = null;
|
||||
var $templateAssignablePort = null;
|
||||
var $templateTrackTarget = null;
|
||||
var $unassignedChannelsHolder = null;
|
||||
var $tracksHolder = null;
|
||||
var $instrumentsHolder = null;
|
||||
|
||||
|
||||
function loadChannels() {
|
||||
var musicPorts = jamClient.FTUEGetChannels();
|
||||
|
||||
$unassignedChannelsHolder.empty();
|
||||
$tracksHolder.find('.ftue-input').remove();
|
||||
|
||||
var inputChannels = musicPorts.inputs;
|
||||
|
||||
context._.each(inputChannels, function (inputChannel) {
|
||||
if(inputChannel.assignment == ASSIGNMENT.UNASSIGNED) {
|
||||
var $channel = $(context._.template($templateAssignablePort.html(), inputChannel, { variable: 'data' }));
|
||||
unassignChannel($channel);
|
||||
}
|
||||
else {
|
||||
var $channel = $(context._.template($templateAssignablePort.html(), inputChannel, { variable: 'data' }));
|
||||
|
||||
// find the track this belongs in
|
||||
|
||||
var trackNumber = inputChannel.assignment - 1;
|
||||
|
||||
var $track = $tracksHolder.find('.track[data-num="' + trackNumber + '"]')
|
||||
|
||||
if($track.length == 0) {
|
||||
context.JK.alertSupportedNeeded('Unable to find a track for channel with assignment ' + inputChannel.assignment);
|
||||
return false;
|
||||
}
|
||||
addChannelToTrack($channel, $track.find('.track-target'));
|
||||
}
|
||||
|
||||
$channel.draggable({
|
||||
helper: 'clone',
|
||||
start: function() {
|
||||
var $channel = $(this);
|
||||
var $track = $channel.closest('.track-target');
|
||||
var isUnassigned = $track.length == 0;
|
||||
if(isUnassigned) {
|
||||
$tracksHolder.find('.track-target').addClass('possible-target');
|
||||
}
|
||||
else {
|
||||
$tracksHolder.find('.track-target').addClass('possible-target');
|
||||
$unassignedChannelsHolder.addClass('possible-target');
|
||||
}
|
||||
},
|
||||
stop: function() {
|
||||
$tracksHolder.find('.track-target').removeClass('possible-target');
|
||||
$unassignedChannelsHolder.removeClass('possible-target')
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
// iterates through the dom and returns a pure data structure for track associations
|
||||
function trackAssociations() {
|
||||
|
||||
var tracks = {};
|
||||
tracks.tracks = [];
|
||||
tracks.unassignedChannels = [];
|
||||
var $unassignedChannels = $unassignedChannelsHolder.find('.ftue-input');
|
||||
var $tracks = $tracksHolder.find('.track-target');
|
||||
|
||||
context._.each($unassignedChannels, function($unassignedTrack) {
|
||||
$unassignedTrack = $($unassignedTrack);
|
||||
var channelId = $unassignedTrack.attr('data-input-id');
|
||||
tracks.unassignedChannels.push(channelId);
|
||||
})
|
||||
|
||||
context._.each($tracks, function($track, index) {
|
||||
$track = $($track);
|
||||
var $assignedChannels = $track.find('.ftue-input');
|
||||
|
||||
var track = {index: index, channels:[]};
|
||||
context._.each($assignedChannels, function($assignedChannel) {
|
||||
$assignedChannel = $($assignedChannel);
|
||||
track.channels.push($assignedChannel.attr('data-input-id'))
|
||||
});
|
||||
|
||||
// sparse array
|
||||
if(track.channels.length > 0) {
|
||||
tracks.tracks.push(track);
|
||||
}
|
||||
var $instrument = $instrumentsHolder.find('[data-num="' + index + '"]').find('.icon-instrument-select');
|
||||
track.instrument_id = $instrument.data('instrument_id');
|
||||
})
|
||||
return tracks;
|
||||
}
|
||||
|
||||
function validate(tracks) {
|
||||
// there must be at least one assigned channel
|
||||
|
||||
if(tracks.tracks.length == 0) {
|
||||
logger.debug("ConfigureTracks validation error: must have assigned at least one input port to a track.");
|
||||
context.JK.Banner.showAlert('Must have assigned at least one input port to a track.');
|
||||
return false;
|
||||
}
|
||||
|
||||
context._.each(tracks.tracks, function(track) {
|
||||
if(!track.instrument_id) {
|
||||
logger.debug("ConfigureTracks validation error: all tracks with ports assigned must specify an instrument.");
|
||||
context.JK.Banner.showAlert('All tracks with ports assigned must specify an instrument.');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function save(tracks) {
|
||||
|
||||
context._.each(tracks.unassignedChannels, function(unassignedChannelId) {
|
||||
context.jamClient.TrackSetAssignment(unassignedChannelId, true, ASSIGNMENT.UNASSIGNED);
|
||||
});
|
||||
|
||||
context._.each(tracks.tracks, function(track, index) {
|
||||
|
||||
var trackNumber = index + 1;
|
||||
|
||||
context._.each(track.channels, function(channelId) {
|
||||
context.jamClient.TrackSetAssignment(channelId, true, trackNumber);
|
||||
|
||||
});
|
||||
logger.debug("context.jamClient.TrackSetInstrument(trackNumber, track.instrument_id)", trackNumber, track.instrument_id);
|
||||
context.jamClient.TrackSetInstrument(trackNumber, context.JK.instrument_id_to_instrument[track.instrument_id].client_id);
|
||||
});
|
||||
|
||||
var result = context.jamClient.TrackSaveAssignments();
|
||||
|
||||
if(!result || result.length == 0) {
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
context.JK.Banner.showAlert('Unable to save assignments. ' + result);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function loadTrackInstruments() {
|
||||
var $trackInstruments = $instrumentsHolder.find('.track-instrument');
|
||||
|
||||
context._.each($trackInstruments, function(trackInstrument) {
|
||||
var $trackInstrument = $(trackInstrument);
|
||||
|
||||
var trackIndex = parseInt($trackInstrument.attr('data-num')) + 1;
|
||||
|
||||
var clientInstrument = context.jamClient.TrackGetInstrument(trackIndex);
|
||||
|
||||
var instrument = context.JK.client_to_server_instrument_map[clientInstrument];
|
||||
|
||||
$trackInstrument.instrumentSelectorSet(instrument ? instrument.server_id : instrument);
|
||||
});
|
||||
}
|
||||
|
||||
function handleNext() {
|
||||
var tracks = trackAssociations();
|
||||
|
||||
if(!validate(tracks)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var saved = save(tracks);
|
||||
|
||||
if(saved) {
|
||||
context.JK.GA.trackConfigureTracksCompletion(context.JK.detectOS());
|
||||
}
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
function beforeShow() {
|
||||
loadChannels();
|
||||
loadTrackInstruments();
|
||||
}
|
||||
|
||||
function unassignChannel($channel) {
|
||||
var $originallyAssignedTrack = $channel.closest('.track-target');
|
||||
$unassignedChannelsHolder.append($channel);
|
||||
$originallyAssignedTrack.attr('track-count', $originallyAssignedTrack.find('.ftue-input:not(.ui-draggable-dragging)').length);
|
||||
|
||||
}
|
||||
function addChannelToTrack($channel, $track) {
|
||||
var $originallyAssignedTrack = $channel.closest('.track-target');
|
||||
$track.append($channel);
|
||||
$track.attr('track-count', $track.find('.ftue-input:not(.ui-draggable-dragging)').length);
|
||||
$originallyAssignedTrack.attr('track-count', $originallyAssignedTrack.find('.ftue-input:not(.ui-draggable-dragging)').length)
|
||||
}
|
||||
|
||||
function initializeUnassignedDroppable() {
|
||||
$unassignedChannelsHolder.droppable(
|
||||
{
|
||||
activeClass: 'drag-in-progress',
|
||||
hoverClass: 'drag-hovering',
|
||||
drop: function( event, ui ) {
|
||||
var $channel = ui.draggable;
|
||||
//$channel.css('left', '0').css('top', '0');
|
||||
unassignChannel($channel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initializeTrackDroppables() {
|
||||
var i;
|
||||
for(i = 0; i < MAX_TRACKS; i++) {
|
||||
var $target = $(context._.template($templateTrackTarget.html(), {num: i }, { variable: 'data' }));
|
||||
$tracksHolder.append($target);
|
||||
$target.find('.track-target').droppable(
|
||||
{
|
||||
activeClass: 'drag-in-progress',
|
||||
hoverClass: 'drag-hovering',
|
||||
drop: function( event, ui ) {
|
||||
var $track = $(this);
|
||||
var $channel = ui.draggable;
|
||||
//$channel.css('left', '0').css('top', '0');
|
||||
addChannelToTrack($channel, $track);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function initializeInstrumentDropdown() {
|
||||
var i;
|
||||
for(i = 0; i < MAX_TRACKS; i++) {
|
||||
var $root = $('<div class="track-instrument"></div>');
|
||||
$root.instrumentSelector().attr('data-num', i);
|
||||
$instrumentsHolder.append($root);
|
||||
}
|
||||
}
|
||||
|
||||
function initialize(_$step) {
|
||||
$step = _$step;
|
||||
|
||||
$templateAssignablePort = $('#template-assignable-port');
|
||||
$templateTrackTarget = $('#template-track-target');
|
||||
$unassignedChannelsHolder = $step.find('.unassigned-channels');
|
||||
$tracksHolder = $step.find('.tracks');
|
||||
$instrumentsHolder = $step.find('.instruments');
|
||||
|
||||
|
||||
initializeUnassignedDroppable();
|
||||
initializeTrackDroppables();
|
||||
initializeInstrumentDropdown();
|
||||
}
|
||||
|
||||
this.handleNext = handleNext;
|
||||
this.beforeShow = beforeShow;
|
||||
this.initialize = initialize;
|
||||
|
||||
return this;
|
||||
}
|
||||
})(window, jQuery);
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
(function (context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.StepConfigureVoiceChat = function (app) {
|
||||
|
||||
var ASSIGNMENT = context.JK.ASSIGNMENT;
|
||||
var VOICE_CHAT = context.JK.VOICE_CHAT;
|
||||
var logger = context.JK.logger;
|
||||
|
||||
var $step = null;
|
||||
var $reuseAudioInputRadio = null;
|
||||
var $useChatInputRadio = null;
|
||||
var $chatInputs = null;
|
||||
var $templateChatInput = null;
|
||||
var $selectedChatInput = null;// should only be used if isChatEnabled = true
|
||||
|
||||
function newSession() {
|
||||
$reuseAudioInputRadio.attr('checked', 'checked').iCheck('check');
|
||||
}
|
||||
|
||||
function isChannelAvailableForChat(chatChannelId, musicPorts) {
|
||||
var result = true;
|
||||
context._.each(musicPorts.input, function(inputChannel) {
|
||||
// if the channel is currently assigned to a track, it not unassigned
|
||||
if(inputChannel.id == chatChannelId && (inputChannel.assignment > 0)) {
|
||||
result = false;
|
||||
return false; // break
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function isChatEnabled() {
|
||||
return $useChatInputRadio.is(':checked');
|
||||
}
|
||||
|
||||
function beforeShow() {
|
||||
|
||||
if(isChatEnabled()) {
|
||||
enableChat();
|
||||
}
|
||||
else {
|
||||
disableChat();
|
||||
}
|
||||
|
||||
var musicPorts = jamClient.FTUEGetChannels();
|
||||
var chatInputs = context.jamClient.FTUEGetChatInputs();
|
||||
|
||||
$chatInputs.empty();
|
||||
|
||||
context._.each(chatInputs, function(chatChannelName, chatChannelId) {
|
||||
if(isChannelAvailableForChat(chatChannelId, musicPorts)) {
|
||||
var $chat = $(context._.template($templateChatInput.html(), {id: chatChannelId, name: chatChannelName}, { variable: 'data' }));
|
||||
$chat.hide(); // we'll show it once it's styled with iCheck
|
||||
$chatInputs.append($chat);
|
||||
}
|
||||
});
|
||||
|
||||
var $radioButtons = $chatInputs.find('input[name="chat-device"]');
|
||||
context.JK.checkbox($radioButtons).on('ifChecked', function(e) {
|
||||
var $input = $(e.currentTarget);
|
||||
$selectedChatInput = $input; // for use in handleNext
|
||||
var channelId = $input.attr('data-channel-id');
|
||||
context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.CHAT);
|
||||
var result = context.jamClient.TrackSaveAssignments();
|
||||
|
||||
if(!result || result.length == 0) {
|
||||
// success
|
||||
}
|
||||
else {
|
||||
context.JK.Banner.showAlert('Unable to save assignments. ' + result);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if(!isChatEnabled()) {
|
||||
$radioButtons.iCheck('disable');
|
||||
}
|
||||
|
||||
$chatInputs.find('.chat-input').show().on('click', function() {
|
||||
if(!isChatEnabled()) {
|
||||
context.JK.prodBubble($step.find('.use-chat-input h3'), 'chat-not-enabled', {}, { positions:['left']});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function disableChat() {
|
||||
logger.debug("FTUE: disabling chat");
|
||||
context.jamClient.TrackSetChatEnable(false);
|
||||
var result = context.jamClient.TrackSaveAssignments();
|
||||
|
||||
if(!result || result.length == 0) {
|
||||
// success
|
||||
}
|
||||
else {
|
||||
context.JK.Banner.showAlert('Unable to disable chat. ' + result);
|
||||
return false;
|
||||
}
|
||||
|
||||
var $radioButtons = $chatInputs.find('input[name="chat-device"]');
|
||||
$radioButtons.iCheck('disable');
|
||||
}
|
||||
|
||||
function enableChat() {
|
||||
logger.debug("FTUE: enabling chat");
|
||||
context.jamClient.TrackSetChatEnable(true);
|
||||
var result = context.jamClient.TrackSaveAssignments();
|
||||
|
||||
if(!result || result.length == 0) {
|
||||
// success
|
||||
}
|
||||
else {
|
||||
context.JK.Banner.showAlert('Unable to enable chat. ' + result);
|
||||
return false;
|
||||
}
|
||||
|
||||
var $radioButtons = $chatInputs.find('input[name="chat-device"]');
|
||||
$radioButtons.iCheck('enable');
|
||||
}
|
||||
|
||||
function handleChatEnabledToggle() {
|
||||
context.JK.checkbox($reuseAudioInputRadio);
|
||||
context.JK.checkbox($useChatInputRadio);
|
||||
|
||||
// plugin sets to relative on the element; have to do this as an override
|
||||
$reuseAudioInputRadio.closest('.iradio_minimal').css('position', 'absolute');
|
||||
$useChatInputRadio.closest('.iradio_minimal').css('position', 'absolute');
|
||||
|
||||
$reuseAudioInputRadio.on('ifChecked', disableChat);
|
||||
$useChatInputRadio.on('ifChecked', enableChat)
|
||||
}
|
||||
|
||||
function handleNext() {
|
||||
var selectedDeviceInfo = context.JK.selectedDeviceInfo(context.jamClient.FTUEGetInputMusicDevice(), context.jamClient.FTUEGetOutputMusicDevice());
|
||||
|
||||
var chatName = null;
|
||||
if(isChatEnabled()) {
|
||||
chatName = $selectedChatInput.attr('data-channel-name');
|
||||
}
|
||||
context.jamClient.FTUESetMusicProfileName(context.JK.createProfileName(selectedDeviceInfo, chatName));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function initialize(_$step) {
|
||||
$step = _$step;
|
||||
|
||||
$reuseAudioInputRadio = $step.find('.reuse-audio-input input');
|
||||
$useChatInputRadio = $step.find('.use-chat-input input');
|
||||
$chatInputs = $step.find('.chat-inputs');
|
||||
$templateChatInput = $('#template-chat-input');
|
||||
|
||||
handleChatEnabledToggle();
|
||||
}
|
||||
|
||||
this.handleNext = handleNext;
|
||||
this.newSession = newSession;
|
||||
this.beforeShow = beforeShow;
|
||||
this.initialize = initialize;
|
||||
|
||||
return this;
|
||||
}
|
||||
})(window, jQuery);
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
(function (context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.StepDirectMonitoring = function (app) {
|
||||
|
||||
var $step = null;
|
||||
var $directMonitoringBtn = null;
|
||||
var isPlaying = false;
|
||||
var playCheckInterval = null;
|
||||
var trackDurationMs = null;
|
||||
|
||||
function checkIfPlaying() {
|
||||
var currentPositionMs = context.jamClient.SessionCurrrentPlayPosMs();
|
||||
var atEnd = currentPositionMs == 0 || trackDurationMs == currentPositionMs;
|
||||
|
||||
if(atEnd) {
|
||||
context.jamClient.SessionStopPlay();
|
||||
startPlay();
|
||||
}
|
||||
}
|
||||
|
||||
function startPlay() {
|
||||
context.jamClient.SessionTrackSeekMs(0);
|
||||
context.jamClient.SessionStartPlay(1);
|
||||
$directMonitoringBtn.removeClass('playing paused').addClass('playing');
|
||||
|
||||
trackDurationMs = context.jamClient.SessionGetTracksPlayDurationMs();
|
||||
if(!playCheckInterval) {
|
||||
playCheckInterval = setInterval(checkIfPlaying, 333);
|
||||
}
|
||||
isPlaying = true;
|
||||
}
|
||||
|
||||
function stopPlay() {
|
||||
context.jamClient.SessionStopPlay();
|
||||
$directMonitoringBtn.removeClass('playing paused').addClass('paused');
|
||||
isPlaying = false;
|
||||
}
|
||||
|
||||
function togglePlay() {
|
||||
if(isPlaying) {
|
||||
stopPlay();
|
||||
}
|
||||
else {
|
||||
startPlay();
|
||||
}
|
||||
}
|
||||
|
||||
function handleNext() {
|
||||
|
||||
}
|
||||
|
||||
function newSession() {
|
||||
|
||||
}
|
||||
|
||||
function beforeShow() {
|
||||
context.jamClient.SessionRemoveAllPlayTracks();
|
||||
if(!context.jamClient.SessionAddPlayTrack("skin:jktest-audio.wav")) {
|
||||
context.JK.alertSupportedNeeded('Unable to open test sound');
|
||||
}
|
||||
}
|
||||
|
||||
function beforeHide() {
|
||||
if(isPlaying) {
|
||||
stopPlay();
|
||||
}
|
||||
|
||||
context.jamClient.SessionRemoveAllPlayTracks();
|
||||
|
||||
if(playCheckInterval) {
|
||||
clearTimeout(playCheckInterval);
|
||||
playCheckInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
function initialize(_$step) {
|
||||
$step = _$step;
|
||||
|
||||
$directMonitoringBtn = $step.find('.test-direct-monitoring');
|
||||
|
||||
$directMonitoringBtn.on('click', togglePlay);
|
||||
}
|
||||
|
||||
this.handleNext = handleNext;
|
||||
this.newSession = newSession;
|
||||
this.beforeShow = beforeShow;
|
||||
this.beforeHide = beforeHide;
|
||||
this.initialize = initialize;
|
||||
|
||||
return this;
|
||||
}
|
||||
})(window, jQuery);
|
||||
|
|
@ -0,0 +1,466 @@
|
|||
(function (context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.StepNetworkTest = function (app, $dialog) {
|
||||
|
||||
var NETWORK_TEST_TYPES = {
|
||||
RestPhase : 0,
|
||||
PktTest100NormalLatency : 1,
|
||||
PktTest200MediumLatency : 2,
|
||||
PktTest400LowLatency : 3,
|
||||
PktTestRateSweep : 4,
|
||||
RcvOnly : 5
|
||||
}
|
||||
var STARTING_NUM_CLIENTS = 4;
|
||||
var PAYLOAD_SIZE = 100;
|
||||
var MINIMUM_ACCEPTABLE_SESSION_SIZE = 2;
|
||||
|
||||
var rest = context.JK.Rest();
|
||||
var logger = context.JK.logger;
|
||||
var $step = null;
|
||||
var TEST_SUCCESS_CALLBACK = 'JK.HandleNetworkTestSuccess';
|
||||
var TEST_TIMEOUT_CALLBACK = 'JK.HandleNetworkTestTimeout';
|
||||
|
||||
var $startNetworkTestBtn = null;
|
||||
var $testResults = null;
|
||||
var $testScore = null;
|
||||
var $testText = null;
|
||||
var testedSuccessfully = false;
|
||||
var $scoringBar = null;
|
||||
var $goodMarker = null;
|
||||
var $goodLine = null;
|
||||
var $currentScore = null;
|
||||
var $scoredClients = null;
|
||||
var $subscore = null;
|
||||
var backendGuardTimeout = null;
|
||||
|
||||
var serverClientId = '';
|
||||
var isScoring = false;
|
||||
var numClientsToTest = STARTING_NUM_CLIENTS;
|
||||
var testSummary = {attempts : [], final:null}
|
||||
|
||||
var scoringZoneWidth = 100;//px
|
||||
|
||||
|
||||
function reset() {
|
||||
serverClientId = '';
|
||||
isScoring = false;
|
||||
numClientsToTest = STARTING_NUM_CLIENTS;
|
||||
testSummary = {attempts : []};
|
||||
updateControlsState();
|
||||
}
|
||||
|
||||
function renderStartTest() {
|
||||
$scoredClients.empty();
|
||||
$testResults.removeClass('good acceptable bad').addClass('testing');
|
||||
$testText.empty();
|
||||
$subscore.empty();
|
||||
updateControlsState();
|
||||
$currentScore.width(0);
|
||||
$goodLine.css('left', (gon.ftue_packet_rate_treshold * 100) + '%');
|
||||
$goodMarker.css('left', (gon.ftue_packet_rate_treshold * 100) + '%');
|
||||
}
|
||||
|
||||
function renderStopTest(score, text) {
|
||||
$scoredClients.html(score);
|
||||
$testText.html(text);
|
||||
$testResults.removeClass('testing');
|
||||
}
|
||||
|
||||
function postDiagnostic() {
|
||||
rest.createDiagnostic({
|
||||
type: 'NETWORK_TEST_RESULT',
|
||||
data: {client_type: context.JK.clientType(), client_id: context.JK.JamServer.clientID, summary:testSummary}
|
||||
});
|
||||
}
|
||||
|
||||
function testFinished() {
|
||||
var attempt = getCurrentAttempt();
|
||||
|
||||
if(!testSummary.final) {
|
||||
testSummary.final = {reason : attempt.reason};
|
||||
}
|
||||
|
||||
var reason = testSummary.final.reason;
|
||||
|
||||
if(reason == "success") {
|
||||
renderStopTest(attempt.num_clients, "Your router and Internet service will support sessions of up to " + attempt.num_clients + " JamKazam musicians.")
|
||||
testedSuccessfully = true;
|
||||
if(!testSummary.final.num_clients) {
|
||||
testSummary.final.num_clients = attempt.num_clients;
|
||||
}
|
||||
context.JK.GA.trackNetworkTest(context.JK.detectOS(), testSummary.final.num_clients);
|
||||
context.jamClient.SetNetworkTestScore(attempt.num_clients);
|
||||
if(testSummary.final.num_clients == 2) {
|
||||
$testResults.addClass('acceptable');
|
||||
}
|
||||
else {
|
||||
$testResults.addClass('good');
|
||||
}
|
||||
}
|
||||
else if(reason == "minimum_client_threshold") {
|
||||
context.jamClient.SetNetworkTestScore(0);
|
||||
renderStopTest('', "We're sorry, but your router and Internet service will not effectively support JamKazam sessions. Please click the HELP button for more information.")
|
||||
}
|
||||
else if(reason == "unreachable") {
|
||||
context.jamClient.SetNetworkTestScore(0);
|
||||
renderStopTest('', "We're sorry, but your router will not support JamKazam in its current configuration. Please click the HELP button for more information.");
|
||||
}
|
||||
else if(reason == "internal_error") {
|
||||
context.JK.alertSupportedNeeded("The JamKazam client software had an unexpected problem while scoring your Internet connection.");
|
||||
renderStopTest('', '');
|
||||
}
|
||||
else if(reason == "remote_peer_cant_test") {
|
||||
context.JK.alertSupportedNeeded("The JamKazam service is experiencing technical difficulties.");
|
||||
renderStopTest('', '');
|
||||
}
|
||||
else if(reason == 'backend_gone') {
|
||||
context.JK.alertSupportedNeeded("The JamKazam client is experiencing technical difficulties.");
|
||||
renderStopTest('', '');
|
||||
}
|
||||
else if(reason == "invalid_response") {
|
||||
context.JK.alertSupportedNeeded("The JamKazam client software had an unexpected problem while scoring your Internet connection. Reason=" + attempt.backend_data.reason + '.');
|
||||
renderStopTest('', '');
|
||||
}
|
||||
else if(reason == 'no_servers') {
|
||||
context.JK.alertSupportedNeeded("No network test servers are available. You can skip this step for now.");
|
||||
renderStopTest('', '');
|
||||
testedSuccessfully = true;
|
||||
}
|
||||
else if(reason == 'no_network') {
|
||||
context.JK.Banner.showAlert("Please try again later. Your network appears down.");
|
||||
renderStopTest('', '');
|
||||
}
|
||||
else if(reason == "rest_api_error") {
|
||||
context.JK.alertSupportedNeeded("Unable to acquire a network test server. You can skip this step for now.");
|
||||
testedSuccessfully = true;
|
||||
renderStopTest('', '');
|
||||
}
|
||||
else if(reason == "timeout") {
|
||||
context.JK.alertSupportedNeeded("Communication with a network test servers timed out. You can skip this step for now.");
|
||||
testedSuccessfully = true;
|
||||
renderStopTest('', '');
|
||||
}
|
||||
else {
|
||||
context.JK.alertSupportedNeeded("The JamKazam client software had a logic error while scoring your Internet connection.");
|
||||
renderStopTest('', '');
|
||||
}
|
||||
|
||||
numClientsToTest = STARTING_NUM_CLIENTS;
|
||||
isScoring = false;
|
||||
updateControlsState();
|
||||
postDiagnostic();
|
||||
}
|
||||
|
||||
function getCurrentAttempt() {
|
||||
return testSummary.attempts[testSummary.attempts.length - 1];
|
||||
}
|
||||
|
||||
function backendTimedOut() {
|
||||
testSummary.final = {reason: 'backend_gone'}
|
||||
testFinished();
|
||||
}
|
||||
|
||||
function clearBackendGuard() {
|
||||
if(backendGuardTimeout) {
|
||||
clearTimeout(backendGuardTimeout);
|
||||
backendGuardTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
function attemptTestPass() {
|
||||
|
||||
var attempt = {};
|
||||
attempt.payload_size = PAYLOAD_SIZE;
|
||||
attempt.duration = gon.ftue_network_test_duration;
|
||||
attempt.test_type = 'PktTest400LowLatency';
|
||||
attempt.num_clients = numClientsToTest;
|
||||
attempt.server_client_id = serverClientId;
|
||||
attempt.received_progress = false;
|
||||
testSummary.attempts.push(attempt);
|
||||
|
||||
//context.jamClient.StopNetworkTest('');
|
||||
|
||||
$testText.text("Simulating the network traffic of a " + numClientsToTest + "-person session.");
|
||||
|
||||
updateProgress(0, false);
|
||||
|
||||
backendGuardTimeout = setTimeout(function(){backendTimedOut()}, (gon.ftue_network_test_duration + 1) * 1000);
|
||||
|
||||
context.jamClient.TestNetworkPktBwRate(serverClientId, TEST_SUCCESS_CALLBACK, TEST_TIMEOUT_CALLBACK,
|
||||
NETWORK_TEST_TYPES.PktTest400LowLatency,
|
||||
gon.ftue_network_test_duration,
|
||||
numClientsToTest,
|
||||
PAYLOAD_SIZE);
|
||||
}
|
||||
|
||||
|
||||
function startNetworkTest(checkWireless) {
|
||||
|
||||
if(isScoring) return false;
|
||||
|
||||
if(checkWireless) {
|
||||
// check if on Wifi 1st
|
||||
var isWireless = context.jamClient.IsMyNetworkWireless();
|
||||
if(isWireless == -1) {
|
||||
logger.warn("unable to determine if the user is on wireless or not for network test. skipping prompt.")
|
||||
}
|
||||
else if(isWireless == 1) {
|
||||
context.JK.Banner.showAlert({buttons: [
|
||||
{name: 'RUN NETWORK TEST ANYWAY', click: function() {startNetworkTest(false)}},
|
||||
{name: 'CANCEL', click: function() {}}],
|
||||
html: "<p>It appears that your computer is connected to your network using WiFi.</p>" +
|
||||
"<p>We strongly advise against running the JamKazam application on a WiFi connection. " +
|
||||
"We recommend using a wired Ethernet connection from your computer to your router. " +
|
||||
"A WiFi connection is likely to cause significant issues in both latency and audio quality.</p>"})
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
reset();
|
||||
isScoring = true;
|
||||
renderStartTest();
|
||||
rest.getLatencyTester()
|
||||
.done(function(response) {
|
||||
// ensure there are no tests ongoing
|
||||
|
||||
serverClientId = response.client_id;
|
||||
|
||||
logger.info("beginning network test against client_id: " + serverClientId);
|
||||
|
||||
attemptTestPass();
|
||||
})
|
||||
.fail(function(jqXHR) {
|
||||
if(jqXHR.status == 404) {
|
||||
// means there are no network testers available.
|
||||
// we have to skip this part of the UI
|
||||
testSummary.final = {reason: 'no_servers'}
|
||||
}
|
||||
else {
|
||||
if(context.JK.isNetworkError(arguments)) {
|
||||
testSummary.final = {reason: 'no_network'}
|
||||
}
|
||||
else {
|
||||
testSummary.final = {reason: 'rest_api_error'}
|
||||
}
|
||||
}
|
||||
testFinished();
|
||||
})
|
||||
logger.info("starting network test");
|
||||
return false;
|
||||
}
|
||||
|
||||
function updateProgress(throughput, showSubscore) {
|
||||
var width = throughput * 100;
|
||||
|
||||
$currentScore.stop().data('showSubscore', showSubscore);
|
||||
|
||||
if(!showSubscore) {
|
||||
$subscore.text('');
|
||||
}
|
||||
|
||||
$currentScore.animate({
|
||||
duration: 1000,
|
||||
width: width + '%'
|
||||
}, {
|
||||
step: function (now, fx) {
|
||||
if(showSubscore) {
|
||||
var newWidth = ( 100 * parseFloat($currentScore.css('width')) / parseFloat($currentScore.parent().css('width')) );
|
||||
$subscore.text((Math.round(newWidth * 10) / 10) + '%');
|
||||
}
|
||||
}
|
||||
}).css('overflow', 'visible');
|
||||
;
|
||||
}
|
||||
|
||||
function networkTestSuccess(data) {
|
||||
clearBackendGuard();
|
||||
|
||||
var attempt = getCurrentAttempt();
|
||||
|
||||
function refineTest(up) {
|
||||
if(up) {
|
||||
if(numClientsToTest == gon.ftue_network_test_max_clients) {
|
||||
attempt.reason = "success";
|
||||
testFinished();
|
||||
}
|
||||
else {
|
||||
numClientsToTest++;
|
||||
logger.debug("increasing number of clients to " + numClientsToTest);
|
||||
setTimeout(attemptTestPass, 500); // wait a second to avoid race conditions with client/server comm
|
||||
}
|
||||
}
|
||||
else {
|
||||
// reduce numclients if we can
|
||||
if(numClientsToTest == MINIMUM_ACCEPTABLE_SESSION_SIZE) {
|
||||
// we are too low already. fail the user
|
||||
attempt.reason = "minimum_client_threshold";
|
||||
testFinished();
|
||||
}
|
||||
else if(numClientsToTest > STARTING_NUM_CLIENTS) {
|
||||
// this means we've gone up before... so don't go back down (i.e., creating a loop)
|
||||
attempt.reason = "success";
|
||||
testSummary.final = { reason:'success', num_clients: numClientsToTest - 1 }
|
||||
testFinished();
|
||||
}
|
||||
else {
|
||||
numClientsToTest--;
|
||||
logger.debug("reducing number of clients to " + numClientsToTest);
|
||||
setTimeout(attemptTestPass, 500); // wait a second to avoid race conditions with client/server comm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attempt.backend_data = data;
|
||||
|
||||
if(data.progress === true) {
|
||||
|
||||
var animate = true;
|
||||
if(data.downthroughput && data.upthroughput) {
|
||||
|
||||
if(data.downthroughput > 0 || data.upthroughput > 0) {
|
||||
attempt.received_progress = true;
|
||||
animate = true;
|
||||
}
|
||||
|
||||
if(attempt.received_progress) {
|
||||
// take the lower
|
||||
var throughput= data.downthroughput < data.upthroughput ? data.downthroughput : data.upthroughput;
|
||||
|
||||
updateProgress(throughput, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
logger.debug("network test pass success. data: ", data);
|
||||
|
||||
if(data.reason == "unreachable") {
|
||||
// STUN
|
||||
logger.debug("network test: unreachable (STUN issue or similar)");
|
||||
attempt.reason = data.reason;
|
||||
testFinished();
|
||||
}
|
||||
else if(data.reason == "internal_error") {
|
||||
// oops
|
||||
logger.debug("network test: internal_error (client had a unexpected problem)");
|
||||
attempt.reason = data.reason;
|
||||
testFinished();
|
||||
}
|
||||
else if(data.reason == "remote_peer_cant_test") {
|
||||
// old client
|
||||
logger.debug("network test: remote_peer_cant_test (old client)")
|
||||
attempt.reason = data.reason;
|
||||
testFinished();
|
||||
}
|
||||
else {
|
||||
if(!data.downthroughput || !data.upthroughput) {
|
||||
// we have to assume this is bad. just not a reason we know about in code
|
||||
logger.debug("network test: no test data (unknown issue? " + data.reason + ")")
|
||||
attempt.reason = "invalid_response";
|
||||
testFinished();
|
||||
}
|
||||
else {
|
||||
// success... but we still have to verify if this data is within threshold
|
||||
if(data.downthroughput < gon.ftue_packet_rate_treshold) {
|
||||
logger.debug("network test: downthroughput too low. downthroughput: " + data.downthroughput + ", threshold: " + gon.ftue_packet_rate_treshold);
|
||||
refineTest(false);
|
||||
}
|
||||
else if(data.upthroughput < gon.ftue_packet_rate_treshold) {
|
||||
logger.debug("network test: upthroughput too low. upthroughput: " + data.upthroughput + ", threshold: " + gon.ftue_packet_rate_treshold);
|
||||
refineTest(false);
|
||||
}
|
||||
else {
|
||||
// true success. we can accept this score
|
||||
logger.debug("network test: success")
|
||||
refineTest(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VRFS-1742
|
||||
// context.jamClient.StopNetworkTest(serverClientId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function networkTestTimeout(data) {
|
||||
clearBackendGuard();
|
||||
|
||||
logger.warn("network timeout when testing latency test: " + data);
|
||||
|
||||
var attempt = getCurrentAttempt();
|
||||
attempt.reason = 'timeout';
|
||||
attempt.backend_data = data;
|
||||
testFinished();
|
||||
}
|
||||
|
||||
function hasScoredNetworkSuccessfully() {
|
||||
return testedSuccessfully;
|
||||
}
|
||||
|
||||
function configureStartButton() {
|
||||
if(isScoring) {
|
||||
$startNetworkTestBtn.text('NETWORK TEST RUNNING...').removeClass('button-orange').addClass('button-grey')
|
||||
}
|
||||
else {
|
||||
$startNetworkTestBtn.text('START NETWORK TEST').removeClass('button-grey').addClass('button-orange');
|
||||
}
|
||||
|
||||
}
|
||||
function updateControlsState() {
|
||||
initializeNextButtonState();
|
||||
initializeBackButtonState();
|
||||
configureStartButton();
|
||||
}
|
||||
|
||||
function initializeNextButtonState() {
|
||||
$dialog.setNextState(hasScoredNetworkSuccessfully() && !isScoring);
|
||||
}
|
||||
|
||||
function initializeBackButtonState() {
|
||||
$dialog.setBackState(!isScoring);
|
||||
}
|
||||
|
||||
function newSession() {
|
||||
reset();
|
||||
//context.jamClient.StopNetworkTest('');
|
||||
}
|
||||
|
||||
function beforeShow() {
|
||||
reset();
|
||||
}
|
||||
|
||||
function beforeHide() {
|
||||
clearBackendGuard();
|
||||
}
|
||||
|
||||
function initialize(_$step) {
|
||||
$step = _$step;
|
||||
|
||||
$startNetworkTestBtn = $step.find('.start-network-test');
|
||||
$testResults = $step.find('.network-test-results');
|
||||
$testScore = $step.find('.network-test-score');
|
||||
$testText = $step.find('.network-test-text');
|
||||
$scoringBar = $step.find('.scoring-bar');
|
||||
$goodMarker = $step.find('.good-marker');
|
||||
$goodLine =$step.find('.good-line');
|
||||
$currentScore = $step.find('.current-score');
|
||||
$scoredClients= $step.find('.scored-clients');
|
||||
$subscore = $step.find('.subscore');
|
||||
$startNetworkTestBtn.on('click', startNetworkTest);
|
||||
}
|
||||
|
||||
this.newSession = newSession;
|
||||
this.beforeHide = beforeHide;
|
||||
this.beforeShow = beforeShow;
|
||||
this.initialize = initialize;
|
||||
|
||||
context.JK.HandleNetworkTestSuccess = networkTestSuccess; // pin to global for bridge callback
|
||||
context.JK.HandleNetworkTestTimeout = networkTestTimeout; // pin to global for bridge callback
|
||||
|
||||
return this;
|
||||
}
|
||||
})(window, jQuery);
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,23 @@
|
|||
(function (context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.StepSuccess = function (app, $dialog) {
|
||||
|
||||
var $step = null;
|
||||
|
||||
function beforeShow() {
|
||||
|
||||
}
|
||||
|
||||
function initialize(_$step) {
|
||||
$step = _$step;
|
||||
}
|
||||
|
||||
this.beforeShow = beforeShow;
|
||||
this.initialize = initialize;
|
||||
|
||||
return this;
|
||||
}
|
||||
})(window, jQuery);
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
(function (context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.StepUnderstandGear = function (app) {
|
||||
|
||||
var $step = null;
|
||||
var operatingSystem;
|
||||
|
||||
function beforeShow() {
|
||||
var $watchVideo = $step.find('.watch-video');
|
||||
var videoUrl = 'https://www.youtube.com/watch?v=VexH4834o9I';
|
||||
if (operatingSystem == "Win32") {
|
||||
$watchVideo.attr('href', 'https://www.youtube.com/watch?v=VexH4834o9I');
|
||||
}
|
||||
$watchVideo.attr('href', videoUrl);
|
||||
}
|
||||
|
||||
function initialize(_$step) {
|
||||
$step = _$step;
|
||||
|
||||
operatingSystem = context.JK.GetOSAsString();
|
||||
}
|
||||
|
||||
this.beforeShow = beforeShow;
|
||||
this.initialize = initialize;
|
||||
|
||||
return this;
|
||||
}
|
||||
})(window, jQuery);
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
(function (context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.LoopbackWizard = function (app) {
|
||||
|
||||
var logger = context.JK.logger;
|
||||
|
||||
var $dialog = null;
|
||||
var wizard = null;
|
||||
var $wizardSteps = null;
|
||||
|
||||
var step1 = new context.JK.Step1(app, this);
|
||||
var step2 = new context.JK.Step2(app, this);
|
||||
var step3 = new context.JK.Step3(app, this);
|
||||
|
||||
var STEPS = {
|
||||
0: step1,
|
||||
1: step2,
|
||||
2: step3
|
||||
}
|
||||
|
||||
function beforeShow(args) {
|
||||
wizard.onBeforeShow(args);
|
||||
}
|
||||
|
||||
function afterHide() {
|
||||
wizard.onAfterHide();
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
wizard.onCloseDialog();
|
||||
app.layout.closeDialog('your-wizard');
|
||||
}
|
||||
|
||||
function setNextState(enabled) {
|
||||
wizard.setNextState(enabled);
|
||||
}
|
||||
|
||||
function setBackState(enabled) {
|
||||
wizard.setBackState(enabled);
|
||||
}
|
||||
|
||||
|
||||
function onStepChanged(e, data) {
|
||||
var step = wizard.getCurrentStep();
|
||||
var $currentWizardStep = wizard.getCurrentWizardStep();
|
||||
}
|
||||
|
||||
|
||||
function onCanceled() {
|
||||
closeDialog();
|
||||
return false;
|
||||
}
|
||||
|
||||
function onClosed() {
|
||||
closeDialog();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function events() {
|
||||
$(wizard).on('step_changed', onStepChanged);
|
||||
$(wizard).on('wizard_cancel', onCanceled);
|
||||
$(wizard).on('wizard_close', onClosed);
|
||||
}
|
||||
|
||||
|
||||
function initialize() {
|
||||
|
||||
|
||||
var dialogBindings = { beforeShow: beforeShow, afterHide: afterHide };
|
||||
|
||||
app.bindDialog('your-wizard', dialogBindings);
|
||||
$dialog = $('#your-wizard');
|
||||
$wizardSteps = $dialog.find('.wizard-step');
|
||||
|
||||
step1.initialize($wizardSteps.filter($('[layout-wizard-step=0]')));
|
||||
step2.initialize($wizardSteps.filter($('[layout-wizard-step=1]')));
|
||||
step3.initialize($wizardSteps.filter($('[layout-wizard-step=2]')));
|
||||
|
||||
wizard = new context.JK.Wizard(app);
|
||||
wizard.initialize($dialog, $wizardSteps, STEPS);
|
||||
|
||||
events();
|
||||
}
|
||||
|
||||
this.setNextState = setNextState;
|
||||
this.setBackState = setBackState;
|
||||
this.initialize = initialize;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
})(window, jQuery);
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
(function (context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.Wizard = function (app) {
|
||||
|
||||
var STEPS = null;
|
||||
var step = null;
|
||||
var previousStep = null;
|
||||
var $dialog = null;
|
||||
var $templateButtons = null;
|
||||
var $wizardSteps = null;
|
||||
var $currentWizardStep = null;
|
||||
var $wizardButtons = null;
|
||||
var $btnHelp = null;
|
||||
var $btnNext = null;
|
||||
var $btnBack = null;
|
||||
var $btnClose = null;
|
||||
var $btnCancel = null;
|
||||
var self = this;
|
||||
var $self = $(this);
|
||||
|
||||
function totalSteps() {
|
||||
return context.JK.dkeys(STEPS).length;
|
||||
}
|
||||
|
||||
function beforeHideStep() {
|
||||
var currentStep = getCurrentStep();
|
||||
if(currentStep === null) return;
|
||||
|
||||
var stepInfo = STEPS[currentStep];
|
||||
|
||||
if (!stepInfo) {
|
||||
throw "unknown step: " + currentStep;
|
||||
}
|
||||
|
||||
if(stepInfo.beforeHide) {
|
||||
stepInfo.beforeHide.call(stepInfo);
|
||||
}
|
||||
}
|
||||
|
||||
function beforeShowStep($step) {
|
||||
var stepInfo = STEPS[step];
|
||||
|
||||
if (!stepInfo) {
|
||||
throw "unknown step: " + step;
|
||||
}
|
||||
|
||||
stepInfo.beforeShow.call(stepInfo);
|
||||
}
|
||||
|
||||
function back() {
|
||||
if ($(this).is('.button-grey')) return false;
|
||||
previousStep = step;
|
||||
step = step - 1;
|
||||
moveToStep();
|
||||
return false;
|
||||
}
|
||||
|
||||
function next() {
|
||||
if ($(this).is('.button-grey')) return false;
|
||||
|
||||
var stepInfo = STEPS[step];
|
||||
if(stepInfo.handleNext) {
|
||||
var result = stepInfo.handleNext.call(stepInfo);
|
||||
if(result === false) {return false;}
|
||||
}
|
||||
|
||||
previousStep = step;
|
||||
step = step + 1;
|
||||
|
||||
moveToStep();
|
||||
return false;
|
||||
}
|
||||
|
||||
function moveToStep() {
|
||||
var $nextWizardStep = $wizardSteps.filter($('[layout-wizard-step=' + step + ']'));
|
||||
|
||||
beforeHideStep();
|
||||
|
||||
$wizardSteps.hide();
|
||||
|
||||
$currentWizardStep = $nextWizardStep;
|
||||
|
||||
context.JK.GA.virtualPageView(location.pathname + location.search + location.hash + '/d1=' + step, $currentWizardStep.attr('dialog-title'));
|
||||
|
||||
$self.triggerHandler('step_changed', {step:step});
|
||||
|
||||
// update buttons
|
||||
var $wizardButtonsContent = $(context._.template($templateButtons.html(), {}, {variable: 'data'}));
|
||||
|
||||
$btnHelp = $wizardButtonsContent.find('.btn-help');
|
||||
$btnBack = $wizardButtonsContent.find('.btn-back');
|
||||
$btnNext = $wizardButtonsContent.find('.btn-next');
|
||||
$btnClose = $wizardButtonsContent.find('.btn-close');
|
||||
$btnCancel = $wizardButtonsContent.find('.btn-cancel');
|
||||
|
||||
// hide help button if on last step
|
||||
if (step == totalSteps() - 1) {
|
||||
$btnHelp.hide();
|
||||
}
|
||||
// hide back button if 1st step or last step
|
||||
if (step == 0 || step == totalSteps() - 1) {
|
||||
$btnBack.hide();
|
||||
}
|
||||
// hide next button if on last step
|
||||
if (step == totalSteps() - 1) {
|
||||
$btnNext.hide();
|
||||
}
|
||||
// hide close if not on last step
|
||||
if (step != totalSteps() - 1) {
|
||||
$btnClose.hide();
|
||||
}
|
||||
// hide cancel if not on last step
|
||||
if (step == totalSteps() - 1) {
|
||||
$btnCancel.hide();
|
||||
}
|
||||
|
||||
$btnNext.on('click', next);
|
||||
$btnBack.on('click', back);
|
||||
$btnClose.on('click', function() {$self.triggerHandler('wizard_close'); return false;});
|
||||
$btnCancel.on('click', function() {$self.triggerHandler('wizard_cancel'); return false;});
|
||||
|
||||
$wizardButtons.empty();
|
||||
$wizardButtons.append($wizardButtonsContent);
|
||||
|
||||
beforeShowStep($currentWizardStep);
|
||||
$currentWizardStep.show();
|
||||
|
||||
}
|
||||
|
||||
// called by owner whenever
|
||||
function onCloseDialog() {
|
||||
beforeHideStep();
|
||||
}
|
||||
|
||||
function onBeforeShow(args) {
|
||||
|
||||
$currentWizardStep = null;
|
||||
previousStep = null;
|
||||
|
||||
step = args != null ? args.d1 : 0;
|
||||
if (!step) step = 0;
|
||||
step = parseInt(step);
|
||||
moveToStep();
|
||||
}
|
||||
|
||||
function onAfterHide() {
|
||||
step = null;
|
||||
}
|
||||
|
||||
function setNextState(enabled) {
|
||||
|
||||
if(!$btnNext) return;
|
||||
|
||||
$btnNext.removeClass('button-orange button-grey');
|
||||
|
||||
if (enabled) {
|
||||
$btnNext.addClass('button-orange');
|
||||
}
|
||||
else {
|
||||
$btnNext.addClass('button-grey');
|
||||
}
|
||||
}
|
||||
|
||||
function setBackState(enabled) {
|
||||
|
||||
if(!$btnBack) return;
|
||||
|
||||
$btnBack.removeClass('button-orange button-grey');
|
||||
|
||||
if (enabled) {
|
||||
$btnBack.addClass('button-orange');
|
||||
}
|
||||
else {
|
||||
$btnBack.addClass('button-grey');
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentStep() {
|
||||
if($currentWizardStep) {
|
||||
return parseInt($currentWizardStep.attr('layout-wizard-step'));
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getCurrentWizardStep() {
|
||||
return $currentWizardStep;
|
||||
}
|
||||
|
||||
function initialize(_$dialog, _$wizardSteps, _STEPS) {
|
||||
$dialog = _$dialog;
|
||||
$wizardSteps = _$wizardSteps;
|
||||
STEPS = _STEPS;
|
||||
|
||||
$wizardButtons = $dialog.find('.wizard-buttons');
|
||||
$templateButtons = $('#template-wizard-buttons');
|
||||
}
|
||||
|
||||
this.setNextState = setNextState;
|
||||
this.setBackState = setBackState;
|
||||
this.getCurrentStep = getCurrentStep;
|
||||
this.getCurrentWizardStep = getCurrentWizardStep;
|
||||
this.onCloseDialog = onCloseDialog;
|
||||
this.onBeforeShow = onBeforeShow;
|
||||
this.onAfterHide = onAfterHide;
|
||||
this.initialize = initialize;
|
||||
|
||||
}
|
||||
|
||||
})(window, jQuery);
|
||||
|
|
@ -237,6 +237,14 @@
|
|||
.actions {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td span[data-current="true"] {
|
||||
font-size:11px;
|
||||
}
|
||||
|
||||
tr[data-status="bad"] {
|
||||
color:grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,5 +26,26 @@
|
|||
.close-btn {
|
||||
display:none;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style:disc;
|
||||
margin-left:20px;
|
||||
}
|
||||
li {
|
||||
margin: 15px 12px 15px 36px;
|
||||
}
|
||||
|
||||
.end-content {
|
||||
height: 0;
|
||||
line-height: 0;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: " ";
|
||||
}
|
||||
|
||||
p {
|
||||
line-height:20px;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@
|
|||
*= require ./search
|
||||
*= require ./ftue
|
||||
*= require ./jamServer
|
||||
*= require ./gearWizard
|
||||
*= require ./wizard/wizard
|
||||
*= require ./wizard/gearWizard
|
||||
*= require ./whatsNextDialog
|
||||
*= require ./invitationDialog
|
||||
*= require ./shareDialog
|
||||
|
|
@ -46,6 +47,7 @@
|
|||
*= require ./textMessageDialog
|
||||
*= require ./acceptFriendRequestDialog
|
||||
*= require ./launchAppDialog
|
||||
*= require ./iconInstrumentSelect
|
||||
*= require ./terms
|
||||
*= require ./createSession
|
||||
*= require ./feed
|
||||
|
|
|
|||
|
|
@ -83,15 +83,14 @@ div.dialog.ftue .ftue-inner div[layout-wizard-step="0"] {
|
|||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
|
||||
div.dialog.ftue {
|
||||
min-width: 800px;
|
||||
max-width: 800px;
|
||||
min-height: 400px;
|
||||
max-height: 400px;
|
||||
min-height: 500px;
|
||||
max-height: 500px;
|
||||
width: 800px;
|
||||
|
||||
.ftue-inner {
|
||||
line-height: 1.3em;
|
||||
width: auto;
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
.icon-instrument-select {
|
||||
.current-instrument {
|
||||
width:24px;
|
||||
height:24px;
|
||||
font-size:20px;
|
||||
cursor:pointer;
|
||||
|
||||
&.none {
|
||||
height:22px;
|
||||
width:22px;
|
||||
border:1px solid white;
|
||||
-webkit-border-radius:12px;
|
||||
-moz-border-radius:12px;
|
||||
border-radius:12px;
|
||||
line-height:22px;
|
||||
vertical-align: middle;
|
||||
text-align:center;
|
||||
}
|
||||
}
|
||||
ul {
|
||||
display:none;
|
||||
border:1px solid white;
|
||||
max-height:250px;
|
||||
position:absolute;
|
||||
overflow:auto;
|
||||
width:150px;
|
||||
background-color:#333;
|
||||
text-align:left;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-instrument-selector-popup {
|
||||
.bt-content {
|
||||
height:150px;
|
||||
width:150px;
|
||||
background-color:#333;
|
||||
text-align:left;
|
||||
overflow:auto;
|
||||
border:1px solid #ED3618;
|
||||
|
||||
li {
|
||||
margin-left:5px;
|
||||
list-style-type: none;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,6 +3,10 @@
|
|||
text-align:center;
|
||||
width:100%;
|
||||
position:absolute;
|
||||
|
||||
&.active {
|
||||
display:block;
|
||||
}
|
||||
}
|
||||
|
||||
.server-connection {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,31 @@
|
|||
overflow-x:hidden;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
|
||||
.track {
|
||||
width:70px;
|
||||
height:290px;
|
||||
display:inline-block;
|
||||
margin-right:8px;
|
||||
position:relative;
|
||||
background-color:#242323;
|
||||
|
||||
.disabled-track-overlay {
|
||||
width:100%;
|
||||
height:100%;
|
||||
position:absolute;
|
||||
background-color:#555;
|
||||
opacity:0.5;
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
.track-instrument {
|
||||
position:absolute;
|
||||
top:85px;
|
||||
left:12px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -25,16 +50,6 @@
|
|||
color:#666;
|
||||
}
|
||||
|
||||
|
||||
.track {
|
||||
width:70px;
|
||||
height:290px;
|
||||
display:inline-block;
|
||||
margin-right:8px;
|
||||
position:relative;
|
||||
background-color:#242323;
|
||||
}
|
||||
|
||||
.track-empty {
|
||||
min-width:230px;
|
||||
height:201px;
|
||||
|
|
@ -268,23 +283,6 @@ table.vu td {
|
|||
}
|
||||
|
||||
|
||||
.track {
|
||||
width:70px;
|
||||
height:290px;
|
||||
display:inline-block;
|
||||
margin-right:8px;
|
||||
position:relative;
|
||||
background-color:#242323;
|
||||
|
||||
.disabled-track-overlay {
|
||||
width:100%;
|
||||
height:100%;
|
||||
position:absolute;
|
||||
background-color:#555;
|
||||
opacity:0.5;
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.track-empty {
|
||||
|
|
@ -412,11 +410,6 @@ table.vu td {
|
|||
border-radius:22px;
|
||||
}
|
||||
|
||||
.track-instrument {
|
||||
position:absolute;
|
||||
top:85px;
|
||||
left:12px;
|
||||
}
|
||||
|
||||
.track-gain {
|
||||
position:absolute;
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@
|
|||
.chat-message-sender {
|
||||
font-weight:bold;
|
||||
margin-right:10px;
|
||||
color: #020C81;
|
||||
color: #ED3618;
|
||||
|
||||
&:after {
|
||||
content:':'
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
margin-right: 10px;
|
||||
font-family: helvetica;
|
||||
text-decoration: none;
|
||||
line-height: 27px;
|
||||
|
||||
&.active {
|
||||
background-color:#ed3618;
|
||||
|
|
@ -57,52 +58,12 @@
|
|||
margin-top:20px;
|
||||
}
|
||||
|
||||
.ftue-buttons {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width:100%;
|
||||
@include border_box_sizing;
|
||||
padding-right:45px;
|
||||
padding-bottom:15px;
|
||||
|
||||
}
|
||||
|
||||
.ftue-buttons-holder {
|
||||
float:right;
|
||||
}
|
||||
|
||||
.wizard-step {
|
||||
display:none;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.wizard-step-content {
|
||||
margin-top:20px;
|
||||
padding-bottom:12px;
|
||||
|
||||
.wizard-step-column{
|
||||
position:relative;
|
||||
float:left;
|
||||
vertical-align:top;
|
||||
@include border_box_sizing;
|
||||
padding-right:12px;
|
||||
height:300px;
|
||||
}
|
||||
|
||||
.wizard-step-column:last-child {
|
||||
padding-right:0;
|
||||
}
|
||||
|
||||
p:nth-of-type(1) {
|
||||
margin-top:0;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #FFFFFF;
|
||||
font-size: 15px;
|
||||
font-weight: normal;
|
||||
margin-bottom: 6px;
|
||||
white-space:nowrap;
|
||||
}
|
||||
.ftue-box {
|
||||
background-color: #222222;
|
||||
|
|
@ -115,10 +76,18 @@
|
|||
|
||||
&.list.ports {
|
||||
height:100px;
|
||||
overflow:auto;
|
||||
}
|
||||
|
||||
&.instructions {
|
||||
height: 230px !important
|
||||
height: 268px !important;
|
||||
line-height:16px;
|
||||
@include border_box_sizing;
|
||||
|
||||
.video-type {
|
||||
font-size:10px;
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
ul li {
|
||||
|
|
@ -127,7 +96,7 @@
|
|||
}
|
||||
|
||||
.watch-video {
|
||||
margin:8px 0 15px 0;
|
||||
margin:8px 0 3px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,6 +112,18 @@
|
|||
width:25%;
|
||||
}
|
||||
|
||||
.watch-video.audio-input {
|
||||
margin-top:29px;
|
||||
|
||||
&.audio-output-showing {
|
||||
margin-top:10px;
|
||||
}
|
||||
}
|
||||
|
||||
.watch-video.audio-output {
|
||||
margin-top:10px;
|
||||
}
|
||||
|
||||
.select-audio-input-device {
|
||||
margin-bottom:20px;
|
||||
}
|
||||
|
|
@ -151,18 +132,32 @@
|
|||
margin-bottom:20px;
|
||||
}
|
||||
|
||||
.asio-settings-btn, .resync-btn {
|
||||
.asio-settings-input-btn, .asio-settings-output-btn, .resync-btn {
|
||||
width:80%;
|
||||
display:inline-block;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.asio-settings-btn {
|
||||
.asio-settings-input-btn, .asio-settings-output-btn {
|
||||
margin-top:10px;
|
||||
}
|
||||
|
||||
.asio-settings-input-btn {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.asio-settings-output-btn {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.resync-btn {
|
||||
margin-top:10px;
|
||||
visibility:hidden;
|
||||
}
|
||||
|
||||
.frame-and-buffers {
|
||||
display:none;
|
||||
margin-top:5px;
|
||||
}
|
||||
|
||||
.framesize {
|
||||
|
|
@ -171,11 +166,6 @@
|
|||
width:30%;
|
||||
}
|
||||
|
||||
.buffers {
|
||||
float:left;
|
||||
width:60%;
|
||||
}
|
||||
|
||||
.select-buffer-in {
|
||||
width:45%;
|
||||
}
|
||||
|
|
@ -186,6 +176,16 @@
|
|||
|
||||
|
||||
.buffers {
|
||||
float:left;
|
||||
width:60%;
|
||||
|
||||
h2 {
|
||||
margin-left:5px;
|
||||
}
|
||||
|
||||
.dropdown-container {
|
||||
width:48px;
|
||||
}
|
||||
.easydropdown-wrapper:nth-of-type(1) {
|
||||
left:5px;
|
||||
}
|
||||
|
|
@ -193,15 +193,41 @@
|
|||
left:35px;
|
||||
}
|
||||
.easydropdown, .easydropdown-wrapper {
|
||||
width:15px;
|
||||
width:30px;
|
||||
}
|
||||
}
|
||||
|
||||
.audio-port {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.audio-channels {
|
||||
margin-top:15px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.ftue-box.results {
|
||||
height: 230px !important;
|
||||
height: 268px !important;
|
||||
padding:0;
|
||||
@include border_box_sizing;
|
||||
|
||||
.io, .latency {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.loopback-button-holder {
|
||||
width:100%;
|
||||
text-align:center;
|
||||
}
|
||||
a.loopback-test {
|
||||
margin-top:10px;
|
||||
}
|
||||
|
||||
.unknown-text {
|
||||
display:none;
|
||||
padding:8px;
|
||||
}
|
||||
|
||||
.scoring-section {
|
||||
font-size:15px;
|
||||
|
|
@ -214,12 +240,17 @@
|
|||
&.acceptable {
|
||||
background-color:#cc9900;
|
||||
}
|
||||
&.bad, &.skip {
|
||||
&.bad {
|
||||
background-color:#660000;
|
||||
}
|
||||
&.unknown {
|
||||
&.unknown, &.skip {
|
||||
background-color:#999;
|
||||
}
|
||||
&.skip {
|
||||
.io-skip-msg {
|
||||
display:inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.io-countdown {
|
||||
|
|
@ -236,9 +267,71 @@
|
|||
|
||||
.io-skip-msg {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.scoring-section.skip & {
|
||||
display:inline;
|
||||
.io-rate {
|
||||
display:none;
|
||||
}
|
||||
.io-var {
|
||||
display:none;
|
||||
}
|
||||
|
||||
ul.results-text {
|
||||
padding:10px 8px;
|
||||
|
||||
li {
|
||||
display:none
|
||||
}
|
||||
|
||||
&[latency-score="unknown"] {
|
||||
display:none;
|
||||
}
|
||||
&[latency-score="good"] li.latency-good {
|
||||
display:list-item;
|
||||
}
|
||||
&[latency-score="acceptable"] li.latency-acceptable {
|
||||
display:list-item;
|
||||
}
|
||||
&[latency-score="bad"] li.latency-bad {
|
||||
display:list-item;
|
||||
}
|
||||
&[io-var-score="good"] li.io-var-good {
|
||||
display:list-item;
|
||||
}
|
||||
&[io-var-score="acceptable"] li.io-var-acceptable {
|
||||
display:list-item;
|
||||
}
|
||||
&[io-var-score="bad"] li.io-var-bad {
|
||||
display:list-item;
|
||||
}
|
||||
&[io-rate-score="good"] li.io-rate-good {
|
||||
display:list-item;
|
||||
}
|
||||
&[io-rate-score="acceptable"] li.io-rate-acceptable {
|
||||
display:list-item;
|
||||
}
|
||||
&[io-rate-score="bad"] li.io-rate-bad {
|
||||
display:list-item;
|
||||
}
|
||||
&[scored="complete"] {
|
||||
li.success {
|
||||
display:list-item;
|
||||
}
|
||||
|
||||
&[io-rate-score="bad"], &[io-var-score="bad"], &[latency-score="bad"]{
|
||||
li.success {
|
||||
display:none;
|
||||
}
|
||||
li.failure {
|
||||
display:list-item;
|
||||
}
|
||||
}
|
||||
|
||||
&[latency-score="unknown"] {
|
||||
li.success {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -251,6 +344,153 @@
|
|||
.wizard-step[layout-wizard-step="2"] {
|
||||
.wizard-step-content .wizard-step-column {
|
||||
width:25%;
|
||||
|
||||
&:nth-of-type(2) {
|
||||
width:21%;
|
||||
}
|
||||
|
||||
|
||||
&:nth-of-type(3) {
|
||||
width:45%;
|
||||
}
|
||||
|
||||
|
||||
&:nth-of-type(4) {
|
||||
width:9%;
|
||||
}
|
||||
}
|
||||
|
||||
.watch-video {
|
||||
margin-top:45px;
|
||||
}
|
||||
|
||||
.icon-instrument-select {
|
||||
padding:3px 0; // to combine 24 of .current-instrument + 3x on either side
|
||||
margin:0 0 15px 25px; // 15 margin-bottom to match tracks on the left
|
||||
width:30px;
|
||||
}
|
||||
|
||||
.unassigned-channels {
|
||||
min-height:240px;
|
||||
overflow-y:auto;
|
||||
//padding-right:18px; // to keep draggables off of scrollbar. maybe necessary
|
||||
|
||||
&.drag-in-progress {
|
||||
overflow-y:visible;
|
||||
overflow-x:visible;
|
||||
}
|
||||
|
||||
&.possible-target {
|
||||
border: solid 1px white;
|
||||
|
||||
&.drag-hovering {
|
||||
border: solid 1px #ED3618;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.num {
|
||||
position:absolute;
|
||||
height:29px;
|
||||
line-height:29px;
|
||||
}
|
||||
.track {
|
||||
margin-bottom: 15px;
|
||||
.track-target {
|
||||
&.possible-target {
|
||||
border-color:white;
|
||||
}
|
||||
&.drag-hovering {
|
||||
border-color:#ED3618;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.ftue-input {
|
||||
font-size:12px;
|
||||
cursor: move;
|
||||
padding: 4px;
|
||||
border: solid 1px #999;
|
||||
margin-bottom: 15px;
|
||||
white-space: nowrap;
|
||||
overflow:hidden;
|
||||
text-align:left;
|
||||
direction:rtl;
|
||||
&.ui-draggable-dragging{
|
||||
margin-bottom:0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color:white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/**
|
||||
&:hover {
|
||||
color:white;
|
||||
overflow:visible;
|
||||
background-color:#333;
|
||||
width: auto !important;
|
||||
direction:ltr;
|
||||
position:absolute;
|
||||
line-height:19.5px;
|
||||
}*/
|
||||
}
|
||||
|
||||
.track-target {
|
||||
|
||||
cursor: move;
|
||||
padding: 4px;
|
||||
border: solid 1px #999;
|
||||
margin-left: 15px;
|
||||
height:20px;
|
||||
overflow:hidden;
|
||||
|
||||
&.drag-in-progress {
|
||||
overflow:visible;
|
||||
}
|
||||
|
||||
.ftue-input {
|
||||
padding:0;
|
||||
border:0;
|
||||
margin-bottom:0;
|
||||
&.ui-draggable-dragging {
|
||||
padding:4px;
|
||||
border: solid 1px #999;
|
||||
overflow:visible;
|
||||
}
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
display:none;
|
||||
font-size:12px;
|
||||
}
|
||||
|
||||
&[track-count="0"] {
|
||||
.placeholder {
|
||||
display:inline;
|
||||
}
|
||||
}
|
||||
|
||||
&[track-count="2"] {
|
||||
.ftue-input {
|
||||
width:49%;
|
||||
display:inline-block;
|
||||
|
||||
&:nth-of-type(1) {
|
||||
float:left;
|
||||
/**&:after {
|
||||
float:right;
|
||||
content: ',';
|
||||
padding-right:3px;
|
||||
}*/
|
||||
}
|
||||
&:nth-of-type(2) {
|
||||
float:right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -264,6 +504,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
.watch-video {
|
||||
margin-top:97px;
|
||||
}
|
||||
|
||||
.voicechat-option {
|
||||
|
||||
|
|
@ -273,8 +516,17 @@
|
|||
|
||||
}
|
||||
|
||||
h3 {
|
||||
padding-left:30px;
|
||||
margin-top:14px;
|
||||
font-weight:bold;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
p {
|
||||
padding-left:30px;
|
||||
margin-top:5px;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
input {
|
||||
|
|
@ -282,11 +534,32 @@
|
|||
margin:auto;
|
||||
width:30px;
|
||||
}
|
||||
|
||||
.iradio_minimal {
|
||||
margin-top:15px;
|
||||
display:inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.ftue-box {
|
||||
&.chat-inputs {
|
||||
height: 230px !important;
|
||||
overflow:auto;
|
||||
|
||||
p {
|
||||
white-space: nowrap;
|
||||
display:inline-block;
|
||||
height:32px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.chat-input {
|
||||
white-space:nowrap;
|
||||
|
||||
.iradio_minimal {
|
||||
display:inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.watch-video {
|
||||
|
|
@ -316,8 +589,21 @@
|
|||
.test-direct-monitoring {
|
||||
margin-top:40px;
|
||||
display:inline-block;
|
||||
text-decoration: none;
|
||||
width:90px;
|
||||
|
||||
span {
|
||||
&.paused {
|
||||
.playing {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
&.playing {
|
||||
.paused {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
.direct-monitoring-btn {
|
||||
padding-left:5px;
|
||||
font-size:16px;
|
||||
}
|
||||
|
|
@ -330,10 +616,170 @@
|
|||
|
||||
.wizard-step[layout-wizard-step="5"] {
|
||||
|
||||
.wizard-step-content .wizard-step-column {
|
||||
&:nth-of-type(1) {
|
||||
width:25%;
|
||||
}
|
||||
&:nth-of-type(2) {
|
||||
width:50%;
|
||||
}
|
||||
&:nth-of-type(3) {
|
||||
width:25%;
|
||||
}
|
||||
}
|
||||
|
||||
.summary {
|
||||
margin-left:20px;
|
||||
margin-top:22px;
|
||||
}
|
||||
|
||||
.watch-video {
|
||||
margin-top:90px;
|
||||
}
|
||||
|
||||
a.start-network-test {
|
||||
margin-top:20px;
|
||||
}
|
||||
|
||||
.network-test-score {
|
||||
height:24px;
|
||||
padding:10px;
|
||||
color:white;
|
||||
font-size:20px;
|
||||
background-color:#222;
|
||||
text-align:center;
|
||||
margin-bottom:20px;
|
||||
|
||||
&.good {
|
||||
background-color: #72a43b;
|
||||
}
|
||||
&.acceptable {
|
||||
background-color: #D6A800;
|
||||
}
|
||||
&.bad {
|
||||
background-color: #7B0C00;
|
||||
}
|
||||
}
|
||||
|
||||
.scoring-bar {
|
||||
width:100%;
|
||||
height:20px;
|
||||
left:0;
|
||||
position:relative;
|
||||
//display:inline-block;
|
||||
display:none;
|
||||
|
||||
.current-score {
|
||||
background-color:gray;
|
||||
border-right:1px solid white;
|
||||
border-top:1px solid #ccc;
|
||||
border-bottom:1px solid #ccc;
|
||||
border-left:1px solid #ccc;
|
||||
height:20px;
|
||||
display:inline-block;
|
||||
position:relative;
|
||||
left:0;
|
||||
min-width:55px;
|
||||
text-align:left;
|
||||
padding-left:5px;
|
||||
@include border_box_sizing;
|
||||
font-size:12px;
|
||||
color:white;
|
||||
|
||||
.subscore {
|
||||
font-size:10px;
|
||||
color:white;
|
||||
bottom:-15px;
|
||||
right:-16px;
|
||||
position:absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.good-marker {
|
||||
position:absolute;
|
||||
text-align:center;
|
||||
left:95%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-top: 5px solid #72a43b;
|
||||
margin-left:-5px;
|
||||
top:-7px;
|
||||
}
|
||||
|
||||
.good-line {
|
||||
position:absolute;
|
||||
height:100%;
|
||||
left:95%;
|
||||
width:1px;
|
||||
background-color: #72a43b;
|
||||
margin-left:-0.5px;
|
||||
top:0;
|
||||
}
|
||||
}
|
||||
.network-test-text {
|
||||
|
||||
}
|
||||
|
||||
.network-test-results {
|
||||
height: 248px ! important;
|
||||
@include border_box_sizing;
|
||||
&.testing {
|
||||
|
||||
text-align:left;
|
||||
.network-test-score {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.scoring-bar {
|
||||
display:inline-block;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
.network-test-text {
|
||||
//background-image: url('/assets/shared/spinner.gif');
|
||||
//background-repeat:no-repeat;
|
||||
//background-position:center;
|
||||
//width:128px;
|
||||
//height:128px;
|
||||
}
|
||||
}
|
||||
|
||||
&.good {
|
||||
.network-test-score {
|
||||
background-color: #72a43b;
|
||||
}
|
||||
}
|
||||
|
||||
&.acceptable {
|
||||
.network-test-score {
|
||||
background-color: #D6A800;
|
||||
}
|
||||
}
|
||||
|
||||
&.bad {
|
||||
.network-test-score {
|
||||
background-color: #7B0C00;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wizard-step[layout-wizard-step="6"] {
|
||||
.wizard-step-content .wizard-step-column {
|
||||
&:nth-of-type(1) {
|
||||
width:50%;
|
||||
height:350px;
|
||||
}
|
||||
&:nth-of-type(2) {
|
||||
width:50%;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-bottom:20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
|
|
@ -586,6 +1032,11 @@
|
|||
}
|
||||
.audio-input {
|
||||
left:0px;
|
||||
margin-top:30px;
|
||||
|
||||
&.audio-output-showing {
|
||||
margin-top:0;
|
||||
}
|
||||
}
|
||||
.voice-chat-input {
|
||||
left:50%;
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
@import "client/common.css.scss";
|
||||
@charset "UTF-8";
|
||||
|
||||
.dialog {
|
||||
.wizard-buttons {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width:100%;
|
||||
@include border_box_sizing;
|
||||
padding-right:45px;
|
||||
padding-bottom:15px;
|
||||
|
||||
}
|
||||
|
||||
.wizard-buttons-holder {
|
||||
float:right;
|
||||
}
|
||||
|
||||
.wizard-step {
|
||||
display:none;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.wizard-step-content {
|
||||
margin-top:20px;
|
||||
padding-bottom:12px;
|
||||
|
||||
.wizard-step-column{
|
||||
position:relative;
|
||||
float:left;
|
||||
vertical-align:top;
|
||||
@include border_box_sizing;
|
||||
padding-right:12px;
|
||||
height:300px;
|
||||
}
|
||||
|
||||
.wizard-step-column:last-child {
|
||||
//padding-right:0;
|
||||
}
|
||||
|
||||
p:nth-of-type(1) {
|
||||
margin-top:0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ body.jam {
|
|||
left: -1px;
|
||||
top: 100%;
|
||||
margin-top: -1px;
|
||||
margin-left:1px;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-top: 1px solid #eee;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
class ApiInvitedUsersController < ApiController
|
||||
class ApiInvitedUsersController < ApiController
|
||||
|
||||
# have to be signed in currently to see this screen
|
||||
before_filter :api_signed_in_user
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
class ApiLatencyTestersController < ApiController
|
||||
|
||||
# have to be signed in currently to see this screen
|
||||
before_filter :api_signed_in_user
|
||||
|
||||
respond_to :json
|
||||
|
||||
def match
|
||||
# some day we can find the best latency tester to test against, now there is only one.
|
||||
@latency_tester = LatencyTester.select_latency_tester
|
||||
|
||||
respond_with_model(@latency_tester)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -13,6 +13,7 @@ class ApiSearchController < ApiController
|
|||
if 1 == query[Search::PARAM_MUSICIAN].to_i
|
||||
clientid = query[:clientid]
|
||||
conn = (clientid ? Connection.where(client_id: clientid, user_id: current_user.id).first : nil)
|
||||
# puts "================== query #{query.inspect}"
|
||||
@search = Search.musician_filter(query, current_user, conn)
|
||||
else
|
||||
@search = Search.band_filter(query, current_user)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ class ClientsController < ApplicationController
|
|||
include ClientHelper
|
||||
include UsersHelper
|
||||
|
||||
|
||||
AUTHED = %W{friend}
|
||||
|
||||
|
||||
def index
|
||||
|
||||
# we want to enforce that /client is always the client view prefix
|
||||
|
|
@ -15,7 +19,9 @@ class ClientsController < ApplicationController
|
|||
render :layout => 'client'
|
||||
end
|
||||
|
||||
AUTHED = %W{friend}
|
||||
def latency_tester
|
||||
render :layout => 'client'
|
||||
end
|
||||
|
||||
def auth_action
|
||||
if current_user
|
||||
|
|
@ -31,5 +37,4 @@ class ClientsController < ApplicationController
|
|||
redirect_to client_url
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -29,10 +29,24 @@ module ClientHelper
|
|||
gon.websocket_gateway_uri = Rails.application.config.websocket_gateway_uri
|
||||
end
|
||||
|
||||
if Rails.env == "development"
|
||||
# if in development mode, we assume you are running websocket-gateway
|
||||
# on the same host as you hit your server.
|
||||
gon.websocket_gateway_trusted_uri = "ws://" + request.host + ":6768/websocket";
|
||||
else
|
||||
# but in any other mode, just use config
|
||||
gon.websocket_gateway_trusted_uri = Rails.application.config.websocket_gateway_trusted_uri
|
||||
end
|
||||
|
||||
gon.check_for_client_updates = Rails.application.config.check_for_client_updates
|
||||
gon.fp_apikey = Rails.application.config.filepicker_rails.api_key
|
||||
gon.fp_upload_dir = Rails.application.config.filepicker_upload_dir
|
||||
gon.allow_force_native_client = Rails.application.config.allow_force_native_client
|
||||
gon.ftue_io_wait_time = Rails.application.config.ftue_io_wait_time
|
||||
gon.ftue_packet_rate_treshold = Rails.application.config.ftue_packet_rate_treshold
|
||||
gon.ftue_network_test_duration = Rails.application.config.ftue_network_test_duration
|
||||
gon.ftue_network_test_max_clients = Rails.application.config.ftue_network_test_max_clients
|
||||
gon.ftue_maximum_gear_latency = Rails.application.config.ftue_maximum_gear_latency
|
||||
|
||||
# is this the native client or browser?
|
||||
@nativeClient = is_native_client?
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
object @latency_tester
|
||||
|
||||
extends "api_latency_testers/show"
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
object @latency_tester
|
||||
|
||||
attribute :id, :client_id
|
||||
|
|
@ -43,7 +43,7 @@ if @search.musicians_filter_search?
|
|||
end
|
||||
|
||||
child(:results => :musicians) {
|
||||
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :email, :online, :musician, :photo_url, :biography
|
||||
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :email, :online, :musician, :photo_url, :biography, :joined_score
|
||||
|
||||
node :is_friend do |musician|
|
||||
@search.is_friend?(musician)
|
||||
|
|
|
|||
|
|
@ -25,12 +25,29 @@
|
|||
<div class="content-wrapper account">
|
||||
<br />
|
||||
|
||||
{% if (data.isNativeClient) { %}
|
||||
<div class="account-left">
|
||||
<h2>audio gear:</h2>
|
||||
</div>
|
||||
|
||||
<div class="account-mid audio">
|
||||
<strong>Profiles:</strong> <span class="audio-profiles-short">{{data.validProfiles}}</span>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<a id="account-edit-audio-link" href="#" class="button-orange">UPDATE</a>
|
||||
</div>
|
||||
<br clear="all" />
|
||||
|
||||
<hr />
|
||||
{% } %}
|
||||
|
||||
<div class="account-left">
|
||||
<h2>identity:</h2>
|
||||
</div>
|
||||
|
||||
<div class="account-mid identity">
|
||||
<strong>Email:</strong> {email}<br />
|
||||
<strong>Email:</strong> {{data.email}}<br />
|
||||
<strong>Password:</strong> •••••••••••
|
||||
</div>
|
||||
|
||||
|
|
@ -44,16 +61,16 @@
|
|||
|
||||
<div class="account-left">
|
||||
<div class="right small mr20" style="margin-top:4px;">
|
||||
<a href="#" class="avatar_large"><img src="{photoUrl}" id="profile-avatar" /></a>
|
||||
<a href="#" class="avatar_large"><img src="{{data.photoUrl}}" id="profile-avatar" /></a>
|
||||
</div>
|
||||
<h2>profile:</h2>
|
||||
</div>
|
||||
|
||||
<div class="account-mid profile">
|
||||
<div class="whitespace">
|
||||
<strong>Name:</strong> {name}<br />
|
||||
<strong>Location:</strong> {location}<br />
|
||||
<strong>Instruments:</strong> {instruments}
|
||||
<strong>Name:</strong> {{data.name}}<br />
|
||||
<strong>Location:</strong> {{data.location}}<br />
|
||||
<strong>Instruments:</strong> {{data.instruments}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -80,9 +97,9 @@
|
|||
</div>
|
||||
<br clear="all" />
|
||||
|
||||
{% if (data.musician) { %}
|
||||
<hr />
|
||||
|
||||
<% if current_user && current_user.musician? %>
|
||||
<div class="account-left">
|
||||
<h2>payments:</h2>
|
||||
</div>
|
||||
|
|
@ -99,22 +116,7 @@
|
|||
</div>
|
||||
<br clear="all" />
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="account-left">
|
||||
<h2>audio:</h2>
|
||||
</div>
|
||||
|
||||
<div class="account-mid audio">
|
||||
<strong>Approved Profiles:</strong> <span class="audio-profiles-short">{validProfiles}</span><br />
|
||||
<strong>Invalid Profiles:</strong> <span class="audio-profiles-short">{invalidProfiles}</span>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<a id="account-edit-audio-link" href="#" class="button-orange">UPDATE</a>
|
||||
</div>
|
||||
<br clear="all" />
|
||||
<% end %>
|
||||
{% } %}
|
||||
</div>
|
||||
<!-- end content wrapper -->
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th align="left">NAME</th>
|
||||
<th class="noborder actions">DELETE</th>
|
||||
<th class="noborder actions">ACTIONS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -44,11 +44,13 @@
|
|||
|
||||
<script type="text/template" id="template-account-audio">
|
||||
<tbody>
|
||||
{% var keys = Object.keys(data.devices); %}
|
||||
{% for (var i = 0; i < keys.length; i++) { %}
|
||||
<tr>
|
||||
<td>{{data.devices[keys[i]]}}</td>
|
||||
<td class="actions"><a href="#" data-id="{{keys[i]}}" data-purpose="delete-audio-profile" class="button-orange">DELETE</a></td>
|
||||
{% for (var i = 0; i < data.profiles.length; i++) { %}
|
||||
{% var profile = data.profiles[i]; %}
|
||||
<tr data-status="{{profile.class}}" data-current="{{profile.current}}">
|
||||
<td>{{profile.id}} <span data-current="{{profile.current}}">{{profile.active_text}}</span></td>
|
||||
<td class="actions">
|
||||
<a href="#" data-id="{{profile.id}}" data-purpose="activate-audio-profile" class="button-orange">ACTIVATE</a>
|
||||
<a href="#" data-id="{{profile.id}}" data-purpose="delete-audio-profile" class="button-orange">DELETE</a></td>
|
||||
</tr>
|
||||
{% } %}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
</div>
|
||||
<!-- end right column -->
|
||||
<br clear="all">
|
||||
<br clear="all" class="end-content">
|
||||
|
||||
<div class="right buttons">
|
||||
<a class="button-orange close-btn">CLOSE</a>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,31 @@
|
|||
<script type="text/template" id="template-help-pre-processed-track">
|
||||
This track has not yet been processed into master form and may include multiple streams from the source musician.
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-help-refocus-rescore">
|
||||
{% if(data.validIOScore) { %}
|
||||
We have re-scored latency based on your changes.
|
||||
{% } else { %}
|
||||
We have re-scored latency and IO based on your changes.
|
||||
{% } %}
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-help-ftue-watch-video">
|
||||
Be sure to watch the help video.
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-help-chat-not-enabled">
|
||||
You must first chose this option in order to activate a chat input.
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-help-select-input">
|
||||
You still need to select an input device.
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-help-select-output">
|
||||
Select an output device, because the current input device has no candidate output ports.
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-help-push-resync-when-done">
|
||||
Push 'Resync' when done modifying Framesize, Buffer In, or Buffer Out.
|
||||
</script>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
%script{type: 'text/template', id: 'template-icon-instrument-select'}
|
||||
.icon-instrument-select
|
||||
.current-instrument.none
|
||||
%a.arrow-down
|
||||
%ul
|
||||
= '{% _.each(data.instruments, function(instrument, instrumentId) { %}'
|
||||
%li{'data-instrument-id' => '{{instrumentId}}'}
|
||||
%a{href:'#'}
|
||||
%img{src: '{{instrument.asset}}'}
|
||||
= '{{instrument.name}}'
|
||||
= '{% }) %}'
|
||||
|
||||
|
|
@ -13,12 +13,12 @@
|
|||
<% end -%>
|
||||
<!-- distance filter -->
|
||||
<%= content_tag(:div, :class => 'filter-element wrapper') do -%>
|
||||
<%= content_tag(:div, 'Within', :class => 'filter-element desc') %>
|
||||
<%= content_tag(:div, 'Latency:', :class => 'filter-element desc') %>
|
||||
<%= content_tag(:div, :class => 'query-distance-params') do -%>
|
||||
<%= select_tag('musician_query_distance', options_for_select(Search::M_DISTANCE_OPTS, Search::M_MILES_DEFAULT)) %>
|
||||
<%= select_tag('musician_query_score', options_for_select(Search::M_SCORE_OPTS, Search::M_SCORE_DEFAULT)) %>
|
||||
<% end -%>
|
||||
<%= content_tag(:div, :class => 'filter-element') do -%>
|
||||
miles of <%= content_tag(:span, current_user ? current_user.current_city(request.remote_ip) : '', :id => 'musician-filter-city') %>
|
||||
to <%= content_tag(:span, current_user ? current_user.current_city(request.remote_ip) : '', :id => 'musician-filter-city') %>
|
||||
<% end -%>
|
||||
<% end -%>
|
||||
<%= content_tag(:div,
|
||||
|
|
|
|||
|
|
@ -30,6 +30,15 @@
|
|||
<!-- avatar -->
|
||||
<div class="avatar-small"><img src="{avatar_url}" /></div>
|
||||
</div>
|
||||
<!-- todo scott vfrs-1455 i need a template tutorial to define the three variables that substitute here:
|
||||
score:
|
||||
one_way_score is joined_score / 2;
|
||||
score_color is green, yellow, red, blue, or purple (depending upon value of joined_score)
|
||||
score_color_alt is good, moderate, poor, unacceptable,or missing
|
||||
-->
|
||||
<!-- <div class="left" style="***help***">
|
||||
{one_way_score} ms <img src="../assets/content/icon_{score_color}_score.png" alt="{score_color_alt}" width="12" height="12" align="absmiddle"/>
|
||||
</div> -->
|
||||
<div class="right musician-following" style="width: 120px;">
|
||||
<div class="bold">FOLLOWING:</div>
|
||||
<table class="musicians" cellpadding="0" cellspacing="5">{musician_follow_template}</table>
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@
|
|||
</div>
|
||||
<div layout-panel="contents" class="chatcontents">
|
||||
<div class="chat-status">
|
||||
<span>Chat is available when session is connected.</span>
|
||||
<span>Chat is available when in session.</span>
|
||||
</div>
|
||||
<div class="chat-list-scroller">
|
||||
<div class="previous-chat-list">
|
||||
|
|
|
|||
|
|
@ -47,6 +47,16 @@
|
|||
<%= content_tag(:div, 'Show:', :class => 'filter-element desc') %>
|
||||
<%= select_tag("#{filter_label}_show", options_for_select(Search::SHOW_OPTS), {:class => "easydropdown"}) %>
|
||||
<!-- @end show filter -->
|
||||
<% elsif :musician == filter_label %>
|
||||
<!-- @begin score filter -->
|
||||
<%= content_tag(:div, 'Latency:', :class => 'filter-element desc') %>
|
||||
<%= content_tag(:div, :class => 'query-distance-params') do -%>
|
||||
<%= select_tag("musician_query_score", options_for_select(Search::M_SCORE_OPTS, Search::M_SCORE_DEFAULT), {:class => 'easydropdown'}) %>
|
||||
<% end -%>
|
||||
<%= content_tag(:div, :class => 'filter-element desc') do -%>
|
||||
to <%= content_tag(:span, current_user ? current_user.current_city(request.remote_ip) : '', :id => "musician-filter-city") %>
|
||||
<% end -%>
|
||||
<!-- @end score filter -->
|
||||
<% else %>
|
||||
<!-- @begin distance filter -->
|
||||
<%= content_tag(:div, 'Within', :class => 'filter-element desc') %>
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
- total_steps = 7
|
||||
|
||||
.ftue-buttons
|
||||
.ftue-buttons-holder
|
||||
%a.button-grey{href: '#'} HELP
|
||||
.wizard-buttons
|
||||
.wizard-buttons-holder
|
||||
%a.button-grey.btn-help{href: '#'} HELP
|
||||
- if step > 0 && step != total_steps
|
||||
%a.button-orange.btn-back{href:'#'} BACK
|
||||
- if step != total_steps
|
||||
|
|
|
|||
|
|
@ -31,22 +31,29 @@
|
|||
%li Configure interface settings.
|
||||
%li View test results.
|
||||
.center
|
||||
%a.button-orange.watch-video.audio-input{href:'#', rel:'external'} WATCH VIDEO
|
||||
%a.button-orange.watch-video.audio-output{href:'#', rel:'external'} WATCH VIDEO
|
||||
%a.button-orange.watch-video.audio-input{href:'#', rel:'external'}
|
||||
WATCH VIDEO
|
||||
%br
|
||||
%span.video-type (FOR INPUT DEVICE)
|
||||
%a.button-orange.watch-video.audio-output{href:'#', rel:'external'}
|
||||
WATCH VIDEO
|
||||
%br
|
||||
%span.video-type (FOR OUTPUT DEVICE)
|
||||
.wizard-step-column
|
||||
%h2 Audio Input Device
|
||||
%select.w100.select-audio-input-device
|
||||
%option None
|
||||
%h2 Audio Input Ports
|
||||
%h2.audio-channels Audio Input Ports
|
||||
.ftue-box.list.ports.input-ports
|
||||
%a.button-orange.asio-settings-btn ASIO SETTINGS...
|
||||
%a.button-orange.asio-settings-input-btn ASIO SETTINGS...
|
||||
%a.button-orange.resync-btn RESYNC
|
||||
.wizard-step-column
|
||||
%h2 Audio Output Device
|
||||
%select.w100.select-audio-output-device
|
||||
%option Same as input
|
||||
%h2 Audio Output Ports
|
||||
%h2.audio-channels Audio Output Ports
|
||||
.ftue-box.list.ports.output-ports
|
||||
%a.button-orange.asio-settings-output-btn ASIO SETTINGS...
|
||||
.frame-and-buffers
|
||||
.framesize
|
||||
%h2 Frame
|
||||
|
|
@ -55,7 +62,7 @@
|
|||
%option{val:'5'} 5
|
||||
%option{val:'10'} 10
|
||||
.buffers
|
||||
%h2 Buffers In/Out
|
||||
%h2 Buffer In/Out
|
||||
%select.select-buffer-in
|
||||
%option{val:'0'} 0
|
||||
%option{val:'1'} 1
|
||||
|
|
@ -91,13 +98,33 @@
|
|||
.p5
|
||||
.io I/O
|
||||
%span.io-skip-msg
|
||||
Skipped
|
||||
Not Tested
|
||||
%span.io-countdown
|
||||
%span.secs
|
||||
seconds left
|
||||
%span.io-rate-score
|
||||
%span.io-var-score
|
||||
|
||||
%span.io-rate<
|
||||
Rate=
|
||||
%span.io-rate-score>
|
||||
%span.io-var<
|
||||
Var=
|
||||
%span.io-var-score>
|
||||
.clearall
|
||||
%ul.results-text
|
||||
%li.latency-good Your latency is good.
|
||||
%li.latency-acceptable Your latency is acceptable.
|
||||
%li.latency-bad Your latency is poor.
|
||||
%li.io-rate-good Your I/O rate is good.
|
||||
%li.io-rate-acceptable Your I/O rate is acceptable.
|
||||
%li.io-rate-bad Your I/O rate is poor.
|
||||
%li.io-var-good Your I/O variance is good.
|
||||
%li.io-var-acceptable Your I/O variance is acceptable.
|
||||
%li.io-var-bad Your I/O variance is poor.
|
||||
%li.success You may proceed to the next step.
|
||||
%li.failure We're sorry, but your audio gear has failed. Please watch video or click HELP button below.
|
||||
.unknown-text
|
||||
%div We cannot accurately predict the latency of your audio gear. To proceed, you must run an audio loopback test. Click button below to do this.
|
||||
%div.loopback-button-holder
|
||||
%a.button-orange.loopback-test{href:'#'} RUN LOOPBACK TEST
|
||||
.clearall
|
||||
|
||||
.wizard-step{ 'layout-wizard-step' => "2", 'dialog-title' => "Configure Tracks", 'dialog-purpose' => "ConfigureTracks" }
|
||||
|
|
@ -114,17 +141,20 @@
|
|||
%li Select the instrument for each track.
|
||||
.center
|
||||
%a.button-orange.watch-video{href:'#'} WATCH VIDEO
|
||||
.wizard-step-column
|
||||
.wizard-step-column.no-selection-range
|
||||
%h2 Unassigned Ports
|
||||
.wizard-step-column
|
||||
.unassigned-channels
|
||||
.wizard-step-column.no-selection-range
|
||||
%h2 Track Input Port(s)
|
||||
.wizard-step-column
|
||||
%h2 Track Instrument
|
||||
.tracks
|
||||
.wizard-step-column.no-selection-range
|
||||
%h2 Instrument
|
||||
.instruments
|
||||
|
||||
.wizard-step{ 'layout-wizard-step' => "3", 'dialog-title' => "Configure Voice Chat", 'dialog-purpose' => "ConfigureVoiceChat" }
|
||||
.ftuesteps
|
||||
.clearall
|
||||
.help-text In this step, you will select, configure, and test your audio gear. Please watch the video for best instructions.
|
||||
.help-text In this step, you may set up a microphone to use for voice chat. Please watch the video for best instructions.
|
||||
.wizard-step-content
|
||||
.wizard-step-column
|
||||
%h2 Instructions
|
||||
|
|
@ -137,12 +167,14 @@
|
|||
%h2 Select Voice Chat Option
|
||||
.voicechat-option.reuse-audio-input
|
||||
%input{type:"radio", name: "voicechat", checked:"checked"}
|
||||
%h3 Use Music Microphone
|
||||
%p I am already using a microphone to capture my vocal or instrumental music, so I can talk with other musicians using that microphone
|
||||
.voicechat-option.use-chat-input
|
||||
%input{type:"radio", name: "voicechat", checked:"unchecked"}
|
||||
%input{type:"radio", name: "voicechat"}
|
||||
%h3 Use Chat Microphone
|
||||
%p I am not using a microphone for acoustic instruments or vocals, so use the input selected to the right for voice chat during my sessions
|
||||
.wizard-step-column
|
||||
%h2 Track Input Port(s)
|
||||
%h2 Voice Chat Input
|
||||
.ftue-box.chat-inputs
|
||||
|
||||
|
||||
|
|
@ -163,25 +195,85 @@
|
|||
.wizard-step-column
|
||||
.help-content
|
||||
When you have fully turned off the direct monitoring control (if any) on your audio interface,
|
||||
please click the Play busson below. If you hear the audio clearly, then your settings are correct,
|
||||
please click the Play button below. If you hear the audio clearly, then your settings are correct,
|
||||
and you can move ahead to the next step.
|
||||
If you use your audio interface for recording, and use the direct monitoring feature for recording,
|
||||
please note that you will need to remember to turn this feature off every time that you use the JamKazam service.
|
||||
.center
|
||||
%a.ftue-box.test-direct-monitoring
|
||||
%a.ftue-box.test-direct-monitoring.paused.no-selection-range
|
||||
.playing
|
||||
%img{src:'assets/content/icon_pausebutton.png', width:20, height:20, align:'top'}
|
||||
%span.direct-monitoring-btn Pause
|
||||
.paused
|
||||
%img{src:'assets/content/icon_playbutton.png', width:20, height:20, align:'top'}
|
||||
%span Play
|
||||
%span.direct-monitoring-btn Play
|
||||
|
||||
|
||||
.wizard-step{ 'layout-wizard-step' => "5", 'dialog-title' => "Test Router & Network", 'dialog-purpose' => "TestRouterNetwork" }
|
||||
.ftuesteps
|
||||
.clearall
|
||||
.help-text In this step, you will test your router and Internet connection to ensure that you can play in online sessions, and to see how many musicians can be in a session with you based on your internet connection.
|
||||
.wizard-step-content
|
||||
.wizard-step-column
|
||||
%h2 Instructions
|
||||
.ftue-box.instructions
|
||||
%ul
|
||||
%li Check that computer is connected to router using Ethernet cable.
|
||||
%li Click Start Network Test button.
|
||||
%li View test results.
|
||||
.center
|
||||
%a.button-orange.watch-video{href:'#'} WATCH VIDEO
|
||||
.wizard-step-column
|
||||
.summary
|
||||
%p Ensure that your computer is connected to your home router using an Ethernet cable rather than using Wi-Fi wireless access. If necessary, find or purchase a long Ethernet cable, up to 100 ft.
|
||||
%p Then click on the Start Network Test button below.
|
||||
.center
|
||||
%a.button-orange.start-network-test{href:'#'} START NETWORK TEST
|
||||
.wizard-step-column
|
||||
%h2 Test Results
|
||||
.network-test-results.ftue-box
|
||||
.scoring-bar
|
||||
.current-score
|
||||
testing...
|
||||
.subscore
|
||||
.good-marker
|
||||
.good-line
|
||||
.network-test-score
|
||||
.scored-clients
|
||||
.network-test-text
|
||||
|
||||
|
||||
.wizard-step{ 'layout-wizard-step' => "6", 'dialog-title' => "Success!", 'dialog-purpose' => "Success" }
|
||||
.ftuesteps
|
||||
|
||||
|
||||
.ftue-buttons
|
||||
.clearall
|
||||
.wizard-step-content
|
||||
.wizard-step-column
|
||||
%p Congratulations! You are now ready to create, join, and play in online sessions with other JamKazam musicians!
|
||||
%p Please feel free to watch any of the videos to the right for information on how to connect and play with other musicians, and how to get the most out of JamKazam. When you're ready to go, click the Close button.
|
||||
%p Have fun and thanks for joining us!
|
||||
%p — Team JamKazam
|
||||
.wizard-step-column
|
||||
%h2 Tutorial Videos
|
||||
%ul
|
||||
%li
|
||||
%a Creating a Session
|
||||
%li
|
||||
%a Finding a Session
|
||||
%li
|
||||
%a Playing in a Session
|
||||
%li
|
||||
%a Connecting with Other Musicians
|
||||
%li
|
||||
%a Making and Sharing Recordings
|
||||
%li
|
||||
%a Broadcasting Your Sessions
|
||||
%h2 Other Valuable Resource Links
|
||||
%ul
|
||||
%li
|
||||
%a JamKazam Support Center
|
||||
%li
|
||||
%a JamKazam Community Forum
|
||||
.wizard-buttons
|
||||
|
||||
%script{type: 'text/template', id: 'template-ftuesteps'}
|
||||
.ftuesteps-inner
|
||||
|
|
@ -201,10 +293,10 @@
|
|||
.ftue-step-title Success!
|
||||
|
||||
|
||||
%script{type: 'text/template', id: 'template-ftue-buttons'}
|
||||
.ftue-buttons-holder
|
||||
%script{type: 'text/template', id: 'template-wizard-buttons'}
|
||||
.wizard-buttons-holder
|
||||
%a.button-grey.btn-cancel{href:'#', 'layout-action' => 'close'} CANCEL
|
||||
%a.button-grey{href: '#'} HELP
|
||||
%a.button-grey.btn-help{href: '#'} HELP
|
||||
%a.button-orange.btn-back{href:'#'} BACK
|
||||
%a.button-orange.btn-next{href:'#'} NEXT
|
||||
%a.button-orange.btn-close{href:'#', 'layout-action' => 'close'} CLOSE
|
||||
|
|
@ -215,5 +307,17 @@
|
|||
%span
|
||||
= '{{data.name}}'
|
||||
|
||||
%script{type: 'text/template', id: 'template-assignable-port'}
|
||||
.ftue-input{'data-input-id' => '{{data.id}}'} {{data.name}}
|
||||
|
||||
%script{type: 'text/template', id: 'template-track-target'}
|
||||
.track{'data-num' => '{{data.num}}'}
|
||||
.num {{data.num + 1}}:
|
||||
.track-target{'data-num' => '{{data.num}}', 'track-count' => 0}
|
||||
%span.placeholder None
|
||||
|
||||
%script{type: 'text/template', id: 'template-chat-input'}
|
||||
.chat-input
|
||||
%input{type:"radio", name: "chat-device", 'data-channel-id' => '{{data.id}}', 'data-channel-name' => '{{data.name}}'}
|
||||
%p
|
||||
= '{{data.name}}'
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
<%= render "vu_meters" %>
|
||||
<%= render "ftue" %>
|
||||
<%= render "jamServer" %>
|
||||
<%= render "iconInstrumentSelect" %>
|
||||
<%= render "clients/gear/gear_wizard" %>
|
||||
<%= render "terms" %>
|
||||
<%= render "leaveSessionWarning" %>
|
||||
|
|
@ -257,6 +258,12 @@
|
|||
|
||||
JK.initJamClient();
|
||||
|
||||
// latency_tester does not want to be here
|
||||
if(window.jamClient.getOperatingMode() == "server") {
|
||||
window.location = "/latency_tester";
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's get things rolling...
|
||||
if (JK.currentUserId) {
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
= render :partial => "banner"
|
||||
= render :partial => "clients/banners/disconnected"
|
||||
= render :partial => "jamServer"
|
||||
|
||||
:javascript
|
||||
$(function() {
|
||||
JK = JK || {};
|
||||
|
||||
JK.logger.proxy_logs_to_backend = true;
|
||||
|
||||
JK.root_url = "#{root_url}"
|
||||
|
||||
// if in development mode, we assume you are running websocket-gateway
|
||||
// on the same host as you hit your server.
|
||||
JK.websocket_gateway_uri = #{Rails.env == "development" ? '"ws://" + location.hostname + ":6768/websocket"' : 'gon.websocket_gateway_trusted_uri'};
|
||||
|
||||
if (console) { console.log("websocket_gateway_uri:" + JK.websocket_gateway_uri); }
|
||||
|
||||
|
||||
// If no trackVolumeObject (when not running in native client)
|
||||
// create a fake one.
|
||||
if (!(window.trackVolumeObject)) {
|
||||
window.trackVolumeObject = {
|
||||
bIsMediaFile: false,
|
||||
broadcast: false,
|
||||
clientID: "",
|
||||
instrumentID: "",
|
||||
master: false,
|
||||
monitor: false,
|
||||
mute: false,
|
||||
name: "",
|
||||
objectName: "",
|
||||
record: false,
|
||||
volL: 0,
|
||||
volR: 0,
|
||||
wigetID: ""
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Some things can't be initialized until we're connected. Put them here.
|
||||
function _initAfterConnect(connected) {
|
||||
if (this.didInitAfterConnect) return;
|
||||
this.didInitAfterConnect = true
|
||||
|
||||
if(!connected) {
|
||||
jamServer.initiateReconnect(null, true);
|
||||
}
|
||||
}
|
||||
|
||||
JK.app = JK.JamKazam();
|
||||
var jamServer = new JK.JamServer(JK.app, function(event_type) {});
|
||||
jamServer.initialize();
|
||||
|
||||
// If no jamClient (when not running in native client)
|
||||
// create a fake one.
|
||||
if (!(window.jamClient)) {
|
||||
var p2pMessageFactory = new JK.FakeJamClientMessages();
|
||||
window.jamClient = new JK.FakeJamClient(JK.app, p2pMessageFactory);
|
||||
window.jamClient.SetFakeRecordingImpl(new JK.FakeJamClientRecordings(JK.app, jamClient, p2pMessageFactory));
|
||||
}
|
||||
|
||||
//JK.app.initialize();
|
||||
|
||||
JK.JamServer.connect() // singleton here defined in JamServer.js
|
||||
.done(function() {
|
||||
_initAfterConnect(true);
|
||||
})
|
||||
.fail(function() {
|
||||
_initAfterConnect(false);
|
||||
});
|
||||
|
||||
})
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<% if current_user.nil? || !Rails.application.config.ga_suppress_admin || !current_user.admin? # remove admin users from GA %>
|
||||
<% if request.user_agent != "monitor" && (current_user.nil? || !Rails.application.config.ga_suppress_admin || !current_user.admin?) # remove admin users from GA %>
|
||||
<script>
|
||||
(function(context) {
|
||||
if(context.jamClient) {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
<option class="label" value="" <%= @location[:country].blank? ? "selected" : "" %>>Select Country</option>
|
||||
<% @countriesx.each do |country| %>
|
||||
<% unless country[:countrycode].blank? %>
|
||||
<option value="<%= country %>" <%= @location[:country] == country[:countrycode] ? "selected" : "" %>><%= country[:countryname] %></option>
|
||||
<option value="<%= country[:countrycode] %>" <%= @location[:country] == country[:countrycode] ? "selected" : "" %>><%= country[:countryname] %></option>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -109,11 +109,12 @@ if defined?(Bundler)
|
|||
config.websocket_gateway_connect_time_expire_client = 60 # 60 matches production
|
||||
config.websocket_gateway_connect_time_stale_browser = 40 # 40 matches production
|
||||
config.websocket_gateway_connect_time_expire_browser = 60 # 60 matches production
|
||||
|
||||
config.websocket_gateway_cidr = ['0.0.0.0/0']
|
||||
config.websocket_gateway_internal_debug = false
|
||||
config.websocket_gateway_port = 6767 + ENV['JAM_INSTANCE'].to_i
|
||||
# Runs the websocket gateway within the web app
|
||||
config.websocket_gateway_uri = "ws://localhost:#{config.websocket_gateway_port}/websocket"
|
||||
config.websocket_gateway_trusted_uri = "ws://localhost:#{config.websocket_gateway_port + 1}/websocket"
|
||||
|
||||
config.external_hostname = ENV['EXTERNAL_HOSTNAME'] || 'localhost'
|
||||
config.external_port = ENV['EXTERNAL_PORT'] || 3000
|
||||
|
|
@ -231,5 +232,16 @@ if defined?(Bundler)
|
|||
# we have to do this for a while until all www.jamkazam.com cookies are gone,
|
||||
# and only .jamkazam.com cookies are around.. 2016?
|
||||
config.middleware.insert_before "ActionDispatch::Cookies", "Middlewares::ClearDuplicatedSession"
|
||||
|
||||
# how long should the frontend wait for the IO to stabilize before asking for a IO score?
|
||||
config.ftue_io_wait_time = 10
|
||||
# what should the threshold be for us to say, 'this person can't play at this rate' during the network test
|
||||
config.ftue_packet_rate_treshold = 0.95
|
||||
# how long to test at each network test step
|
||||
config.ftue_network_test_duration = 10
|
||||
# max number of people to test
|
||||
config.ftue_network_test_max_clients = 8
|
||||
# the maximum amount of allowable latency
|
||||
config.ftue_maximum_gear_latency = 20
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -81,5 +81,10 @@ SampleApp::Application.configure do
|
|||
|
||||
config.vanilla_url = '/forums'
|
||||
config.vanilla_login_url = '/forums/entry/jsconnect'
|
||||
|
||||
# very low waits to speed up automated tests
|
||||
config.ftue_io_wait_time = 1
|
||||
config.ftue_network_test_duration = 1
|
||||
config.ftue_network_test_max_clients = 5
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ unless $rails_rake_task
|
|||
:connect_time_expire_browser=> APP_CONFIG.websocket_gateway_connect_time_expire_browser,
|
||||
:rabbitmq_host => APP_CONFIG.rabbitmq_host,
|
||||
:rabbitmq_port => APP_CONFIG.rabbitmq_port,
|
||||
:calling_thread => current)
|
||||
:calling_thread => current,
|
||||
:cidr => APP_CONFIG.websocket_gateway_cidr)
|
||||
end
|
||||
Thread.stop
|
||||
end
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ SampleApp::Application.routes.draw do
|
|||
match '/isp/ping:isp', :to => 'users#jnlp', :constraints => {:format => :jnlp}, :as => 'isp_ping'
|
||||
|
||||
match '/client', to: 'clients#index'
|
||||
match '/latency_tester', to: 'clients#latency_tester'
|
||||
|
||||
match '/confirm/:signup_token', to: 'users#signup_confirm', as: 'signup_confirm'
|
||||
|
||||
|
|
@ -436,6 +437,9 @@ SampleApp::Application.routes.draw do
|
|||
|
||||
# diagnostic
|
||||
match '/diagnostics' => 'api_diagnostics#create', :via => :post
|
||||
|
||||
# latency_tester
|
||||
match '/latency_testers' => 'api_latency_testers#match', :via => :get
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue