merge conflict from feature/new_session (VRFS-3283)

This commit is contained in:
Seth Call 2015-07-15 10:04:45 -05:00
parent f2ce59005a
commit 3b71931e73
259 changed files with 9674 additions and 1183 deletions

View File

@ -58,7 +58,7 @@ gem 'resque'
gem 'resque-retry' gem 'resque-retry'
gem 'resque-failed-job-mailer' gem 'resque-failed-job-mailer'
gem 'resque-lonely_job', '~> 1.0.0' gem 'resque-lonely_job', '~> 1.0.0'
gem 'eventmachine', '1.0.3' gem 'eventmachine', '1.0.4'
gem 'amqp', '0.9.8' gem 'amqp', '0.9.8'
gem 'logging-rails', :require => 'logging/rails' gem 'logging-rails', :require => 'logging/rails'
gem 'pg_migrate' gem 'pg_migrate'

View File

@ -77,7 +77,7 @@ EOF
#bundle install --path vendor/bundle --local #bundle install --path vendor/bundle --local
# prepare production acssets # prepare production acssets
rm -rf $DIR/public/assets rm -rf $DIR/public/assets
bundle exec rake assets:precompile RAILS_ENV=production #bundle exec rake assets:precompile RAILS_ENV=production
# create debian using fpm # create debian using fpm
bundle exec fpm -s dir -t deb -p target/deb/jam-admin_0.1.${BUILD_NUMBER}_${ARCH}.deb -n "jam-admin" -v "0.1.$BUILD_NUMBER" --prefix /var/lib/jam-admin --after-install $DIR/script/package/post-install.sh --before-install $DIR/script/package/pre-install.sh --before-remove $DIR/script/package/pre-uninstall.sh --after-remove $DIR/script/package/post-uninstall.sh Gemfile .bundle config Rakefile script config.ru lib public vendor app BUILD_NUMBER bundle exec fpm -s dir -t deb -p target/deb/jam-admin_0.1.${BUILD_NUMBER}_${ARCH}.deb -n "jam-admin" -v "0.1.$BUILD_NUMBER" --prefix /var/lib/jam-admin --after-install $DIR/script/package/post-install.sh --before-install $DIR/script/package/pre-install.sh --before-remove $DIR/script/package/pre-uninstall.sh --after-remove $DIR/script/package/post-uninstall.sh Gemfile .bundle config Rakefile script config.ru lib public vendor app BUILD_NUMBER

View File

@ -1,5 +1,10 @@
ActionMailer::Base.raise_delivery_errors = true ActionMailer::Base.raise_delivery_errors = true
ActionMailer::Base.delivery_method = Rails.env == "test" ? :test : :smtp begin
ActionMailer::Base.delivery_method = Rails.env == "test" ? :test : :smtp
rescue
# this can happen on the build server when it's compiling assets and doesn't have the 'jam' database
ActionMailer::Base.delivery_method = :test
end
ActionMailer::Base.smtp_settings = { ActionMailer::Base.smtp_settings = {
:address => Rails.application.config.email_smtp_address, :address => Rails.application.config.email_smtp_address,
:port => Rails.application.config.email_smtp_port, :port => Rails.application.config.email_smtp_port,

5
build
View File

@ -62,8 +62,9 @@ popd > /dev/null
if [ ! -z "$PACKAGE" ]; then if [ ! -z "$PACKAGE" ]; then
DEB_SERVER=http://localhost:9010/apt-`uname -p` source /etc/lsb-release
GEM_SERVER=http://localhost:9000/gems DEB_SERVER=https://int.jamkazam.com:9010/apt-`uname -p`/$DISTRIB_CODENAME
GEM_SERVER=http://localhost:9000/gems
# if still going, then push all debs up # if still going, then push all debs up
if [[ "$GIT_BRANCH" == *develop* || "$GIT_BRANCH" == *master* || "$GIT_BRANCH" == *release* || "$GIT_BRANCH" == *feature* || "$GIT_BRANCH" == *hotfix* ]]; then if [[ "$GIT_BRANCH" == *develop* || "$GIT_BRANCH" == *master* || "$GIT_BRANCH" == *release* || "$GIT_BRANCH" == *feature* || "$GIT_BRANCH" == *hotfix* ]]; then

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
GEM_SERVER=http://localhost:9000/gems GEM_SERVER=https://int.jamkazam.com:9000/gems
DEB_SERVER=http://localhost:9010/apt-`uname -p` DEB_SERVER=https://int.jamkazam.com:9010/apt-`uname -p`
echo "starting build..." echo "starting build..."
./build ./build

View File

@ -276,12 +276,6 @@ jam_track_duration.sql
sales.sql sales.sql
show_whats_next_count.sql show_whats_next_count.sql
recurly_adjustments.sql recurly_adjustments.sql
alter_type_columns.sql
user_presences_rename.sql
add_genre_type.sql
add_description_to_perf_samples.sql
alter_genre_player_unique_constraint.sql
musician_search.sql
signup_hints.sql signup_hints.sql
packaging_notices.sql packaging_notices.sql
first_played_jamtrack_at.sql first_played_jamtrack_at.sql
@ -292,7 +286,14 @@ signing.sql
optimized_redeemption.sql optimized_redeemption.sql
optimized_redemption_warn_mode.sql optimized_redemption_warn_mode.sql
affiliate_partners2.sql affiliate_partners2.sql
enhance_band_profile.sql
broadcast_notifications.sql broadcast_notifications.sql
broadcast_notifications_fk.sql broadcast_notifications_fk.sql
calendar.sql calendar.sql
alter_type_columns.sql
user_presences_rename.sql
add_genre_type.sql
add_description_to_perf_samples.sql
alter_genre_player_unique_constraint.sql
musician_search.sql
enhance_band_profile.sql
alter_band_profile_rate_defaults.sql

View File

@ -0,0 +1,2 @@
ALTER TABLE bands ALTER COLUMN hourly_rate SET DEFAULT NULL;
ALTER TABLE bands ALTER COLUMN gig_minimum SET DEFAULT NULL;

View File

@ -0,0 +1,2 @@
ALTER TABLE users ALTER paid_sessions_hourly_rate TYPE integer;
ALTER TABLE users ALTER paid_sessions_daily_rate TYPE integer;

View File

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
GEM_SERVER=http://localhost:9000/gems GEM_SERVER=https://int.jamkazam.com:9000/gems
echo "starting build..." echo "starting build..."
./build ./build

View File

@ -23,7 +23,7 @@ gem "activerecord-import", "~> 0.4.1"
gem 'uuidtools', '2.1.2' gem 'uuidtools', '2.1.2'
gem 'bcrypt-ruby', '3.0.1' gem 'bcrypt-ruby', '3.0.1'
gem 'ruby-protocol-buffers', '1.2.2' gem 'ruby-protocol-buffers', '1.2.2'
gem 'eventmachine', '1.0.3' gem 'eventmachine', '1.0.4'
gem 'amqp', '1.0.2' gem 'amqp', '1.0.2'
gem 'will_paginate' gem 'will_paginate'
gem 'actionmailer', '3.2.13' gem 'actionmailer', '3.2.13'

View File

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
GEM_SERVER=http://localhost:9000/gems GEM_SERVER=https://int.jamkazam.com:9000/gems
echo "starting build..." echo "starting build..."
./build ./build

View File

@ -23,6 +23,8 @@ module JamRuby
validate :validate_photo_info validate :validate_photo_info
validate :require_at_least_one_genre, :unless => :skip_genre_validation validate :require_at_least_one_genre, :unless => :skip_genre_validation
validate :limit_max_genres validate :limit_max_genres
validates_numericality_of :hourly_rate, greater_than:0, less_than:100000, :allow_nil => true
validates_numericality_of :gig_minimum, greater_than:0, less_than:200000, :allow_nil => true
before_save :check_lat_lng before_save :check_lat_lng
before_save :check_website_url before_save :check_website_url
@ -192,6 +194,17 @@ module JamRuby
band.photo_url = params[:photo_url] if params.has_key?(:photo_url) band.photo_url = params[:photo_url] if params.has_key?(:photo_url)
band.logo_url = params[:logo_url] if params.has_key?(:logo_url) band.logo_url = params[:logo_url] if params.has_key?(:logo_url)
band.paid_gigs = params[:paid_gigs] if params.has_key?(:paid_gigs)
band.free_gigs = params[:free_gigs] if params.has_key?(:free_gigs)
band.hourly_rate = (params.has_key?(:hourly_rate) && params[:hourly_rate].to_i > 0) ? params[:hourly_rate] : nil
band.gig_minimum = (params.has_key?(:gig_minimum) && params[:hourly_rate].to_i > 0) ? params[:gig_minimum] : nil
band.add_new_members = params[:add_new_members] if params.has_key?(:add_new_members)
band.touring_option = params[:touring_option] if params.has_key?(:touring_option)
band.band_type = params[:band_type] if params.has_key?(:band_type)
band.band_status = params[:band_status] if params.has_key?(:band_status)
band.concert_count = params[:concert_count] if params.has_key?(:concert_count)
band.play_commitment = params[:play_commitment] if params.has_key?(:play_commitment)
if params.has_key?(:genres) && params[:genres] if params.has_key?(:genres) && params[:genres]
# loop through each genre in the array and save to the db # loop through each genre in the array and save to the db
genres = [] genres = []

View File

@ -125,6 +125,7 @@ module JamRuby
ms ms
end end
# XXX SQL INJECTION
def _genres(rel) def _genres(rel)
gids = json[KEY_GENRES] gids = json[KEY_GENRES]
unless gids.blank? unless gids.blank?
@ -135,11 +136,12 @@ module JamRuby
rel rel
end end
# XXX SQL INJECTION
def _instruments(rel) def _instruments(rel)
unless (instruments = json['instruments']).blank? unless (instruments = json['instruments']).blank?
instsql = "SELECT player_id FROM musicians_instruments WHERE ((" instsql = "SELECT player_id FROM musicians_instruments WHERE (("
instsql += instruments.collect do |inst| instsql += instruments.collect do |inst|
"instrument_id = '#{inst['instrument_id']}' AND proficiency_level = #{inst['proficiency_level']}" "instrument_id = '#{inst['id']}' AND proficiency_level = #{inst['level']}"
end.join(") OR (") end.join(") OR (")
instsql += "))" instsql += "))"
rel = rel.where("users.id IN (#{instsql})") rel = rel.where("users.id IN (#{instsql})")
@ -357,47 +359,54 @@ module JamRuby
return 'Click search button to look for musicians with similar interests, skill levels, etc.' return 'Click search button to look for musicians with similar interests, skill levels, etc.'
end end
jj = self.json jj = self.json
str = 'Current Search: ' str = ''
str += "Sort = #{SORT_ORDERS[json_value(MusicianSearch::KEY_SORT_ORDER)]}" if 0 < (val = jj[KEY_INSTRUMENTS]).length
str += ", Instruments = "
instr_ids = val.collect { |stored_instrument| stored_instrument['id'] }
instrs = Instrument.where(["id IN (?)", instr_ids]).order(:description)
instrs.each_with_index do |ii, idx|
proficiency = val.detect { |stored_instrument| stored_instrument['id'] == ii.id }['level']
str += "#{ii.description} / #{INSTRUMENT_PROFICIENCY[proficiency.to_i]}"
str += ', ' unless idx==(instrs.length-1)
end
end
if (val = jj[KEY_INTERESTS]) != INTEREST_VALS[0] if (val = jj[KEY_INTERESTS]) != INTEREST_VALS[0]
str += "; Interest = #{INTERESTS[val]}" str += ", Interest = #{INTERESTS[val]}"
end end
if (val = jj[KEY_SKILL].to_i) != SKILL_VALS[0] if (val = jj[KEY_SKILL].to_i) != SKILL_VALS[0]
str += "; Skill = #{SKILL_LEVELS[val]}" str += ", Skill = #{SKILL_LEVELS[val]}"
end end
if (val = jj[KEY_STUDIOS].to_i) != STUDIO_COUNTS[0] if (val = jj[KEY_STUDIOS].to_i) != STUDIO_COUNTS[0]
str += "; Studio Sessions = #{STUDIOS_LABELS[val]}" str += ", Studio Sessions = #{STUDIOS_LABELS[val]}"
end end
if (val = jj[KEY_GIGS].to_i) != GIG_COUNTS[0] if (val = jj[KEY_GIGS].to_i) != GIG_COUNTS[0]
str += "; Concert Gigs = #{GIG_LABELS[val]}" str += ", Concert Gigs = #{GIG_LABELS[val]}"
end end
val = jj[KEY_AGES].map(&:to_i) val = jj[KEY_AGES].map(&:to_i)
val.sort! val.sort!
if !val.blank? if !val.blank?
str += "; Ages = " str += ", Ages = "
val.each_with_index do |vv, idx| val.each_with_index do |vv, idx|
str += "#{AGES[vv]}" str += "#{AGES[vv]}"
str += ', ' unless idx==(val.length-1) str += ', ' unless idx==(val.length-1)
end end
end end
if 0 < (val = jj[KEY_GENRES]).length if 0 < (val = jj[KEY_GENRES]).length
str += "; Genres = " str += ", Genres = "
genres = Genre.where(["id IN (?)", val]).order('description').pluck(:description) genres = Genre.where(["id IN (?)", val]).order('description').pluck(:description)
genres.each_with_index do |gg, idx| genres.each_with_index do |gg, idx|
str += "#{gg}" str += "#{gg}"
str += ', ' unless idx==(genres.length-1) str += ', ' unless idx==(genres.length-1)
end end
end end
if 0 < (val = jj[KEY_INSTRUMENTS]).length str += ", Sort = #{SORT_ORDERS[json_value(MusicianSearch::KEY_SORT_ORDER)]}"
str += "; Instruments = "
instr_ids = val.collect { |vv| vv['instrument_id'] } if str.start_with?(', ')
instrs = Instrument.where(["id IN (?)", instr_ids]).order(:description) # trim off any leading ,
instrs.each_with_index do |ii, idx| str = str[2..-1]
proficiency = val.detect { |vv| vv['instrument_id'] == ii.id }['proficiency_level']
str += "#{ii.description} (#{INSTRUMENT_PROFICIENCY[proficiency.to_i]})"
str += ', ' unless idx==(instrs.length-1)
end
end end
str = 'Current Search: ' + str
str str
end end

View File

@ -196,6 +196,11 @@ module JamRuby
validates_numericality_of :last_jam_audio_latency, greater_than:MINIMUM_AUDIO_LATENCY, less_than:MAXIMUM_AUDIO_LATENCY, :allow_nil => true validates_numericality_of :last_jam_audio_latency, greater_than:MINIMUM_AUDIO_LATENCY, less_than:MAXIMUM_AUDIO_LATENCY, :allow_nil => true
validates :last_jam_updated_reason, :inclusion => {:in => [nil, JAM_REASON_REGISTRATION, JAM_REASON_NETWORK_TEST, JAM_REASON_FTUE, JAM_REASON_JOIN, JAM_REASON_IMPORT, JAM_REASON_LOGIN] } validates :last_jam_updated_reason, :inclusion => {:in => [nil, JAM_REASON_REGISTRATION, JAM_REASON_NETWORK_TEST, JAM_REASON_FTUE, JAM_REASON_JOIN, JAM_REASON_IMPORT, JAM_REASON_LOGIN] }
# stored in cents
validates_numericality_of :paid_sessions_hourly_rate, greater_than:0, less_than:200000, :allow_nil => true
# stored in cents
validates_numericality_of :paid_sessions_daily_rate, greater_than:0, less_than:5000000, :allow_nil => true
# custom validators # custom validators
validate :validate_musician_instruments validate :validate_musician_instruments
validate :validate_current_password validate :validate_current_password

View File

@ -67,6 +67,40 @@ describe Band do
band.country.should == band_params[:country] band.country.should == band_params[:country]
end end
it "saves current interests" do
parms = band_params
parms[:paid_gigs]=true
parms[:free_gigs]=false
parms[:hourly_rate]=5000
parms[:gig_minimum]=30000
parms[:add_new_members]=true
parms[:touring_option]=false
parms[:band_type]="virtual"
parms[:band_status]="amateur"
parms[:concert_count]=3
band = Band.save(user, parms)
band.errors.any?.should be_false
band.paid_gigs.should == true
band.free_gigs.should == false
band.hourly_rate.should == 5000
parms[:gig_minimum]=30000
band.add_new_members.should == true
band.touring_option.should == false
band.band_type.should == "virtual"
band.band_status.should == "amateur"
band.concert_count.should == 3
parms[:hourly_rate]="foobar"
parms[:gig_minimum]="barfoo"
band=Band.save(user, parms)
band.errors.any?.should be_true
band.errors[:hourly_rate].should == ["is not a number"]
band.errors[:gig_minimum].should == ["is not a number"]
end
it "ensures user is a musician" do it "ensures user is a musician" do
expect{ Band.save(fan, band_params) }.to raise_error("must be a musician") expect{ Band.save(fan, band_params) }.to raise_error("must be a musician")
end end

View File

@ -206,6 +206,10 @@ def app_config
1 1
end end
def google_public_server_key
"AIzaSyCPTPq5PEcl4XWcm7NZ2IGClZlbsiE8JNo"
end
private private
def audiomixer_workspace_path def audiomixer_workspace_path

View File

@ -36,7 +36,7 @@ gem 'pg', '0.17.1'
gem 'compass-rails', '1.1.3' # 1.1.4 throws an exception on startup about !initialize on nil gem 'compass-rails', '1.1.3' # 1.1.4 throws an exception on startup about !initialize on nil
gem 'rabl', '0.11.0' # for JSON API development gem 'rabl', '0.11.0' # for JSON API development
gem 'gon', '~>4.1.0' # for passthrough of Ruby variables to Javascript variables gem 'gon', '~>4.1.0' # for passthrough of Ruby variables to Javascript variables
gem 'eventmachine', '1.0.3' gem 'eventmachine', '1.0.4'
gem 'faraday', '~>0.9.0' gem 'faraday', '~>0.9.0'
gem 'amqp', '0.9.8' gem 'amqp', '0.9.8'
gem 'logging-rails', :require => 'logging/rails' gem 'logging-rails', :require => 'logging/rails'
@ -95,6 +95,7 @@ gem 'react-rails', '~> 1.0'
source 'https://rails-assets.org' do source 'https://rails-assets.org' do
gem 'rails-assets-reflux' gem 'rails-assets-reflux'
gem 'rails-assets-classnames'
end end
group :development, :test do group :development, :test do

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1018 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -198,6 +198,11 @@
function loggedIn(header, payload) { function loggedIn(header, payload) {
// reason for setTimeout:
// loggedIn causes an absolute ton of initialization to happen, and errors sometimes happen
// but because loggedIn(header,payload) is a callback from a websocket, the browser doesn't show a stack trace...
setTimeout(function() {
server.signedIn = true; server.signedIn = true;
server.clientID = payload.client_id; server.clientID = payload.client_id;
server.publicIP = payload.public_ip; server.publicIP = payload.public_ip;
@ -213,7 +218,7 @@
app.clientId = payload.client_id; app.clientId = payload.client_id;
if(isClientMode()) { if (isClientMode()) {
// tell the backend that we have logged in // tell the backend that we have logged in
context.jamClient.OnLoggedIn(payload.user_id, payload.token); // ACTS AS CONTINUATION context.jamClient.OnLoggedIn(payload.user_id, payload.token); // ACTS AS CONTINUATION
$.cookie('client_id', payload.client_id); $.cookie('client_id', payload.client_id);
@ -239,9 +244,10 @@
activeElementEvent('afterConnect', payload); activeElementEvent('afterConnect', payload);
if(payload.client_update && context.JK.ClientUpdateInstance) { if (payload.client_update && context.JK.ClientUpdateInstance) {
context.JK.ClientUpdateInstance.runCheck(payload.client_update.product, payload.client_update.version, payload.client_update.uri, payload.client_update.size) context.JK.ClientUpdateInstance.runCheck(payload.client_update.product, payload.client_update.version, payload.client_update.uri, payload.client_update.size)
} }
}, 0)
} }
function heartbeatAck(header, payload) { function heartbeatAck(header, payload) {

View File

@ -130,7 +130,7 @@
$('#account-content-scroller').on('click', '#account-my-jamtracks-link', function(evt) { evt.stopPropagation(); navToMyJamTracks(); return false; } ); $('#account-content-scroller').on('click', '#account-my-jamtracks-link', function(evt) { evt.stopPropagation(); navToMyJamTracks(); return false; } );
$('#account-content-scroller').on('click', '#account-edit-identity-link', function(evt) { evt.stopPropagation(); navToEditIdentity(); return false; } ); $('#account-content-scroller').on('click', '#account-edit-identity-link', function(evt) { evt.stopPropagation(); navToEditIdentity(); return false; } );
$('#account-content-scroller').on('click', '#account-edit-profile-link', function(evt) { evt.stopPropagation(); navToEditProfile(); return false; } ); $('#account-content-scroller').on('click', '.account-edit-profile-link', function(evt) { evt.stopPropagation(); navToEditProfile(); return false; } );
$('#account-content-scroller').on('click', '#account-edit-subscriptions-link', function(evt) { evt.stopPropagation(); navToEditSubscriptions(); return false; } ); $('#account-content-scroller').on('click', '#account-edit-subscriptions-link', function(evt) { evt.stopPropagation(); navToEditSubscriptions(); return false; } );
$('#account-content-scroller').on('click', '#account-edit-payments-link', function(evt) { evt.stopPropagation(); navToEditPayments(); return false; } ); $('#account-content-scroller').on('click', '#account-edit-payments-link', function(evt) { evt.stopPropagation(); navToEditPayments(); return false; } );
$('#account-content-scroller').on('click', '#account-edit-audio-link', function(evt) { evt.stopPropagation(); navToEditAudio(); return false; } ); $('#account-content-scroller').on('click', '#account-edit-audio-link', function(evt) { evt.stopPropagation(); navToEditAudio(); return false; } );

View File

@ -207,7 +207,7 @@
rest.getLinks(type) rest.getLinks(type)
.done(populateLinkTable) .done(populateLinkTable)
.fail(function() { .fail(function() {
app.notify({message: 'Unable to fetch links. Please try again later.' }) app.notify({text: 'Unable to fetch links. Please try again later.' })
}) })
} }
} }

View File

@ -145,8 +145,9 @@
$traditionalTouringOption.val(userDetail.traditional_band_touring ? '1' : '0') $traditionalTouringOption.val(userDetail.traditional_band_touring ? '1' : '0')
context.JK.dropdown($traditionalTouringOption) context.JK.dropdown($traditionalTouringOption)
$hourlyRate.val(userDetail.paid_sessions_hourly_rate) // convert the value to cents
$dailyRate.val(userDetail.paid_sessions_daily_rate) $hourlyRate.val(profileUtils.normalizeMoneyForDisplay(userDetail.paid_sessions_hourly_rate));
$dailyRate.val(profileUtils.normalizeMoneyForDisplay(userDetail.paid_sessions_daily_rate));
$cowritingPurpose.val(userDetail.cowriting_purpose) $cowritingPurpose.val(userDetail.cowriting_purpose)
context.JK.dropdown($cowritingPurpose) context.JK.dropdown($cowritingPurpose)
@ -238,11 +239,11 @@
paid_sessions: $screen.find('input[name=paid_sessions]:checked').val(), paid_sessions: $screen.find('input[name=paid_sessions]:checked').val(),
paid_session_genres: $paidSessionsGenreList.html() === NONE_SPECIFIED ? [] : $paidSessionsGenreList.html().split(GENRE_LIST_DELIMITER), paid_session_genres: $paidSessionsGenreList.html() === NONE_SPECIFIED ? [] : $paidSessionsGenreList.html().split(GENRE_LIST_DELIMITER),
paid_sessions_hourly_rate: $hourlyRate.val(), paid_sessions_hourly_rate: profileUtils.normalizeMoneyForSubmit($hourlyRate.val()),
paid_sessions_daily_rate: $dailyRate.val(), paid_sessions_daily_rate: profileUtils.normalizeMoneyForSubmit($dailyRate.val()),
free_sessions: $screen.find('input[name=free_sessions]:checked').val(), free_sessions: $screen.find('input[name=free_sessions]:checked').val(),
free_session_genre: $freeSessionsGenreList.html() === NONE_SPECIFIED ? [] : $freeSessionsGenreList.html().split(GENRE_LIST_DELIMITER), free_session_genres: $freeSessionsGenreList.html() === NONE_SPECIFIED ? [] : $freeSessionsGenreList.html().split(GENRE_LIST_DELIMITER),
cowriting: $screen.find('input[name=cowriting]:checked').val(), cowriting: $screen.find('input[name=cowriting]:checked').val(),
cowriting_genres: $cowritingGenreList.html() === NONE_SPECIFIED ? [] : $cowritingGenreList.html().split(GENRE_LIST_DELIMITER), cowriting_genres: $cowritingGenreList.html() === NONE_SPECIFIED ? [] : $cowritingGenreList.html().split(GENRE_LIST_DELIMITER),
@ -263,7 +264,12 @@
var errors = JSON.parse(xhr.responseText) var errors = JSON.parse(xhr.responseText)
if(xhr.status == 422) { if(xhr.status == 422) {
context.JK.append_errors($hourlyRate, 'paid_sessions_hourly_rate', errors)
context.JK.append_errors($dailyRate, 'paid_sessions_daily_rate', errors)
if(errors.errors.length > 0) {
app.notifyServerError(xhr)
}
} }
else { else {
app.ajaxError(xhr, textStatus, errorMessage) app.ajaxError(xhr, textStatus, errorMessage)

View File

@ -20,6 +20,9 @@
var ui = new context.JK.UIHelper(JK.app); var ui = new context.JK.UIHelper(JK.app);
var target = {}; var target = {};
var profileUtils = context.JK.ProfileUtils; var profileUtils = context.JK.ProfileUtils;
parent
var parent = $(".account-profile-samples")
var $screen = $('.profile-online-sample-controls', parent); var $screen = $('.profile-online-sample-controls', parent);
// online presences // online presences
var $website = $screen.find('.website'); var $website = $screen.find('.website');
@ -60,6 +63,7 @@
function afterShow(data) { function afterShow(data) {
$.when(loadFn()) $.when(loadFn())
.done(function(targetPlayer) { .done(function(targetPlayer) {
console.log("TARGET PLAYER", targetPlayer)
if (targetPlayer && targetPlayer.keys && targetPlayer.keys.length > 0) { if (targetPlayer && targetPlayer.keys && targetPlayer.keys.length > 0) {
renderPlayer(targetPlayer) renderPlayer(targetPlayer)
} }
@ -147,22 +151,27 @@
if (samples && samples.length > 0) { if (samples && samples.length > 0) {
$.each(samples, function(index, val) { $.each(samples, function(index, val) {
recordingSources.push({ var source = {
'url': val.url, 'url': val.url,
'recording_id': val.service_id, 'recording_id': val.service_id,
'recording_title': val.description 'recording_title': val.description
}
recordingSources.push(source);
buildNonJamKazamEntry($sampleList, type, source);
}); });
}
}
}
function buildNonJamKazamEntry($sampleList, type, source) {
// TODO: this code is repeated in HTML file // TODO: this code is repeated in HTML file
var recordingIdAttr = ' data-recording-id="' + val.service_id + '" '; var recordingIdAttr = ' data-recording-id="' + source.recording_id + '" ';
var recordingUrlAttr = ' data-recording-url="' + val.url + '" '; var recordingUrlAttr = ' data-recording-url="' + source.url + '" ';
var recordingTitleAttr = ' data-recording-title="' + val.description + '"'; var recordingTitleAttr = ' data-recording-title="' + source.recording_title + '"';
var title = formatTitle(val.description); var title = formatTitle(source.recording_title);
$sampleList.find(".empty").addClass("hidden")
$sampleList.append('<div class="clearall recording-row left entry"' + recordingIdAttr + recordingUrlAttr + recordingTitleAttr + '>' + title + '</div>'); $sampleList.append('<div class="clearall recording-row left entry"' + recordingIdAttr + recordingUrlAttr + recordingTitleAttr + '>' + title + '</div>');
$sampleList.append('<div class="right close-button" data-recording-type="' + type + '"' + recordingIdAttr + '>X</div>'); $sampleList.append('<div class="right close-button" data-recording-type="' + type + '"' + recordingIdAttr + '>X</div>');
});
}
}
} }
function buildJamkazamEntry(recordingId, recordingName) { function buildJamkazamEntry(recordingId, recordingName) {
@ -179,10 +188,7 @@
$btnAddJkRecording.click(function(evt) { $btnAddJkRecording.click(function(evt) {
evt.preventDefault(); evt.preventDefault();
// retrieve recordings and pass to modal dialog ui.launchRecordingSelectorDialog(jamkazamRecordingSources, function(selectedRecordings) {
api.getClaimedRecordings()
.done(function(response) {
ui.launchRecordingSelectorDialog(response, jamkazamRecordingSources, function(selectedRecordings) {
$jamkazamSampleList.empty(); $jamkazamSampleList.empty();
jamkazamRecordingSources = []; jamkazamRecordingSources = [];
@ -197,7 +203,7 @@
buildJamkazamEntry(val.id, val.name); buildJamkazamEntry(val.id, val.name);
}); });
}); });
});
return false; return false;
}); });
@ -287,6 +293,7 @@
disableSubmits() disableSubmits()
var player = buildPlayer() var player = buildPlayer()
updateFn({ updateFn({
website: player.website, website: player.website,
online_presences: player.online_presences, online_presences: player.online_presences,
@ -316,8 +323,13 @@
addPerformanceSamples(ps, $soundCloudSampleList, performanceSampleTypes.SOUNDCLOUD.description); addPerformanceSamples(ps, $soundCloudSampleList, performanceSampleTypes.SOUNDCLOUD.description);
addPerformanceSamples(ps, $youTubeSampleList, performanceSampleTypes.YOUTUBE.description); addPerformanceSamples(ps, $youTubeSampleList, performanceSampleTypes.YOUTUBE.description);
var website = $website.val()
if (website == '') {
website = null;
}
return { return {
website: $website.val(), website: website,
online_presences: op, online_presences: op,
performance_samples: ps performance_samples: ps
} }
@ -428,8 +440,8 @@
siteSuccessCallback($inputDiv, youTubeRecordingValidator, $youTubeSampleList, 'youtube'); siteSuccessCallback($inputDiv, youTubeRecordingValidator, $youTubeSampleList, 'youtube');
} }
function siteSuccessCallback($inputDiv, recordingSiteValidator, sampleList, type) { function siteSuccessCallback($inputDiv, recordingSiteValidator, $sampleList, type) {
sampleList.find(".empty").addClass("hidden") $sampleList.find(".empty").addClass("hidden")
$inputDiv.removeClass('error'); $inputDiv.removeClass('error');
$inputDiv.find('.error-text').remove(); $inputDiv.find('.error-text').remove();
@ -437,13 +449,7 @@
if (recordingSources && recordingSources.length > 0) { if (recordingSources && recordingSources.length > 0) {
var addedRecording = recordingSources[recordingSources.length-1]; var addedRecording = recordingSources[recordingSources.length-1];
// TODO: this code is repeated in elsewhere in this JS file: buildNonJamKazamEntry($sampleList, type, addedRecording);
var recordingIdAttr = ' data-recording-id="' + addedRecording.recording_id + '" ';
var recordingUrlAttr = ' data-recording-url="' + addedRecording.url + '" ';
var recordingTitleAttr = ' data-recording-title="' + addedRecording.recording_title + '"';
var title = formatTitle(addedRecording.recording_title);
sampleList.append('<div class="clearall recording-row left entry"' + recordingIdAttr + recordingUrlAttr + recordingTitleAttr + '>' + title + '</div>');
sampleList.append('<div class="right close-button" data-recording-type="' + type + '"' + recordingIdAttr + '>X</div>');
} }
$inputDiv.find('input').val(''); $inputDiv.find('input').val('');

View File

@ -3,18 +3,16 @@
"use strict"; "use strict";
context.JK = context.JK || {}; context.JK = context.JK || {};
context.JK.AddNewGearDialog = function(app, sessionScreen) { context.JK.AddNewGearDialog = function(app) {
var logger = context.JK.logger; var logger = context.JK.logger;
function events() { function events() {
$('#btn-leave-session-test').click(function() { $('#btn-leave-session-test').click(function() {
sessionScreen.setPromptLeave(false); context.SessionActions.leaveSession.trigger({location: '/client#/home'})
app.layout.closeDialog('configure-tracks'); app.layout.closeDialog('configure-tracks');
context.location = "/client#/home";
app.layout.startNewFtue(); app.layout.startNewFtue();
}); });

View File

@ -161,7 +161,7 @@
/** /**
setTimeout(function() { setTimeout(function() {
var inputTracks = context.JK.TrackHelpers.getTracks(context.jamClient, 2); var inputTracks = context.JK.TrackHelpers.getTracks(context.jamClient, 4);
// this is some ugly logic coming up, here's why: // this is some ugly logic coming up, here's why:
// we need the id (guid) that the backend generated for the new track we just added // we need the id (guid) that the backend generated for the new track we just added

View File

@ -38,6 +38,7 @@
//= require jquery.exists //= require jquery.exists
//= require jquery.payment //= require jquery.payment
//= require jquery.visible //= require jquery.visible
//= require classnames
//= require reflux //= require reflux
//= require howler.core.js //= require howler.core.js
//= require jstz //= require jstz
@ -54,11 +55,12 @@
//= require react //= require react
//= require react_ujs //= require react_ujs
//= require react-init //= require react-init
//= require react-components
//= require web/signup_helper //= require web/signup_helper
//= require web/signin_helper //= require web/signin_helper
//= require web/signin //= require web/signin
//= require web/tracking //= require web/tracking
//= require webcam_viewer
//= require react-components
//= require_directory . //= require_directory .
//= require_directory ./dialog //= require_directory ./dialog
//= require_directory ./wizard //= require_directory ./wizard

View File

@ -37,20 +37,16 @@
} }
function onGenericEvent(type, text) { function onGenericEvent(type, text) {
context.setTimeout(function() {
var alert = ALERT_TYPES[type]; var alert = ALERT_TYPES[type];
if(alert && alert.title) { if(alert && alert.title) {
app.notify({ context.NotificationActions.backendNotification({msg: alert.title, detail: alert.message, backend_detail:text, help: alert.help})
"title": ALERT_TYPES[type].title,
"text": text,
"icon_url": "/assets/content/icon_alert_big.png"
});
} }
else { else {
logger.debug("Unhandled Backend Event type %o, data %o", type, text) logger.debug("Unhandled Backend Event type %o, data %o", type, text)
} }
}, 1);
} }
function alertCallback(type, text) { function alertCallback(type, text) {
@ -77,8 +73,11 @@
} }
if (type === 2) { // BACKEND_MIXER_CHANGE if (type === 2) { // BACKEND_MIXER_CHANGE
if(context.JK.CurrentSessionModel)
context.JK.CurrentSessionModel.onBackendMixerChanged(type, text) context.MixerActions.mixersChanged(type, text)
//if(context.JK.CurrentSessionModel)
// context.JK.CurrentSessionModel.onBackendMixerChanged(type, text)
} }
else if (type === ALERT_NAMES.NO_VALID_AUDIO_CONFIG) { // NO_VALID_AUDIO_CONFIG else if (type === ALERT_NAMES.NO_VALID_AUDIO_CONFIG) { // NO_VALID_AUDIO_CONFIG
if(context.JK.GearUtilsInstance && context.JK.GearUtilsInstance.isRestartingAudio()) { if(context.JK.GearUtilsInstance && context.JK.GearUtilsInstance.isRestartingAudio()) {
@ -101,28 +100,36 @@
onStunEvent(); onStunEvent();
} }
else if (type === 26) { // DEAD_USER_REMOVE_EVENT else if (type === 26) { // DEAD_USER_REMOVE_EVENT
if(context.JK.CurrentSessionModel) MixerActions.deadUserRemove(text);
context.JK.CurrentSessionModel.onDeadUserRemove(type, text); //if(context.JK.CurrentSessionModel)
// context.JK.CurrentSessionModel.onDeadUserRemove(type, text);
} }
else if (type === 27) { // WINDOW_CLOSE_BACKGROUND_MODE else if (type === 27) { // WINDOW_CLOSE_BACKGROUND_MODE
if(context.JK.CurrentSessionModel)
context.JK.CurrentSessionModel.onWindowBackgrounded(type, text); SessionActions.windowBackgrounded()
//if(context.JK.CurrentSessionModel)
// context.JK.CurrentSessionModel.onWindowBackgrounded(type, text);
} }
else if(type === ALERT_NAMES.SESSION_LIVEBROADCAST_FAIL) { else if(type === ALERT_NAMES.SESSION_LIVEBROADCAST_FAIL) {
if(context.JK.CurrentSessionModel) SessionActions.broadcastFailure(text)
context.JK.CurrentSessionModel.onBroadcastFailure(type, text); //if(context.JK.CurrentSessionModel)
// context.JK.CurrentSessionModel.onBroadcastFailure(type, text);
} }
else if(type === ALERT_NAMES.SESSION_LIVEBROADCAST_ACTIVE) { else if(type === ALERT_NAMES.SESSION_LIVEBROADCAST_ACTIVE) {
if(context.JK.CurrentSessionModel) SessionActions.broadcastSuccess(text)
context.JK.CurrentSessionModel.onBroadcastSuccess(type, text); //if(context.JK.CurrentSessionModel)
// context.JK.CurrentSessionModel.onBroadcastSuccess(type, text);
} }
else if(type === ALERT_NAMES.SESSION_LIVEBROADCAST_STOPPED) { else if(type === ALERT_NAMES.SESSION_LIVEBROADCAST_STOPPED) {
if(context.JK.CurrentSessionModel) SessionActions.broadcastStopped(text)
context.JK.CurrentSessionModel.onBroadcastStopped(type, text); //if(context.JK.CurrentSessionModel)
//context.JK.CurrentSessionModel.onBroadcastStopped(type, text);
} }
else if(type === ALERT_NAMES.RECORD_PLAYBACK_STATE) { else if(type === ALERT_NAMES.RECORD_PLAYBACK_STATE) {
if(context.JK.CurrentSessionModel) //if(context.JK.CurrentSessionModel)
context.JK.CurrentSessionModel.onPlaybackStateChange(type, text); // context.JK.CurrentSessionModel.onPlaybackStateChange(type, text);
context.MediaPlaybackActions.playbackStateChange(text);
} }
else if((!context.JK.CurrentSessionModel || !context.JK.CurrentSessionModel.inSession()) && else if((!context.JK.CurrentSessionModel || !context.JK.CurrentSessionModel.inSession()) &&
(ALERT_NAMES.INPUT_IO_RATE == type || ALERT_NAMES.INPUT_IO_JTR == type || ALERT_NAMES.OUTPUT_IO_RATE == type || ALERT_NAMES.OUTPUT_IO_JTR== type)) { (ALERT_NAMES.INPUT_IO_RATE == type || ALERT_NAMES.INPUT_IO_JTR == type || ALERT_NAMES.OUTPUT_IO_RATE == type || ALERT_NAMES.OUTPUT_IO_JTR== type)) {

View File

@ -151,8 +151,8 @@
$("#play-commitment").val('1') $("#play-commitment").val('1')
$("#hourly-rate").val("0.0") $screen.find("#hourly-rate").val("0")
$("#gig-minimum").val("0.0") $screen.find("#gig-minimum").val("0")
resetGenres(); resetGenres();
renderDesiredExperienceLabel([]) renderDesiredExperienceLabel([])
@ -225,9 +225,14 @@
band.free_gigs=$('input[name="free_gigs"]:checked').val()=="yes" band.free_gigs=$('input[name="free_gigs"]:checked').val()=="yes"
band.touring_option=$('#touring-option').val()=="yes" band.touring_option=$('#touring-option').val()=="yes"
band.play_commitment=$("#play-commitment").val()
band.hourly_rate=$("#hourly-rate").val() if ($screen.find("#play-commitment").length == 0) {
band.gig_minimum=$("#gig-minimum").val() logger.error("MISSING PLAY MOTIMMENT")
}
band.play_commitment = $screen.find("#play-commitment").val()
band.hourly_rate = profileUtils.normalizeMoneyForSubmit($screen.find("#hourly-rate").val())
band.gig_minimum = profileUtils.normalizeMoneyForSubmit($("#gig-minimum").val())
if (currentStep==GENRE_STEP) { if (currentStep==GENRE_STEP) {
band.genres = getSelectedGenres(); band.genres = getSelectedGenres();
@ -424,8 +429,8 @@
$('#touring-option').val(band.touring_option ? 'yes' : 'no') $('#touring-option').val(band.touring_option ? 'yes' : 'no')
$("#play-commitment").val(band.play_commitment) $("#play-commitment").val(band.play_commitment)
$("#hourly-rate").val(band.hourly_rate) $("#hourly-rate").val(profileUtils.normalizeMoneyForDisplay(band.hourly_rate))
$("#gig-minimum").val(band.gig_minimum) $("#gig-minimum").val(profileUtils.normalizeMoneyForDisplay(band.gig_minimum))
// Initialize avatar // Initialize avatar
if (band.photo_url) { if (band.photo_url) {

View File

@ -216,7 +216,7 @@
updateUri = uri; updateUri = uri;
updateSize = size; updateSize = size;
if(context.JK.CurrentSessionModel && context.JK.CurrentSessionModel.inSession()) { if(context.SessionStore.inSession()) {
logger.debug("deferring client update because in session") logger.debug("deferring client update because in session")
return; return;
} }

View File

@ -3,7 +3,7 @@
$ = jQuery $ = jQuery
context = window context = window
context.JK ||= {}; context.JK ||= {};
broadcastActions = context.JK.Actions.Broadcast broadcastActions = @BroadcastActions
context.JK.ClientInit = class ClientInit context.JK.ClientInit = class ClientInit
constructor: () -> constructor: () ->
@ -21,7 +21,10 @@ context.JK.ClientInit = class ClientInit
this.watchBroadcast() this.watchBroadcast()
checkBroadcast: () => checkBroadcast: () =>
broadcastActions.load.triggerPromise() broadcastActions.load.triggerPromise().catch(() ->
false
)
watchBroadcast: () => watchBroadcast: () =>
if context.JK.currentUserId if context.JK.currentUserId

View File

@ -112,10 +112,14 @@
$voiceChatTabSelector.click(function () { $voiceChatTabSelector.click(function () {
// validate audio settings // validate audio settings
if (validateAudioSettings()) { if (validateAudioSettings()) {
logger.debug("initializing voice chat helper")
configureTracksHelper.reset(); configureTracksHelper.reset();
voiceChatHelper.reset(); voiceChatHelper.reset();
showVoiceChatPanel(); showVoiceChatPanel();
} }
else {
logger.debug("invalid audio settings; ignoring")
}
}); });
$btnCancel.click(function() { $btnCancel.click(function() {

View File

@ -120,11 +120,11 @@
openingRecording = true; openingRecording = true;
// tell the server we are about to start a recording // tell the server we are about to start a recording
rest.startPlayClaimedRecording({id: context.JK.CurrentSessionModel.id(), claimed_recording_id: claimedRecording.id}) rest.startPlayClaimedRecording({id: context.SessionStore.id(), claimed_recording_id: claimedRecording.id})
.done(function(response) { .done(function(response) {
// update session info // update session info
context.JK.CurrentSessionModel.updateSession(response); context.SessionActions.updateSession.trigger(response);
var recordingId = $(this).attr('data-recording-id'); var recordingId = $(this).attr('data-recording-id');
var openRecordingResult = context.jamClient.OpenRecording(claimedRecording.recording); var openRecordingResult = context.jamClient.OpenRecording(claimedRecording.recording);
@ -138,7 +138,7 @@
"icon_url": "/assets/content/icon_alert_big.png" "icon_url": "/assets/content/icon_alert_big.png"
}); });
rest.stopPlayClaimedRecording({id: context.JK.CurrentSessionModel.id(), claimed_recording_id: claimedRecording.id}) rest.stopPlayClaimedRecording({id: context.SessionStore.id(), claimed_recording_id: claimedRecording.id})
.fail(function(jqXHR) { .fail(function(jqXHR) {
app.notify({ app.notify({
"title": "Couldn't Stop Recording Playback", "title": "Couldn't Stop Recording Playback",

View File

@ -85,7 +85,7 @@
var backingTrack = $(this).data('server-model'); var backingTrack = $(this).data('server-model');
// tell the server we are about to open a backing track: // tell the server we are about to open a backing track:
rest.openBackingTrack({id: context.JK.CurrentSessionModel.id(), backing_track_path: backingTrack.name}) rest.openBackingTrack({id: context.SessionStore.id(), backing_track_path: backingTrack.name})
.done(function(response) { .done(function(response) {
var result = context.jamClient.SessionOpenBackingTrackFile(backingTrack.name, false); var result = context.jamClient.SessionOpenBackingTrackFile(backingTrack.name, false);
@ -99,7 +99,7 @@
// else { // else {
// logger.error("unable to open backing track") // logger.error("unable to open backing track")
// } // }
context.JK.CurrentSessionModel.refreshCurrentSession(true); context.SessionActions.syncWithServer()
}) })
.fail(function(jqXHR) { .fail(function(jqXHR) {

View File

@ -86,10 +86,10 @@
var jamTrack = $(this).data('server-model'); var jamTrack = $(this).data('server-model');
// tell the server we are about to open a jamtrack // tell the server we are about to open a jamtrack
rest.openJamTrack({id: context.JK.CurrentSessionModel.id(), jam_track_id: jamTrack.id}) rest.openJamTrack({id: context.SessionStore.id(), jam_track_id: jamTrack.id})
.done(function(response) { .done(function(response) {
$dialog.data('result', {success:true, jamTrack: jamTrack}) $dialog.data('result', {success:true, jamTrack: jamTrack})
context.JK.CurrentSessionModel.updateSession(response); context.SessionActions.updateSession.trigger(response);
app.layout.closeDialog('open-jam-track-dialog'); app.layout.closeDialog('open-jam-track-dialog');
}) })
.fail(function(jqXHR) { .fail(function(jqXHR) {

View File

@ -54,6 +54,7 @@
function events() { function events() {
$('#btn-rate-session-cancel', $scopeSelector).click(function(evt) { $('#btn-rate-session-cancel', $scopeSelector).click(function(evt) {
closeDialog(); closeDialog();
return false;
}); });
$('#btn-rate-session-up', $scopeSelector).click(function(evt) { $('#btn-rate-session-up', $scopeSelector).click(function(evt) {
if ($(this).hasClass('selected')) { if ($(this).hasClass('selected')) {

View File

@ -57,7 +57,7 @@
}); });
} }
else if (localResult.aggregate_state == 'PARTIALLY_MISSING') { else if (localResult.aggregate_state == 'PARTIALLY_MISSING') {
logger.error("unable to open recording due to some missing tracks: %o", localResults); logger.error("unable to open recording due to some missing tracks: %o", recording, localResults);
app.notify({ app.notify({
title: "Unable to Open Recording for Playback", title: "Unable to Open Recording for Playback",
text: "Some of your tracks associated with the recording are missing. This is a bug in the application.", text: "Some of your tracks associated with the recording are missing. This is a bug in the application.",

View File

@ -2,7 +2,7 @@
"use strict"; "use strict";
context.JK = context.JK || {}; context.JK = context.JK || {};
context.JK.RecordingSelectorDialog = function(app, recordings, selectedRecordings, selectCallback) { context.JK.RecordingSelectorDialog = function(app, selectedRecordings, selectCallback) {
var logger = context.JK.logger; var logger = context.JK.logger;
var rest = context.JK.Rest(); var rest = context.JK.Rest();
var recordingUtils = context.JK.RecordingUtils; var recordingUtils = context.JK.RecordingUtils;
@ -10,173 +10,26 @@
var dialogId = 'recording-selector-dialog'; var dialogId = 'recording-selector-dialog';
var $screen = $('#' + dialogId); var $screen = $('#' + dialogId);
var $btnSelect = $screen.find(".btn-select-recordings"); var $btnSelect = $screen.find(".btn-select-recordings");
var $instructions = $screen.find('#instructions');
var $recordings = $screen.find('.recordings'); var $recordings = $screen.find('.recordings');
var $paginatorHolder = null;
var feedHelper = new context.JK.Feed(app); var feedHelper = new context.JK.Feed(app);
var $scroller = $recordings;
var $content = $recordings;
var $noMoreFeeds = $screen.find('.end-of-list');
var $empty = $();
feedHelper.initialize($screen, $scroller, $content, $noMoreFeeds, $empty, $empty, $empty, $empty, {sort: 'date', time_range: 'all', type: 'recording', show_checkbox: true, hide_avatar: true});
function beforeShow(data) { function beforeShow(data) {
} }
function afterShow(data) { function afterShow(data) {
$recordings.empty(); feedHelper.setUser(context.JK.currentUserId)
feedHelper.refresh()
$.each(recordings, function(index, val) {
bindRecordingItem(val);
});
// hide the avatars // hide the avatars
$screen.find('.avatar-small.ib').hide(); //$screen.find('.avatar-small.ib').hide();
}
/********* THE FOLLOWING BLOCK IS REPEATED IN feedHelper.js **********/
function startRecordingPlay($feedItem) {
var img = $('.play-icon', $feedItem);
var $controls = $feedItem.find('.recording-controls');
img.attr('src', '/assets/content/icon_pausebutton.png');
$controls.trigger('play.listenRecording');
$feedItem.data('playing', true);
}
function stopRecordingPlay($feedItem) {
var img = $('.play-icon', $feedItem);
var $controls = $feedItem.find('.recording-controls');
img.attr('src', '/assets/content/icon_playbutton.png');
$controls.trigger('pause.listenRecording');
$feedItem.data('playing', false);
}
function toggleRecordingPlay() {
var $playLink = $(this);
var $feedItem = $playLink.closest('.feed-entry');
var playing = $feedItem.data('playing');
if(playing) {
stopRecordingPlay($feedItem);
}
else {
startRecordingPlay($feedItem);
}
return false;
}
function toggleRecordingDetails() {
var $detailsLink = $(this);
var $feedItem = $detailsLink.closest('.feed-entry');
var $musicians = $feedItem.find('.musician-detail');
var $description = $feedItem.find('.description');
var $name = $feedItem.find('.name');
var toggledOpen = $detailsLink.data('toggledOpen');
if(toggledOpen) {
toggleClose($feedItem, $name, $description, $musicians)
}
else {
toggleOpen($feedItem, $name, $description, $musicians)
}
toggledOpen = !toggledOpen;
$detailsLink.data('toggledOpen', toggledOpen);
return false;
}
function stateChangeRecording(e, data) {
var $controls = data.element;
var $feedItem = $controls.closest('.feed-entry');
var $sliderBar = $('.recording-position', $feedItem);
var $statusBar = $('.recording-status', $feedItem);
var $currentTime = $('.recording-current', $feedItem);
var $status = $('.status-text', $feedItem);
var $playButton = $('.play-button', $feedItem);
if(data.isEnd) stopRecordingPlay($feedItem);
if(data.isError) {
$sliderBar.hide();
$playButton.hide();
$currentTime.hide();
$statusBar.show();
$status.text(data.displayText);
}
}
function toggleOpen($feedItem, $name, $description, $musicians) {
$description.trigger('destroy.dot');
$description.data('original-height', $description.css('height')).css('height', 'auto');
$name.trigger('destroy.dot');
$name.data('original-height', $name.css('height')).css('height', 'auto');
$musicians.show();
$feedItem.animate({'max-height': '1000px'});
}
function toggleClose($feedItem, $name, $description, $musicians, immediate) {
$feedItem.css('height', $feedItem.height() + 'px')
$feedItem.animate({'height': $feedItem.data('original-max-height')}, immediate ? 0 : 400).promise().done(function() {
$feedItem.css('height', 'auto').css('max-height', $feedItem.data('original-max-height'));
$musicians.hide();
$description.css('height', $description.data('original-height'));
$description.dotdotdot();
$name.css('height', $name.data('original-height'));
$name.dotdotdot();
});
}
/**********************************************************/
function bindRecordingItem(claimedRecording) {
claimedRecording.recording.mix_info = recordingUtils.createMixInfo({state: claimedRecording.recording.mix_state});
var options = {
feed_item: claimedRecording.recording,
candidate_claimed_recording: claimedRecording,
mix_class: claimedRecording['has_mix?'] ? 'has-mix' : 'no-mix',
};
var $feedItem = $(context._.template($('#template-feed-recording').html(), options, {variable: 'data'}));
var $controls = $feedItem.find('.recording-controls');
var $titleText = $feedItem.find('.title .title-text');
// if this item will be discarded, tack on a * to the RECORDING NAME
var discardTime = claimedRecording.recording['when_will_be_discarded?'];
if(discardTime) {
context.JK.helpBubble($titleText, 'recording-discarded-soon', {discardTime: discardTime}, {});
$titleText.text($titleText.text() + '*');
}
$controls.data('mix-state', claimedRecording.recording.mix_info); // for recordingUtils helper methods
$controls.data('server-info', claimedRecording.recording.mix); // for recordingUtils helper methods
$controls.data('view-context', 'feed');
$('.timeago', $feedItem).timeago();
context.JK.prettyPrintElements($('.duration', $feedItem));
context.JK.setInstrumentAssetPath($('.instrument-icon', $feedItem));
$('.details', $feedItem).click(toggleRecordingDetails);
$('.details-arrow', $feedItem).click(toggleRecordingDetails);
$('.play-button', $feedItem).click(toggleRecordingPlay);
var checked = '';
var match = $.grep(selectedRecordings, function(obj, index) {
return obj.claimed_recording_id === claimedRecording.id;
});
if (match && match.length > 0) {
checked = 'checked';
}
// put the item on the page
$recordings.append("<div class='left'><input type='checkbox' " + checked + " data-recording-id='" + claimedRecording.id + "' data-recording-title='" + claimedRecording.name + "' />");
$recordings.append($feedItem);
// these routines need the item to have height to work (must be after renderFeed)
$controls.listenRecording({recordingId: claimedRecording.recording.id, claimedRecordingId: options.candidate_claimed_recording.id, sliderSelector:'.recording-slider', sliderBarSelector: '.recording-playback', currentTimeSelector:'.recording-current'});
$controls.bind('statechange.listenRecording', stateChangeRecording);
$('.dotdotdot', $feedItem).dotdotdot();
$feedItem.data('original-max-height', $feedItem.css('height'));
context.JK.bindHoverEvents($feedItem);
context.JK.bindProfileClickEvents($feedItem);
} }
function afterHide() { function afterHide() {
@ -187,10 +40,10 @@
} }
function events() { function events() {
$btnSelect.click(function(evt) { $btnSelect.off('click').on('click', function(evt) {
evt.preventDefault(); evt.preventDefault();
var preSelectedRecordings = []; var preSelectedRecordings = [];
$recordings.find('input[type=checkbox]:checked').each(function(index) { $recordings.find('.select-box input[type=checkbox]:checked').each(function(index) {
preSelectedRecordings.push({ preSelectedRecordings.push({
"id": $(this).attr('data-recording-id'), "id": $(this).attr('data-recording-id'),
"name": $(this).attr('data-recording-title') "name": $(this).attr('data-recording-title')
@ -198,6 +51,7 @@
}); });
if (selectCallback) { if (selectCallback) {
console.log("calling selectCallback", preSelectedRecordings)
selectCallback(preSelectedRecordings); selectCallback(preSelectedRecordings);
} }
@ -217,8 +71,6 @@
app.bindDialog(dialogId, dialogBindings); app.bindDialog(dialogId, dialogBindings);
$instructions.html('Select one or more recordings and click ADD to add JamKazam recordings to your performance samples.');
events(); events();
} }

View File

@ -0,0 +1,36 @@
$ = jQuery
context = window
context.JK ||= {}
MIX_MODES = context.JK.MIX_MODES
context.JK.SessionMasterMixDialog = class SessionMasterMixDialog
constructor: (@app) ->
@rest = context.JK.Rest()
@logger = context.JK.logger
@screen = null
@dialogId = 'session-master-mix-dialog'
@dialog = null
@closeBtn = null
initialize:() =>
dialogBindings =
'beforeShow' : @beforeShow
'afterShow' : @afterShow
'afterHide' : @afterHide
@dialog = $('[layout-id="' + @dialogId + '"]')
@app.bindDialog(@dialogId, dialogBindings)
@content = @dialog.find(".dialog-inner")
beforeShow:() =>
@logger.debug("session-master-mix-dlg: beforeShow")
context.jamClient.SetMixerMode(MIX_MODES.MASTER)
afterShow:() =>
@logger.debug("session-master-mix-dlg: afterShow")
afterHide:() =>
context.jamClient.SetMixerMode(MIX_MODES.PERSONAL)

View File

@ -1,16 +1,17 @@
(function(context,$) { (function(context,$) {
context.JK = context.JK || {}; context.JK = context.JK || {};
context.JK.SessionSettingsDialog = function(app, sessionScreen) { context.JK.SessionSettingsDialog = function(app) {
var logger = context.JK.logger; var logger = context.JK.logger;
var gearUtils = context.JK.GearUtilsInstance; var gearUtils = context.JK.GearUtilsInstance;
var $dialog; var $dialog;
var $screen = $('#session-settings'); var $screen = $('#session-settings');
var $selectedFilenames = $screen.find('#selected-filenames'); //var $selectedFilenames = $screen.find('#selected-filenames');
var $uploadSpinner = $screen.find('.upload-spinner'); var $uploadSpinner = $screen.find('.spinner-small');
var $selectedFilenames = $('#settings-selected-filenames'); //var $selectedFilenames = $('#settings-selected-filenames');
var $inputFiles = $screen.find('#session-select-files'); var $inputFiles = $screen.find('#session-select-files');
var $btnSelectFiles = $screen.find('.btn-select-files'); var $btnSelectFiles = $screen.find('.btn-select-files');
var $inputBox = $screen.find('.inputbox')
var rest = new JK.Rest(); var rest = new JK.Rest();
var sessionId; var sessionId;
@ -21,7 +22,7 @@
context.JK.GenreSelectorHelper.render('#session-settings-genre'); context.JK.GenreSelectorHelper.render('#session-settings-genre');
$dialog = $('[layout-id="session-settings"]'); $dialog = $('[layout-id="session-settings"]');
var currentSession = sessionScreen.getCurrentSession(); var currentSession = context.SessionStore.currentSession;
sessionId = currentSession.id; sessionId = currentSession.id;
// id // id
@ -65,13 +66,21 @@
$('#session-settings-fan-access').val('listen-chat-band'); $('#session-settings-fan-access').val('listen-chat-band');
} }
// notation files /**
// notation files in the account screen. ugh.
$selectedFilenames.empty(); $selectedFilenames.empty();
for (var i=0; i < currentSession.music_notations.length; i++) { for (var i=0; i < currentSession.music_notations.length; i++) {
var notation = currentSession.music_notations[i]; var notation = currentSession.music_notations[i];
$selectedFilenames.append('<a href="' + notation.file_url + '" rel="external">' + notation.file_name + '</a>&nbsp;'); $selectedFilenames.append('<a href="' + notation.file_url + '" rel="external">' + notation.file_name + '</a>&nbsp;');
}*/
$inputBox.empty();
for (var i=0; i < currentSession.music_notations.length; i++) {
var notation = currentSession.music_notations[i];
addNotation(notation)
} }
context.JK.dropdown($('#session-settings-language')); context.JK.dropdown($('#session-settings-language'));
context.JK.dropdown($('#session-settings-musician-access')); context.JK.dropdown($('#session-settings-musician-access'));
context.JK.dropdown($('#session-settings-fan-access')); context.JK.dropdown($('#session-settings-fan-access'));
@ -81,6 +90,29 @@
$('#session-settings-fan-access').easyDropDown(easyDropDownState) $('#session-settings-fan-access').easyDropDown(easyDropDownState)
} }
function addNotation(notation) {
var $notation = $('<div class="notation-entry"><div>' + notation.file_name + '</div><a href="#" data-id="' + notation.id + '">X</a></div>')
$notation.find('a').on('click', function(e) {
if($(this).attr('data-deleting')) {
// ignore duplicate delete attempts
return false;
}
$(this).attr('data-deleting', true)
var $notationEntry = $(this).closest('.notation-entry').find('div').text('deleting...')
rest.deleteMusicNotation({id: notation.id})
.done(function() {
$notation.remove()
})
.fail(app.ajaxError)
return false;
})
$inputBox.append($notation);
}
function saveSettings(evt) { function saveSettings(evt) {
var data = {}; var data = {};
@ -111,16 +143,14 @@
data.fan_access = false; data.fan_access = false;
data.fan_chat = false; data.fan_chat = false;
} }
else if (fanAccess == 'listen-chat-each') { else if (fanAccess == 'listen-chat') {
data.fan_access = true;
data.fan_chat = false;
}
else if (fanAccess == 'listen-chat-band') {
data.fan_access = true; data.fan_access = true;
data.fan_chat = true; data.fan_chat = true;
} }
rest.updateSession($('#session-settings-id').val(), data).done(settingsSaved); rest.updateSession($('#session-settings-id').val(), data).done(settingsSaved);
return false;
} }
function uploadNotations(notations) { function uploadNotations(notations) {
@ -177,7 +207,7 @@
} }
}) })
.always(function() { .always(function() {
$btnSelectFiles.text('SELECT FILES...').data('uploading', null) $btnSelectFiles.text('ADD FILES...').data('uploading', null)
$uploadSpinner.hide(); $uploadSpinner.hide();
}); });
} }
@ -203,10 +233,9 @@
else { else {
// upload as soon as user picks their files. // upload as soon as user picks their files.
uploadNotations($inputFiles.get(0).files) uploadNotations($inputFiles.get(0).files)
.done(function() { .done(function(response) {
context._.each(fileNames, function(fileName) { context._.each(response, function(notation) {
var $text = $('<span>').text(fileName); addNotation(notation)
$selectedFilenames.append($text);
}) })
}) })
} }
@ -225,13 +254,13 @@
function settingsSaved(response) { function settingsSaved(response) {
// No response returned from this call. 204. // No response returned from this call. 204.
sessionScreen.refreshCurrentSession(true); context.SessionActions.syncWithServer()
app.layout.closeDialog('session-settings'); app.layout.closeDialog('session-settings');
} }
function events() { function events() {
$('#session-settings-dialog-submit').on('click', saveSettings); $('#session-settings-dialog-submit').on('click', saveSettings);
$('#session-settings-dialog').on('submit', saveSettings)
$inputFiles.on('change', changeSelectedFiles); $inputFiles.on('change', changeSelectedFiles);
$btnSelectFiles.on('click', toggleSelectFiles); $btnSelectFiles.on('click', toggleSelectFiles);
} }

View File

@ -15,7 +15,8 @@ context.JK.SoundCloudPlayerDialog = class SoundCloudPlayerDialog
initialize:(@url, @caption) => initialize:(@url, @caption) =>
dialogBindings = { dialogBindings = {
'beforeShow' : @beforeShow, 'beforeShow' : @beforeShow,
'afterShow' : @afterShow 'afterShow' : @afterShow,
'afterHide' : @afterHide
} }
@dialog = $('[layout-id="' + @dialogId + '"]') @dialog = $('[layout-id="' + @dialogId + '"]')
@ -27,6 +28,12 @@ context.JK.SoundCloudPlayerDialog = class SoundCloudPlayerDialog
beforeShow:() => beforeShow:() =>
@player.addClass("hidden") @player.addClass("hidden")
@player.attr("src", "") @player.attr("src", "")
# the Windows client does not play back correctly
if context.jamClient.IsNativeClient()
context.JK.popExternalLink(@url)
return false
else
u = encodeURIComponent(@url) u = encodeURIComponent(@url)
src = "https://w.soundcloud.com/player/?url=#{u}&auto_play=true&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true&loop=true" src = "https://w.soundcloud.com/player/?url=#{u}&auto_play=true&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true&loop=true"
@player.attr("src", src) @player.attr("src", src)
@ -37,4 +44,7 @@ context.JK.SoundCloudPlayerDialog = class SoundCloudPlayerDialog
showDialog:() => showDialog:() =>
@app.layout.showDialog(@dialogId) @app.layout.showDialog(@dialogId)
afterHide: () =>
@player.attr('src', '')

View File

@ -11,6 +11,7 @@
var $draggingFaderHandle = null; var $draggingFaderHandle = null;
var $draggingFader = null; var $draggingFader = null;
var $floater = null;
var draggingOrientation = null; var draggingOrientation = null;
var logger = g.JK.logger; var logger = g.JK.logger;
@ -20,6 +21,7 @@
e.stopPropagation(); e.stopPropagation();
var $fader = $(this); var $fader = $(this);
var floaterConvert = $fader.data('floaterConverter')
var sessionModel = window.JK.CurrentSessionModel || null; var sessionModel = window.JK.CurrentSessionModel || null;
var mediaControlsDisabled = $fader.data('media-controls-disabled'); var mediaControlsDisabled = $fader.data('media-controls-disabled');
@ -43,7 +45,7 @@
} }
} }
draggingOrientation = $fader.attr('orientation'); draggingOrientation = $fader.attr('data-orientation');
var offset = $fader.offset(); var offset = $fader.offset();
var position = { top: e.pageY - offset.top, left: e.pageX - offset.left} var position = { top: e.pageY - offset.top, left: e.pageX - offset.left}
@ -53,6 +55,10 @@
return false; return false;
} }
if(floaterConvert) {
window.JK.FaderHelpers.setFloaterValue($fader.find('.floater'), floaterConvert(faderPct))
}
$fader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: false}) $fader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: false})
setHandlePosition($fader, faderPct); setHandlePosition($fader, faderPct);
@ -61,9 +67,9 @@
function setHandlePosition($fader, value) { function setHandlePosition($fader, value) {
var ratio, position; var ratio, position;
var $handle = $fader.find('div[control="fader-handle"]'); var $handle = $fader.find('div[data-control="fader-handle"]');
var orientation = $fader.attr('orientation'); var orientation = $fader.attr('data-orientation');
var handleCssAttribute = getHandleCssAttribute($fader); var handleCssAttribute = getHandleCssAttribute($fader);
// required because this method is entered directly when from a callback // required because this method is entered directly when from a callback
@ -81,7 +87,7 @@
} }
function faderValue($fader, e, offset) { function faderValue($fader, e, offset) {
var orientation = $fader.attr('orientation'); var orientation = $fader.attr('data-orientation');
var getPercentFunction = getVerticalFaderPercent; var getPercentFunction = getVerticalFaderPercent;
var relativePosition = offset.top; var relativePosition = offset.top;
if (orientation && orientation == 'horizontal') { if (orientation && orientation == 'horizontal') {
@ -92,7 +98,7 @@
} }
function getHandleCssAttribute($fader) { function getHandleCssAttribute($fader) {
var orientation = $fader.attr('orientation'); var orientation = $fader.attr('data-orientation');
return (orientation === 'horizontal') ? 'left' : 'top'; return (orientation === 'horizontal') ? 'left' : 'top';
} }
@ -134,12 +140,34 @@
return false; return false;
} }
// simple snap feature to stick to the mid point
if(faderPct > 46 && faderPct < 54 && $draggingFader.data('snap')) {
faderPct = 50
var orientation = $draggingFader.attr('data-orientation');
if(orientation == 'horizontal') {
var width = $draggingFader.width()
var left = width / 2
ui.position.left = left
}
else {
var height = $draggingFader.height()
var top = height / 2
ui.position.top = top
}
}
var floaterConvert = $draggingFaderHandle.data('floaterConverter')
if(floaterConvert && $floater) {
window.JK.FaderHelpers.setFloaterValue($floater, floaterConvert(faderPct))
}
$draggingFader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: true}) $draggingFader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: true})
} }
function onFaderDragStart(e, ui) { function onFaderDragStart(e, ui) {
$draggingFaderHandle = $(this); $draggingFaderHandle = $(this);
$draggingFader = $draggingFaderHandle.closest('div[control="fader"]'); $draggingFader = $draggingFaderHandle.closest('div[data-control="fader"]');
$floater = $draggingFaderHandle.find('.floater')
draggingOrientation = $draggingFader.attr('orientation'); draggingOrientation = $draggingFader.attr('orientation');
var mediaControlsDisabled = $draggingFaderHandle.data('media-controls-disabled'); var mediaControlsDisabled = $draggingFaderHandle.data('media-controls-disabled');
@ -210,12 +238,12 @@
selector.html(g._.template(templateSource, options)); selector.html(g._.template(templateSource, options));
selector.find('div[control="fader"]') selector.find('div[data-control="fader"]')
.data('media-controls-disabled', selector.data('media-controls-disabled')) .data('media-controls-disabled', selector.data('media-controls-disabled'))
.data('media-track-opener', selector.data('media-track-opener')) .data('media-track-opener', selector.data('media-track-opener'))
.data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers')) .data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))
selector.find('div[control="fader-handle"]').draggable({ selector.find('div[data-control="fader-handle"]').draggable({
drag: onFaderDrag, drag: onFaderDrag,
start: onFaderDragStart, start: onFaderDragStart,
stop: onFaderDragStop, stop: onFaderDragStop,
@ -233,6 +261,43 @@
} }
}, },
renderFader2: function (selector, userOptions, floaterConverter) {
selector = $(selector);
if (userOptions === undefined) {
throw ("renderFader: userOptions is required");
}
var renderDefaults = {
faderType: "vertical"
};
var options = $.extend({}, renderDefaults, userOptions);
selector.find('div[data-control="fader"]')
.data('media-controls-disabled', selector.data('media-controls-disabled'))
.data('media-track-opener', selector.data('media-track-opener'))
.data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))
.data('floaterConverter', floaterConverter)
.data('snap', userOptions.snap)
selector.find('div[data-control="fader-handle"]').draggable({
drag: onFaderDrag,
start: onFaderDragStart,
stop: onFaderDragStop,
containment: "parent",
axis: options.faderType === 'horizontal' ? 'x' : 'y'
}).data('media-controls-disabled', selector.data('media-controls-disabled'))
.data('media-track-opener', selector.data('media-track-opener'))
.data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))
.data('floaterConverter', floaterConverter)
.data('snap', userOptions.snap)
// Embed any custom styles, applied to the .fader below selector
if ("style" in options) {
for (var key in options.style) {
selector.find(' .fader').css(key, options.style[key]);
}
}
},
convertLinearToDb: function (input) { convertLinearToDb: function (input) {
// deal with extremes better // deal with extremes better
@ -263,27 +328,48 @@
// composite function resembling audio taper // composite function resembling audio taper
if (input <= 1) { return -80; } if (input <= 1) { return -80; }
if (input <= 28) { return (2 * input - 80); } if (input <= 28) { return Math.round((2 * input - 80)); } // -78 to -24 db
if (input <= 79) { return (0.5 * input - 38); } if (input <= 79) { return Math.round((0.5 * input - 38)); } // -24 to 1.5 db
if (input < 99) { return (0.875 * input - 67.5); } if (input < 99) { return Math.round((0.875 * input - 67.5)); } // 1.625 - 19.125 db
if (input >= 99) { return 20; } if (input >= 99) { return 20; }
}, },
convertAudioTaperToPercent: function(db) {
if(db <= -78) { return 0}
if(db <= -24) { return (db + 80) / 2 }
if(db <= 1.5) { return (db + 38) / .5 }
if(db <= 19.125) { return (db + 67.5) / 0.875 }
return 100;
},
setFaderValue: function (faderId, faderValue) {
var $fader = $('[fader-id="' + faderId + '"]'); setFaderValue: function (faderId, faderValue, floaterValue) {
var $fader = $('[data-fader-id="' + faderId + '"]');
this.setHandlePosition($fader, faderValue); this.setHandlePosition($fader, faderValue);
if(floaterValue !== undefined) {
var $floater = $fader.find('.floater')
this.setFloaterValue($floater, floaterValue)
}
},
showFader: function(faderId) {
var $fader = $('[data-fader-id="' + faderId + '"]');
$fader.find('div[data-control="fader-handle"]').show()
}, },
setHandlePosition: function ($fader, faderValue) { setHandlePosition: function ($fader, faderValue) {
draggingOrientation = $fader.attr('orientation'); draggingOrientation = $fader.attr('data-orientation');
setHandlePosition($fader, faderValue); setHandlePosition($fader, faderValue);
draggingOrientation = null; draggingOrientation = null;
}, },
setFloaterValue: function($floater, floaterValue) {
$floater.text(floaterValue)
},
initialize: function () { initialize: function () {
$('body').on('click', 'div[control="fader"]', faderClick); $('body').on('click', 'div[data-control="fader"]', faderClick);
} }
}; };

View File

@ -4,7 +4,7 @@
context.JK = context.JK || {}; context.JK = context.JK || {};
context.JK.FakeJamClient = function(app, p2pMessageFactory) { context.JK.FakeJamClient = function(app, p2pMessageFactory) {
var ChannelGroupIds = context.JK.ChannelGroupIds; var ChannelGroupIds = context.JK.ChannelGroupIds
var logger = context.JK.logger; var logger = context.JK.logger;
logger.info("*** Fake JamClient instance initialized. ***"); logger.info("*** Fake JamClient instance initialized. ***");
@ -170,22 +170,22 @@
function FTUEGetMusicInputs() { function FTUEGetMusicInputs() {
dbg('FTUEGetMusicInputs'); dbg('FTUEGetMusicInputs');
return { return {
"i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1": "i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1":
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2" "Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
}; };
} }
function FTUEGetMusicOutputs() { function FTUEGetMusicOutputs() {
dbg('FTUEGetMusicOutputs'); dbg('FTUEGetMusicOutputs');
return { return {
"o~11~Multichannel (FW AP Multi)~0^o~11~Multichannel (FW AP Multi)~1": "o~11~Multichannel (FWAPMulti)~0^o~11~Multichannel (FWAPMulti)~1":
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2" "Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
}; };
} }
function FTUEGetChatInputs() { function FTUEGetChatInputs() {
dbg('FTUEGetChatInputs'); dbg('FTUEGetChatInputs');
return { return {
"i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1": "i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1":
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2" "Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
}; };
} }
function FTUEGetChannels() { function FTUEGetChannels() {
@ -450,7 +450,7 @@
} }
function GetASIODevices() { 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}]; 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":"FWAPMulti","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; return response;
} }
@ -475,8 +475,8 @@
} }
function SessionGetControlState(mixerIds, isMasterOrPersonal) { function SessionGetControlState(mixerIds, isMasterOrPersonal) {
dbg("SessionGetControlState"); dbg("SessionGetControlState");
var groups = [ var groups =
ChannelGroupIds.MasterGroup, [ChannelGroupIds.MasterGroup,
ChannelGroupIds.MonitorGroup, ChannelGroupIds.MonitorGroup,
ChannelGroupIds.AudioInputMusicGroup, ChannelGroupIds.AudioInputMusicGroup,
ChannelGroupIds.AudioInputChatGroup, ChannelGroupIds.AudioInputChatGroup,
@ -485,13 +485,12 @@
ChannelGroupIds.UserChatInputGroup, ChannelGroupIds.UserChatInputGroup,
ChannelGroupIds.PeerMediaTrackGroup, ChannelGroupIds.PeerMediaTrackGroup,
ChannelGroupIds.JamTrackGroup, ChannelGroupIds.JamTrackGroup,
ChannelGroupIds.MetronomeGroup ChannelGroupIds.MetronomeGroup];
]
var names = [ var names = [
"FW AP Multi", "FWAPMulti",
"FW AP Multi", "FWAPMulti",
"FW AP Multi", "FWAPMulti",
"FW AP Multi", "FWAPMulti",
"", "",
"", "",
"", "",
@ -545,6 +544,7 @@
stereo: true, stereo: true,
volume_left: -40, volume_left: -40,
volume_right:-40, volume_right:-40,
pan: 0,
instrument_id:50, // see globals.js instrument_id:50, // see globals.js
mode: isMasterOrPersonal, mode: isMasterOrPersonal,
rid: mixerIds[i] rid: mixerIds[i]
@ -554,10 +554,10 @@
} }
function SessionGetIDs() { function SessionGetIDs() {
return [ return [
"FW AP Multi_0_10000", "FWAPMulti_0_10000",
"FW AP Multi_1_10100", "FWAPMulti_1_10100",
"FW AP Multi_2_10200", "FWAPMulti_2_10200",
"FW AP Multi_3_10500", "FWAPMulti_3_10500",
"User@208.191.152.98#", "User@208.191.152.98#",
"User@208.191.152.98_*" "User@208.191.152.98_*"
]; ];
@ -624,9 +624,9 @@
function doCallbacks() { function doCallbacks() {
var names = ["vu"]; var names = ["vu"];
//var ids = ["FW AP Multi_2_10200", "FW AP Multi_0_10000"]; //var ids = ["FWAPMulti_2_10200", "FWAPMulti_0_10000"];
var ids= ["i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1", var ids= ["i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1",
"i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~2"]; "i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~2"];
var args = []; var args = [];
for (var i=0; i<ids.length; i++) { for (var i=0; i<ids.length; i++) {

View File

@ -93,8 +93,8 @@
} }
function onStartRecording(from, payload) { function onStartRecording(from, payload) {
logger.debug("received start recording request from " + from); logger.debug("received start recording request from " + from, context.SessionStore.isRecording);
if(context.JK.CurrentSessionModel.recordingModel.isRecording()) { if(context.SessionStore.isRecording) {
// reject the request to start the recording // reject the request to start the recording
context.JK.JamServer.sendP2PMessage(from, JSON.stringify(p2pMessageFactory.startRecordingAck(payload.recordingId, false, "already-recording", null))); context.JK.JamServer.sendP2PMessage(from, JSON.stringify(p2pMessageFactory.startRecordingAck(payload.recordingId, false, "already-recording", null)));
} }

View File

@ -25,6 +25,7 @@
var $includeType = null; var $includeType = null;
var didLoadAllFeeds = false, isLoading = false; var didLoadAllFeeds = false, isLoading = false;
var $templateRecordingDiscardedSoon = null; var $templateRecordingDiscardedSoon = null;
var defaults;
function defaultQuery() { function defaultQuery() {
var query = { limit: feedBatchSize }; var query = { limit: feedBatchSize };
@ -47,9 +48,9 @@
var currentQuery = defaultQuery(); var currentQuery = defaultQuery();
// specify search criteria based on form // specify search criteria based on form
currentQuery.sort = $sortFeedBy.val(); currentQuery.sort = $sortFeedBy.length == 0 ? defaults.sort : $sortFeedBy.val();
currentQuery.time_range = $includeDate.val(); currentQuery.time_range = $includeDate.length == 0 ? defaults.time_range : $includeDate.val();
currentQuery.type = $includeType.val(); currentQuery.type = $includeType.length == 0 ? defaults.type : $includeType.val();
return currentQuery; return currentQuery;
} }
@ -423,6 +424,11 @@
ui.addSessionLike(feed.id, JK.currentUserId, $('.likes', $feedItem), $('.btn-like', $feedItem)) ui.addSessionLike(feed.id, JK.currentUserId, $('.likes', $feedItem), $('.btn-like', $feedItem))
}); });
// should we show the select checkbox?
if(!defaults.show_checkbox) {
$feedItem.find('.select-box').hide();
}
// put the feed item on the page // put the feed item on the page
renderFeed($feedItem); renderFeed($feedItem);
@ -448,6 +454,7 @@
mix_class: feed['has_mix?'] ? 'has-mix' : 'no-mix', mix_class: feed['has_mix?'] ? 'has-mix' : 'no-mix',
} }
console.log("OPTIONS", options)
var $feedItem = $(context._.template($('#template-feed-recording').html(), options, {variable: 'data'})); var $feedItem = $(context._.template($('#template-feed-recording').html(), options, {variable: 'data'}));
var $controls = $feedItem.find('.recording-controls'); var $controls = $feedItem.find('.recording-controls');
@ -534,6 +541,14 @@
context.JK.helpBubble($feedItem.find('.help-launcher'), recordingUtils.onMixHover, {}, {width:'450px', closeWhenOthersOpen: true, positions:['top', 'left', 'bottom', 'right'], offsetParent: $screen.parent()}) context.JK.helpBubble($feedItem.find('.help-launcher'), recordingUtils.onMixHover, {}, {width:'450px', closeWhenOthersOpen: true, positions:['top', 'left', 'bottom', 'right'], offsetParent: $screen.parent()})
// should we show the select checkbox?
if(!defaults.show_checkbox) {
$feedItem.find('.select-box').hide();
}
if(defaults.hide_avatar) {
$feedItem.find('.avatar-small.ib').hide();
}
// put the feed item on the page // put the feed item on the page
renderFeed($feedItem); renderFeed($feedItem);
@ -582,7 +597,7 @@
} }
function initialize(_$parent, _$scroller, _$content, _$noMorefeeds, _$refresh, _$sortFeedBy, _$includeDate, _$includeType, defaults) { function initialize(_$parent, _$scroller, _$content, _$noMorefeeds, _$refresh, _$sortFeedBy, _$includeDate, _$includeType, _defaults) {
$screen = _$parent; $screen = _$parent;
$scroller = _$scroller; $scroller = _$scroller;
$content = _$content; $content = _$content;
@ -596,12 +611,10 @@
if($scroller.length == 0) throw "$scroller must be specified"; if($scroller.length == 0) throw "$scroller must be specified";
if($content.length == 0) throw "$content must be specified"; if($content.length == 0) throw "$content must be specified";
if($noMoreFeeds.length == 0) throw "$noMoreFeeds must be specified"; if($noMoreFeeds.length == 0) throw "$noMoreFeeds must be specified";
if($refresh.length == 0) throw "$refresh must be specified";
if($sortFeedBy.length == 0) throw "$sortFeedBy must be specified";
if($includeDate.length == 0) throw "$includeDate must be specified";
if($includeType.length ==0) throw "$includeType must be specified";
defaults = $.extend({}, {sort: 'date', time_range: 'month', type: 'all'}, defaults) // show_checkbox will show a checkbox in the upper left
defaults = $.extend({}, {sort: 'date', time_range: 'month', type: 'all', show_checkbox: false, hide_avatar: true}, _defaults)
// set default search criteria // set default search criteria
$sortFeedBy.val(defaults.date) $sortFeedBy.val(defaults.date)
$includeDate.val(defaults.time_range) $includeDate.val(defaults.time_range)

View File

@ -123,23 +123,23 @@
0: {"title": "", "message": ""}, // NO_EVENT, 0: {"title": "", "message": ""}, // NO_EVENT,
1: {"title": "", "message": ""}, // BACKEND_ERROR: generic error - eg P2P message error 1: {"title": "", "message": ""}, // BACKEND_ERROR: generic error - eg P2P message error
2: {"title": "", "message": ""}, // BACKEND_MIXER_CHANGE, - event that controls have been regenerated 2: {"title": "", "message": ""}, // BACKEND_MIXER_CHANGE, - event that controls have been regenerated
3: {"title": "High Packet Jitter", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // PACKET_JTR, 3: {"title": "High Packet Jitter", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // PACKET_JTR,
4: {"title": "High Packet Loss", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // PACKET_LOSS 4: {"title": "High Packet Loss", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // PACKET_LOSS
5: {"title": "High Packet Late", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // PACKET_LATE, 5: {"title": "High Packet Late", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // PACKET_LATE,
6: {"title": "Large Jitter Queue", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // JTR_QUEUE_DEPTH, 6: {"title": "Large Jitter Queue", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // JTR_QUEUE_DEPTH,
7: {"title": "High Network Jitter", "message": "Your network connection is currently experiencing network jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // NETWORK_JTR, 7: {"title": "High Network Jitter", "message": "Your network connection is currently experiencing network jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // NETWORK_JTR,
8: {"title": "High Session Latency", "message": "The latency of your audio device combined with your Internet connection has become high enough to impact your session quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // NETWORK_PING, 8: {"title": "High Session Latency", "message": "The latency of your audio device combined with your Internet connection has become high enough to impact your session quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // NETWORK_PING,
9: {"title": "Bandwidth Throttled", "message": "The available bandwidth on your network has diminished, and this may impact your audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // BITRATE_THROTTLE_WARN, 9: {"title": "Bandwidth Throttled", "message": "The available bandwidth on your network has diminished, and this may impact your audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // BITRATE_THROTTLE_WARN,
10:{"title": "Low Bandwidth", "message": "The available bandwidth on your network has become too low, and this may impact your audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // BANDWIDTH_LOW 10:{"title": "Low Bandwidth", "message": "The available bandwidth on your network has become too low, and this may impact your audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // BANDWIDTH_LOW
// IO related events // IO related events
11:{"title": "Variable Input Rate", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // INPUT_IO_RATE 11:{"title": "Variable Input Rate", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // INPUT_IO_RATE
12:{"title": "High Input Jitter", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // INPUT_IO_JTR, 12:{"title": "High Input Jitter", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // INPUT_IO_JTR,
13:{"title": "Variable Output Rate", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // OUTPUT_IO_RATE 13:{"title": "Variable Output Rate", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // OUTPUT_IO_RATE
14:{"title": "High Output Jitter", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // OUTPUT_IO_JTR, 14:{"title": "High Output Jitter", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // OUTPUT_IO_JTR,
// CPU load related // CPU load related
15: { "title": "CPU Utilization High", "message": "The CPU of your computer is unable to keep up with the current processing load, and this may impact your audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // CPU_LOAD 15: { "title": "CPU Utilization High", "message": "The CPU of your computer is unable to keep up with the current processing load, and this may impact your audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // CPU_LOAD
16: {"title": "Decode Violations", "message": ""}, // DECODE_VIOLATIONS, 16: {"title": "Decode Violations", "message": ""}, // DECODE_VIOLATIONS,
17: {"title": "", "message": ""}, // LAST_THRESHOLD 17: {"title": "", "message": ""}, // LAST_THRESHOLD
18: {"title": "Wifi Alert", "message": ""}, // WIFI_NETWORK_ALERT, //user or peer is using wifi 18: {"title": "Wifi Alert", "message": ""}, // WIFI_NETWORK_ALERT, //user or peer is using wifi
@ -162,10 +162,10 @@
33: {"title": "Client No Longer Pinned", "message": "This client is no longer designated as the source of the broadcast."}, // SESSION_LIVEBROADCAST_UNPINNED, //node unpinned by user 33: {"title": "Client No Longer Pinned", "message": "This client is no longer designated as the source of the broadcast."}, // SESSION_LIVEBROADCAST_UNPINNED, //node unpinned by user
34: {"title": "", "message": ""}, // BACKEND_STATUS_MSG, //status/informational message 34: {"title": "", "message": ""}, // BACKEND_STATUS_MSG, //status/informational message
35: {"title": "LAN Unpredictable", "message": "Your local network is adding considerable variance to transmit times. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // LOCAL_NETWORK_VARIANCE_HIGH,//the ping time via a hairpin for the user network is unnaturally high or variable. 35: {"title": "LAN Unpredictable", "message": "Your local network is adding considerable variance to transmit times. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // LOCAL_NETWORK_VARIANCE_HIGH,//the ping time via a hairpin for the user network is unnaturally high or variable.
//indicates problem with user computer stack or network itself (wifi, antivirus etc) //indicates problem with user computer stack or network itself (wifi, antivirus etc)
36: {"title": "LAN High Latency", "message": "Your local network is adding considerable latency. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // LOCAL_NETWORK_LATENCY_HIGH, 36: {"title": "LAN High Latency", "message": "Your local network is adding considerable latency. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // LOCAL_NETWORK_LATENCY_HIGH,
37: {"title": "", "message": ""}, // RECORDING_CLOSE, //update and remove tracks from front-end 37: {"title": "", "message": ""}, // RECORDING_CLOSE, //update and remove tracks from front-end
38: {"title": "No Audio Sent", "message": ""}, // PEER_REPORTS_NO_AUDIO_RECV, //update and remove tracks from front-end 38: {"title": "No Audio Sent", "message": ""}, // PEER_REPORTS_NO_AUDIO_RECV, //update and remove tracks from front-end
39: {"title": "", "message": ""}, // SHOW_PREFERENCES, //show preferences dialog 39: {"title": "", "message": ""}, // SHOW_PREFERENCES, //show preferences dialog
@ -314,19 +314,17 @@
MASTER_VS_PERSONAL_MIX : 'master_vs_personal_mix' MASTER_VS_PERSONAL_MIX : 'master_vs_personal_mix'
} }
// Recreate ChannelGroupIDs ENUM from C++ context.JK.ChannelGroupIds = {
context.JK.ChannelGroupIds =
{
"MasterGroup": 0, "MasterGroup": 0,
"MonitorGroup": 1, "MonitorGroup": 1,
"MasterCatGroup": 2, "MasterCatGroup" : 2,
"MonitorCatGroup": 3, "MonitorCatGroup" : 3,
"AudioInputMusicGroup": 4, "AudioInputMusicGroup": 4,
"AudioInputChatGroup": 5, "AudioInputChatGroup": 5,
"MediaTrackGroup": 6, "MediaTrackGroup": 6,
"StreamOutMusicGroup": 7, "StreamOutMusicGroup": 7,
"StreamOutChatGroup": 8, "StreamOutChatGroup": 8,
"StreamOutMediaGroup": 9, "StreamOutMediaGroup" : 9,
"UserMusicInputGroup": 10, "UserMusicInputGroup": 10,
"UserChatInputGroup": 11, "UserChatInputGroup": 11,
"UserMediaInputGroup": 12, "UserMediaInputGroup": 12,
@ -335,4 +333,34 @@
"JamTrackGroup": 15, "JamTrackGroup": 15,
"MetronomeGroup": 16 "MetronomeGroup": 16
}; };
})(window,jQuery);
context.JK.ChannelGroupLookup = {
0: "MasterGroup",
1: "MonitorGroup",
2: "MasterCatGroup",
3: "MonitorCatGroup",
4: "AudioInputMusicGroup",
5: "AudioInputChatGroup",
6: "MediaTrackGroup",
7: "StreamOutMusicGroup",
8: "StreamOutChatGroup",
9: "StreamOutMediaGroup",
10: "UserMusicInputGroup",
11: "UserChatInputGroup",
12: "UserMediaInputGroup",
13: "PeerAudioInputMusicGroup",
14: "PeerMediaTrackGroup",
15: "JamTrackGroup",
16: "MetronomeGroup"
}
context.JK.CategoryGroupIds = {
"AudioInputMusic" : "AudioInputMusic",
"AudioInputChat" : "AudioInputChat",
"UserMusic" : "UserMusic",
"UserChat" : "UserChat",
"UserMedia" : "UserMedia",
"MediaTrack" : "MediaTrack",
"Metronome" : "Metronome"
}
})(window,jQuery);

View File

@ -29,6 +29,7 @@
function bigHelpOptions(options) { function bigHelpOptions(options) {
return {positions: options.positions, offsetParent: options.offsetParent, return {positions: options.positions, offsetParent: options.offsetParent,
width:options.width,
spikeGirth: 15, spikeGirth: 15,
spikeLength: 20, spikeLength: 20,
fill: 'white', fill: 'white',
@ -68,13 +69,13 @@
helpBubble.jamtrackLandingPreview($preview, $preview.offsetParent()) helpBubble.jamtrackLandingPreview($preview, $preview.offsetParent())
setTimeout(function() { setTimeout(function() {
helpBubble.jamtrackLandingVideo($video, $video.offsetParent()) helpBubble.jamtrackLandingVideo($video, $video.closest('.row'))
setTimeout(function() { setTimeout(function() {
helpBubble.jamtrackLandingCta($ctaButton, $alternativeCta) helpBubble.jamtrackLandingCta($ctaButton, $alternativeCta)
}, 11000); // 5 seconds on top of 6 second show time of bubbles }, 11000); // 5 seconds on top of 6 second show time of bubbles
}, 11000); // 5 seconds on top of 6 second show time of bubbles }, 11000); // 5 seconds on top of 6 second show time of bubbles
}, 1500) }, 15000)
}) })
@ -101,18 +102,19 @@
} }
helpBubble.jamtrackLandingPreview = function($element, $offsetParent) { helpBubble.jamtrackLandingPreview = function($element, $offsetParent) {
context.JK.prodBubble($element, 'jamtrack-landing-preview', {}, bigHelpOptions({positions:['right'], offsetParent: $offsetParent})) console.log("SHOWING THE PREVIEW BUBBLE")
context.JK.prodBubble($element, 'jamtrack-landing-preview', {}, bigHelpOptions({positions:['right', 'top'], offsetParent: $offsetParent, width:250}))
} }
helpBubble.jamtrackLandingVideo = function($element, $offsetParent) { helpBubble.jamtrackLandingVideo = function($element, $offsetParent) {
context.JK.prodBubble($element, 'jamtrack-landing-video', {}, bigHelpOptions({positions:['left'], offsetParent: $offsetParent})) context.JK.prodBubble($element, 'jamtrack-landing-video', {}, bigHelpOptions({positions:['top', 'right'], offsetParent: $offsetParent}))
} }
helpBubble.jamtrackLandingCta = function($element, $alternativeElement) { helpBubble.jamtrackLandingCta = function($element, $alternativeElement) {
if ($element.visible()) { if (!$alternativeElement || $element.visible()) {
context.JK.prodBubble($element, 'jamtrack-landing-cta', {}, bigHelpOptions({positions:['left']})) context.JK.prodBubble($element, 'jamtrack-landing-cta', {}, bigHelpOptions({positions:['top', 'right'], width:240}))
} }
else { else if($alternativeElement) {
context.JK.prodBubble($alternativeElement, 'jamtrack-landing-cta', {}, bigHelpOptions({positions:['right']})) context.JK.prodBubble($alternativeElement, 'jamtrack-landing-cta', {}, bigHelpOptions({positions:['right']}))
} }
} }

View File

@ -10,6 +10,7 @@
var rest = new context.JK.Rest(); var rest = new context.JK.Rest();
var _instruments = []; // will be list of structs: [ {label:xxx, value:yyy}, {...}, ... ] var _instruments = []; // will be list of structs: [ {label:xxx, value:yyy}, {...}, ... ]
var _rsvp = false; var _rsvp = false;
var _noICheck = false;
if (typeof(_parentSelector)=="undefined") {_parentSelector=null} if (typeof(_parentSelector)=="undefined") {_parentSelector=null}
var _parentSelector = parentSelector; var _parentSelector = parentSelector;
var deferredInstruments = null; var deferredInstruments = null;
@ -109,13 +110,15 @@
return; return;
} }
$.each(instrumentList, function (index, value) { $.each(instrumentList, function (index, value) {
$('input[type="checkbox"][session-instrument-id="' + value.id + '"]') var $item = $('input[type="checkbox"][session-instrument-id="' + value.id + '"]')
.attr('checked', 'checked') .attr('checked', 'checked')
.iCheck({ if(!_noICheck) {
$item.iCheck({
checkboxClass: 'icheckbox_minimal', checkboxClass: 'icheckbox_minimal',
radioClass: 'iradio_minimal', radioClass: 'iradio_minimal',
inheritClass: true inheritClass: true
}); })
}
if (_rsvp) { if (_rsvp) {
$('select[session-instrument-id="' + value.id + '"].rsvp-count', _parentSelector).val(value.count); $('select[session-instrument-id="' + value.id + '"].rsvp-count', _parentSelector).val(value.count);
$('select[session-instrument-id="' + value.id + '"].rsvp-level', _parentSelector).val(value.level); $('select[session-instrument-id="' + value.id + '"].rsvp-level', _parentSelector).val(value.level);
@ -126,8 +129,9 @@
}); });
} }
function initialize(rsvp) { function initialize(rsvp, noICheck) {
_rsvp = rsvp; _rsvp = rsvp;
_noICheck = noICheck;
// XXX; _instruments should be populated in a template, rather than round-trip to server // XXX; _instruments should be populated in a template, rather than round-trip to server
if(!context.JK.InstrumentSelectorDeferred) { if(!context.JK.InstrumentSelectorDeferred) {
// this dance is to make sure there is only one server request instead of InstrumentSelector instances * // this dance is to make sure there is only one server request instead of InstrumentSelector instances *

View File

@ -95,6 +95,14 @@
}); });
} }
function deleteMusicNotation(options) {
return $.ajax({
type: "DELETE",
url: "/api/music_notations/" +options.id
});
}
function legacyJoinSession(options) { function legacyJoinSession(options) {
var sessionId = options["session_id"]; var sessionId = options["session_id"];
delete options["session_id"]; delete options["session_id"];
@ -478,6 +486,14 @@
}); });
} }
function deleteParticipant(clientId) {
var url = "/api/participants/" + clientId;
return $.ajax({
type: "DELETE",
url: url
});
}
function login(options) { function login(options) {
var url = '/api/auths/login'; var url = '/api/auths/login';
@ -507,17 +523,13 @@
function getUserProfile(options) { function getUserProfile(options) {
var id = getId(options); var id = getId(options);
var profile = null; return $.ajax({
if (id != null && typeof(id) != 'undefined') {
profile = $.ajax({
type: "GET", type: "GET",
dataType: "json", dataType: "json",
url: "/api/users/" + id + "/profile", url: "/api/users/" + id + "/profile",
processData: false processData: false
}); });
} }
return profile;
}
function createAffiliatePartner(options) { function createAffiliatePartner(options) {
return $.ajax({ return $.ajax({
@ -1827,6 +1839,7 @@
this.createScheduledSession = createScheduledSession; this.createScheduledSession = createScheduledSession;
this.uploadMusicNotations = uploadMusicNotations; this.uploadMusicNotations = uploadMusicNotations;
this.getMusicNotation = getMusicNotation; this.getMusicNotation = getMusicNotation;
this.deleteMusicNotation = deleteMusicNotation;
this.getBroadcastNotification = getBroadcastNotification; this.getBroadcastNotification = getBroadcastNotification;
this.quietBroadcastNotification = quietBroadcastNotification; this.quietBroadcastNotification = quietBroadcastNotification;
this.legacyJoinSession = legacyJoinSession; this.legacyJoinSession = legacyJoinSession;
@ -1882,6 +1895,7 @@
this.addRecordingLike = addRecordingLike; this.addRecordingLike = addRecordingLike;
this.addPlayablePlay = addPlayablePlay; this.addPlayablePlay = addPlayablePlay;
this.getSession = getSession; this.getSession = getSession;
this.deleteParticipant = deleteParticipant;
this.getClientDownloads = getClientDownloads; this.getClientDownloads = getClientDownloads;
this.createEmailInvitations = createEmailInvitations; this.createEmailInvitations = createEmailInvitations;
this.createMusicianInvite = createMusicianInvite; this.createMusicianInvite = createMusicianInvite;

View File

@ -308,7 +308,6 @@ context.JK.JamTrackScreen=class JamTrackScreen
rest.addJamtrackToShoppingCart(params).done((response) => rest.addJamtrackToShoppingCart(params).done((response) =>
if(isFree) if(isFree)
if context.JK.currentUserId? if context.JK.currentUserId?
alert("TODO")
context.JK.currentUserFreeJamTrack = true # make sure the user sees no more free notices context.JK.currentUserFreeJamTrack = true # make sure the user sees no more free notices
context.location = '/client#/redeemComplete' context.location = '/client#/redeemComplete'
else else

View File

@ -226,7 +226,7 @@
var errors = JSON.parse(jqXHR.responseText); var errors = JSON.parse(jqXHR.responseText);
var $errors = context.JK.format_all_errors(errors); var $errors = context.JK.format_all_errors(errors);
logger.debug("Unprocessable entity sent from server:", JSON.stringify(errors)) logger.debug("Unprocessable entity sent from server:", JSON.stringify(errors))
this.notify({title: title, text: $errors, icon_url: "/assets/content/icon_alert_big.png"}) this.notify({title: title || "Validation Error", text: $errors, icon_url: "/assets/content/icon_alert_big.png"})
} }
else if(jqXHR.status == 403) { else if(jqXHR.status == 403) {
logger.debug("permission error sent from server:", jqXHR.responseText) logger.debug("permission error sent from server:", jqXHR.responseText)

View File

@ -24,7 +24,7 @@
$.fn.metronomePlaybackMode = function(options) { $.fn.metronomePlaybackMode = function(options) {
options = options || {mode: 'self'} options = $.extend(false, {mode: 'self', positions: ['top']}, options);
return this.each(function(index) { return this.each(function(index) {
@ -78,8 +78,8 @@
spikeLength:0, spikeLength:0,
width:180, width:180,
closeWhenOthersOpen: true, closeWhenOthersOpen: true,
offsetParent: $parent.offsetParent(), offsetParent: options.offsetParent || $parent.offsetParent(),
positions:['top'], positions: options.positions,
preShow: function() { preShow: function() {
$parent.find('.down-arrow').removeClass('down-arrow').addClass('up-arrow') $parent.find('.down-arrow').removeClass('down-arrow').addClass('up-arrow')
}, },

View File

@ -0,0 +1,25 @@
//= require bugsnag
//= require bind-polyfill
//= require jquery
//= require jquery.monkeypatch
//= require jquery_ujs
//= require jquery.ui.draggable
//= require jquery.ui.droppable
//= require jquery.bt
//= require jquery.icheck
//= require jquery.easydropdown
//= require jquery.metronomePlaybackMode
//= require classnames
//= require reflux
//= require AAC_underscore
//= require AAA_Log
//= require globals
//= require jam_rest
//= require ga
//= require utils
//= require playbackControls
//= require webcam_viewer
//= require react
//= require react_ujs
//= require react-init
//= require react-components

View File

@ -14,6 +14,7 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
@isSearching = false @isSearching = false
@pageNumber = 1 @pageNumber = 1
@instrument_logo_map = context.JK.getInstrumentIconMap24() @instrument_logo_map = context.JK.getInstrumentIconMap24()
@instrumentSelector = null
init: (app) => init: (app) =>
@app = app @app = app
@ -23,14 +24,18 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
@screen = $('#musicians-screen') @screen = $('#musicians-screen')
@resultsListContainer = @screen.find('#musician-search-filter-results-list') @resultsListContainer = @screen.find('#musician-search-filter-results-list')
@spinner = @screen.find('.paginate-wait') @spinner = @screen.find('.paginate-wait')
@instrumentSelector = new context.JK.InstrumentSelector(JK.app)
@instrumentSelector.initialize(false, true)
this.registerResultsPagination() this.registerResultsPagination()
@screen.find('#btn-musician-search-builder').on 'click', => @screen.find('#btn-musician-search-builder').on 'click', =>
this.showBuilder() this.showBuilder()
false
@screen.find('#btn-musician-search-reset').on 'click', => @screen.find('#btn-musician-search-reset').on 'click', =>
this.resetFilter() this.resetFilter()
false
afterShow: () => afterShow: () =>
@screen.find('#musician-search-filter-results').show() @screen.find('#musician-search-filter-results').show()
@ -64,9 +69,11 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
@screen.find('#btn-perform-musician-search').on 'click', => @screen.find('#btn-perform-musician-search').on 'click', =>
this.performSearch() this.performSearch()
false
@screen.find('#btn-musician-search-cancel').on 'click', => @screen.find('#btn-musician-search-cancel').on 'click', =>
this.cancelFilter() this.cancelFilter()
false
this._populateSkill() this._populateSkill()
this._populateStudio() this._populateStudio()
@ -86,15 +93,16 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
blankOption = $ '<option value=""></option>' blankOption = $ '<option value=""></option>'
blankOption.text label blankOption.text label
blankOption.attr 'value', value blankOption.attr 'value', value
blankOption.attr 'selected', '' if value == selection
element.append(blankOption) element.append(blankOption)
element.val(selection)
context.JK.dropdown(element) context.JK.dropdown(element)
_populateSelectIdentifier: (identifier) => _populateSelectIdentifier: (identifier) =>
elem = $ '#musician-search-filter-builder select[name='+identifier+']' elem = $ '#musician-search-filter-builder select[name='+identifier+']'
struct = gon.musician_search_meta[identifier]['map'] struct = gon.musician_search_meta[identifier]['map']
keys = gon.musician_search_meta[identifier]['keys'] keys = gon.musician_search_meta[identifier]['keys']
this._populateSelectWithKeys(struct, @searchFilter[identifier], keys, elem) console.log("@searchFilter", @searchFilter, identifier)
this._populateSelectWithKeys(struct, @searchFilter.data_blob[identifier], keys, elem)
_populateSelectWithInt: (sourceStruct, selection, element) => _populateSelectWithInt: (sourceStruct, selection, element) =>
struct = struct =
@ -125,24 +133,23 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
ages_map = gon.musician_search_meta['ages']['map'] ages_map = gon.musician_search_meta['ages']['map']
$.each gon.musician_search_meta['ages']['keys'], (index, key) => $.each gon.musician_search_meta['ages']['keys'], (index, key) =>
ageTemplate = @screen.find('#template-search-filter-setup-ages').html() ageTemplate = @screen.find('#template-search-filter-setup-ages').html()
selected = ''
ageLabel = ages_map[key] ageLabel = ages_map[key]
if 0 < @searchFilter.data_blob.ages.length if 0 < @searchFilter.data_blob.ages.length
key_val = key.toString() key_val = key.toString()
ageMatch = $.grep(@searchFilter.data_blob.ages, (n, i) -> ageMatch = $.grep(@searchFilter.data_blob.ages, (n, i) ->
n == key_val) n == key_val)
selected = 'checked' if ageMatch.length > 0 selected = 'checked' if ageMatch.length > 0
ageHtml = context.JK.fillTemplate(ageTemplate, ageHtml = context._.template(ageTemplate,
id: key { id: key
description: ageLabel description: ageLabel
checked: selected) checked: selected},
{variable: 'data'})
@screen.find('#search-filter-ages').append ageHtml @screen.find('#search-filter-ages').append ageHtml
_populateGenres: () => _populateGenres: () =>
@screen.find('#search-filter-genres').empty() @screen.find('#search-filter-genres').empty()
@rest.getGenres().done (genres) => @rest.getGenres().done (genres) =>
genreTemplate = @screen.find('#template-search-filter-setup-genres').html() genreTemplate = @screen.find('#template-search-filter-setup-genres').html()
selected = ''
$.each genres, (index, genre) => $.each genres, (index, genre) =>
if 0 < @searchFilter.data_blob.genres.length if 0 < @searchFilter.data_blob.genres.length
genreMatch = $.grep(@searchFilter.data_blob.genres, (n, i) -> genreMatch = $.grep(@searchFilter.data_blob.genres, (n, i) ->
@ -150,35 +157,19 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
else else
genreMatch = [] genreMatch = []
selected = 'checked' if genreMatch.length > 0 selected = 'checked' if genreMatch.length > 0
genreHtml = context.JK.fillTemplate(genreTemplate, genreHtml = context._.template(genreTemplate,
id: genre.id { id: genre.id
description: genre.description description: genre.description
checked: selected) checked: selected },
{ variable: 'data' })
@screen.find('#search-filter-genres').append genreHtml @screen.find('#search-filter-genres').append genreHtml
_populateInstruments: () => _populateInstruments: () =>
@screen.find('#search-filter-instruments').empty()
@rest.getInstruments().done (instruments) => # TODO hydrate user's selection from json_store
$.each instruments, (index, instrument) => @instrumentSelector.render(@screen.find('.session-instrumentlist'), [])
instrumentTemplate = @screen.find('#template-search-filter-setup-instrument').html() @instrumentSelector.setSelectedInstruments(@searchFilter.data_blob.instruments)
selected = ''
proficiency = '1'
if 0 < @searchFilter.data_blob.instruments.length
instMatch = $.grep(@searchFilter.data_blob.instruments, (inst, i) ->
yn = inst.instrument_id == instrument.id
proficiency = inst.proficiency_level if yn
yn)
selected = 'checked' if instMatch.length > 0
instrumentHtml = context.JK.fillTemplate(instrumentTemplate,
id: instrument.id
description: instrument.description
checked: selected)
@screen.find('#search-filter-instruments').append instrumentHtml
profsel = '#search-filter-instruments tr[data-instrument-id="'+instrument.id+'"] select'
jprofsel = @screen.find(profsel)
jprofsel.val(proficiency)
context.JK.dropdown(jprofsel)
return true
_builderSelectValue: (identifier) => _builderSelectValue: (identifier) =>
elem = $ '#musician-search-filter-builder select[name='+identifier+']' elem = $ '#musician-search-filter-builder select[name='+identifier+']'
@ -188,12 +179,7 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
vals = [] vals = []
elem = $ '#search-filter-'+identifier+' input[type=checkbox]:checked' elem = $ '#search-filter-'+identifier+' input[type=checkbox]:checked'
if 'instruments' == identifier if 'instruments' == identifier
elem.each (idx) -> vals = @instrumentSelector.getSelectedInstruments()
row = $(this).parent().parent()
instrument =
instrument_id: row.data('instrument-id')
proficiency_level: row.find('select').val()
vals.push instrument
else else
elem.each (idx) -> elem.each (idx) ->
vals.push $(this).val() vals.push $(this).val()
@ -231,18 +217,20 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
performSearch: () => performSearch: () =>
if this.willSearch(true) if this.willSearch(true)
filter = {}
$.each gon.musician_search_meta.filter_keys.single, (index, key) => $.each gon.musician_search_meta.filter_keys.single, (index, key) =>
@searchFilter[key] = this._builderSelectValue(key) filter[key] = this._builderSelectValue(key)
$.each gon.musician_search_meta.filter_keys.multi, (index, key) => $.each gon.musician_search_meta.filter_keys.multi, (index, key) =>
@searchFilter[key] = this._builderSelectMultiValue(key) filter[key] = this._builderSelectMultiValue(key)
@rest.postMusicianSearchFilter({ filter: JSON.stringify(@searchFilter), page: @pageNumber }).done(this.didSearch) @rest.postMusicianSearchFilter({ filter: JSON.stringify(filter), page: @pageNumber }).done(this.didSearch)
renderResultsHeader: () => renderResultsHeader: () =>
@screen.find('#musician-search-filter-description').html(@searchResults.description)
if @searchResults.is_blank_filter if @searchResults.is_blank_filter
@screen.find('#btn-musician-search-reset').hide() @screen.find('#btn-musician-search-reset').hide()
@screen.find('.musician-search-text').text('Click search button to look for musicians with similar interests, skill levels, etc.')
else else
@screen.find('#btn-musician-search-reset').show() @screen.find('#btn-musician-search-reset').show()
@screen.find('.musician-search-text').text(@searchResults.summary)
renderMusicians: () => renderMusicians: () =>
this.renderResultsHeader() if @pageNumber == 1 this.renderResultsHeader() if @pageNumber == 1
@ -338,20 +326,25 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
@screen.find('.search-m-message').on 'click', (evt) -> @screen.find('.search-m-message').on 'click', (evt) ->
userId = $(this).parent().data('musician-id') userId = $(this).parent().data('musician-id')
objThis.app.layout.showDialog 'text-message', d1: userId objThis.app.layout.showDialog 'text-message', d1: userId
return false
_bindFriendMusician: () => _bindFriendMusician: () =>
objThis = this objThis = this
@screen.find('.search-m-friend').on 'click', (evt) -> @screen.find('.search-m-friend').on 'click', (evt) =>
# if the musician is already a friend, remove the button-orange class, and prevent the link from working # if the musician is already a friend, remove the button-orange class, and prevent the link from working
if 0 == $(this).closest('.button-orange').size() $self = $(evt.target)
if 0 == $self.closest('.button-orange').size()
return false return false
$(this).click (ee) -> logger.debug("evt.target", evt.target)
$self.click (ee) ->
ee.preventDefault() ee.preventDefault()
return return false
evt.stopPropagation() evt.stopPropagation()
uid = $(this).parent().data('musician-id') uid = $self.parent().data('musician-id')
objThis.rest.sendFriendRequest objThis.app, uid, this.friendRequestCallback objThis.rest.sendFriendRequest objThis.app, uid, this.friendRequestCallback
@app.notify({text: 'A friend request has been sent.'})
return false
_bindFollowMusician: () => _bindFollowMusician: () =>
objThis = this objThis = this
@ -361,7 +354,7 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
return false return false
$(this).click (ee) -> $(this).click (ee) ->
ee.preventDefault() ee.preventDefault()
return return false
evt.stopPropagation() evt.stopPropagation()
newFollowing = {} newFollowing = {}
newFollowing.user_id = $(this).parent().data('musician-id') newFollowing.user_id = $(this).parent().data('musician-id')
@ -377,8 +370,9 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
# remove the orange look to indicate it's not selectable # remove the orange look to indicate it's not selectable
# @FIXME -- this will need to be tweaked when we allow unfollowing # @FIXME -- this will need to be tweaked when we allow unfollowing
objThis.screen.find('div[data-musician-id=' + newFollowing.user_id + '] .search-m-follow').removeClass('button-orange').addClass 'button-grey' objThis.screen.find('div[data-musician-id=' + newFollowing.user_id + '] .search-m-follow').removeClass('button-orange').addClass 'button-grey'
return return false
error: objThis.app.ajaxError error: objThis.app.ajaxError
return false
_formatLocation: (musician) -> _formatLocation: (musician) ->
if musician.city and musician.state if musician.city and musician.state
@ -394,10 +388,11 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
# TODO: # TODO:
paginate: () => paginate: () =>
if @pageNumber < @searchResults.page_count && this.willSearch(false) if @pageNumber < @searchResults.page_count && this.willSearch(false)
@screen.find('.paginate-wait').show() @screen.find('.paginate-wait').show()
@pageNumber += 1 @pageNumber += 1
@rest.postMusicianSearchFilter({ filter: JSON.stringify(@searchFilter), page: @pageNumber }).done(this.didSearch) @rest.postMusicianSearchFilter({ filter: JSON.stringify(@searchFilter.data_blob), page: @pageNumber }).done(this.didSearch)
return true return true
false false

View File

@ -0,0 +1,37 @@
context = window
$ = jQuery
panHelper = class PanHelper
###
Convert the pan value that comes from a backend mixer
to a 0-100 % usable by a draggable panner element
###
convertPanToPercent: (mixerPan) ->
value = (((mixerPan + 90) / 90) * 100) / 2
if value < 0
0
else if value > 100
100
else
value
###
Convert the % value of a draggable panner element
to a mixer-ready pan value
###
convertPercentToPan: (percent) ->
value = 2 * percent / 100 * 90 - 90
if value < -90
-90
else if value > 90
90
else
Math.round(value)
convertPercentToPanForDisplay: (percent) ->
Math.abs(context.JK.PanHelpers.convertPercentToPan(percent))
context.JK.PanHelpers = new panHelper()

View File

@ -1,6 +1,7 @@
/** /**
* Playback widget (play, pause , etc) * Playback widget (play, pause , etc)
*/ */
(function(context, $) { (function(context, $) {
"use strict"; "use strict";
@ -18,7 +19,7 @@
context.JK = context.JK || {}; context.JK = context.JK || {};
context.JK.PlaybackControls = function($parentElement, options){ context.JK.PlaybackControls = function($parentElement, options){
options = $.extend(false, {playmodeControlsVisible:false}, options); options = $.extend(false, {playmodeControlsVisible:false, mediaActions:null}, options);
var logger = context.JK.logger; var logger = context.JK.logger;
if($parentElement.length == 0) { if($parentElement.length == 0) {
@ -68,24 +69,43 @@
if(endReached) { if(endReached) {
update(0, playbackDurationMs, playbackPlaying); update(0, playbackDurationMs, playbackPlaying);
} }
if(options.mediaActions) {
options.mediaActions.mediaStartPlay({playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode})
}
else {
$self.triggerHandler('play', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode}); $self.triggerHandler('play', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode});
}
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) { if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
var sessionModel = context.JK.CurrentSessionModel || null; context.JK.GA.trackJamTrackPlaySession(context.SessionStore.id(), true)
context.JK.GA.trackJamTrackPlaySession(sessionModel.id(), true)
} }
} }
function stopPlay(endReached) { function stopPlay(endReached) {
logger.debug("STOP PLAY CLICKED")
updateIsPlaying(false); updateIsPlaying(false);
if(options.mediaActions) {
logger.debug("mediaStopPlay", endReached)
options.mediaActions.mediaStopPlay({playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached})
}
else {
$self.triggerHandler('stop', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached}); $self.triggerHandler('stop', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached});
} }
}
function pausePlay(endReached) { function pausePlay(endReached) {
updateIsPlaying(false); updateIsPlaying(false);
if(options.mediaActions) {
options.mediaActions.mediaPausePlay({playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached})
}
else {
$self.triggerHandler('pause', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached}); $self.triggerHandler('pause', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached});
} }
}
function updateOffsetBasedOnPosition(offsetLeft) { function updateOffsetBasedOnPosition(offsetLeft) {
var sliderBarWidth = $sliderBar.width(); var sliderBarWidth = $sliderBar.width();
@ -93,7 +113,12 @@
playbackPositionMs = parseInt((offsetLeft / sliderBarWidth) * playbackDurationMs); playbackPositionMs = parseInt((offsetLeft / sliderBarWidth) * playbackDurationMs);
updateCurrentTimeText(playbackPositionMs); updateCurrentTimeText(playbackPositionMs);
if(canUpdateBackend) { if(canUpdateBackend) {
if(options.mediaActions) {
options.mediaActions.mediaChangePosition({positionMs: playbackPositionMs, playbackMonitorMode: playbackMonitorMode})
}
else {
$self.triggerHandler('change-position', {positionMs: playbackPositionMs, playbackMonitorMode: playbackMonitorMode}); $self.triggerHandler('change-position', {positionMs: playbackPositionMs, playbackMonitorMode: playbackMonitorMode});
}
canUpdateBackend = false; canUpdateBackend = false;
} }
} }
@ -127,34 +152,17 @@
} }
$playButton.on('click', function(e) { $playButton.on('click', function(e) {
var sessionModel = context.JK.CurrentSessionModel || null; console.log("CLICKED PLAY")
//if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $parentElement.closest('.session-track').data('track_data').type == 'jam_track') {
// context.JK.prodBubble($fader, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $playButton})
// return false;
//}
startPlay(); startPlay();
return false; return false;
}); });
$pauseButton.on('click', function(e) { $pauseButton.on('click', function(e) {
var sessionModel = context.JK.CurrentSessionModel || null;
//if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $parentElement.closest('.session-track').data('track_data').type == 'jam_track') {
// context.JK.prodBubble($pauseButton, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $pauseButton})
// return false;
//}
pausePlay(); pausePlay();
return false; return false;
}); });
$stopButton.on('click', function(e) { $stopButton.on('click', function(e) {
var sessionModel = context.JK.CurrentSessionModel || null;
//if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $parentElement.closest('.session-track').data('track_data').type == 'jam_track') {
// context.JK.prodBubble($pauseButton, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $pauseButton})
// return false;
//}
stopPlay(); stopPlay();
return false; return false;
}); });
@ -211,22 +219,8 @@
throw "unknown playbackMonitorMode: " + playbackMonitorMode; throw "unknown playbackMonitorMode: " + playbackMonitorMode;
} }
} }
function monitorRecordingPlayback() {
if(!monitoring) {
return;
}
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
var positionMs = context.jamClient.SessionCurrrentJamTrackPlayPosMs();
var duration = context.jamClient.SessionGetJamTracksPlayDurationMs();
var durationMs = duration.media_len;
var start = duration.start; // needed to understand start offset, and prevent slider from moving in tapins
}
else {
var positionMs = context.jamClient.SessionCurrrentPlayPosMs();
var durationMs = context.jamClient.SessionGetTracksPlayDurationMs();
}
var isPlaying = context.jamClient.isSessionTrackPlaying(); function executeMonitor(positionMs, durationMs, isPlaying) {
if(positionMs < 0) { if(positionMs < 0) {
// bug in backend? // bug in backend?
@ -237,7 +231,6 @@
seenActivity = true; seenActivity = true;
} }
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.METRONOME) { if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.METRONOME) {
updateIsPlaying(isPlaying); updateIsPlaying(isPlaying);
} }
@ -254,12 +247,35 @@
// this is so the jamtrack 'Get Ready!' stays hidden when it's not playing // this is so the jamtrack 'Get Ready!' stays hidden when it's not playing
$jamTrackGetReady.attr('data-current-time', -1) $jamTrackGetReady.attr('data-current-time', -1)
} }
} }
monitorPlaybackTimeout = setTimeout(monitorRecordingPlayback, 500); monitorPlaybackTimeout = setTimeout(monitorRecordingPlayback, 500);
} }
function monitorRecordingPlayback() {
if(!monitoring) {
return;
}
if(options.mediaActions) {
options.mediaActions.positionUpdate(playbackMonitorMode)
}
else {
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
var positionMs = context.jamClient.SessionCurrrentJamTrackPlayPosMs();
var duration = context.jamClient.SessionGetJamTracksPlayDurationMs();
var durationMs = duration.media_len;
}
else {
var positionMs = context.jamClient.SessionCurrrentPlayPosMs();
var durationMs = context.jamClient.SessionGetTracksPlayDurationMs();
}
var isPlaying = context.jamClient.isSessionTrackPlaying();
executeMonitor(positionMs, durationMs, isPlaying)
}
}
function update(currentTimeMs, durationTimeMs, isPlaying, offsetStart) { function update(currentTimeMs, durationTimeMs, isPlaying, offsetStart) {
if(dragging) { if(dragging) {
@ -304,7 +320,11 @@
} }
function updateCurrentTimeText(timeMs) { function updateCurrentTimeText(timeMs) {
$currentTime.text(context.JK.prettyPrintSeconds(parseInt(timeMs / 1000))); var time = context.JK.prettyPrintSeconds(parseInt(timeMs / 1000))
$currentTime.text(time);
if(options.mediaActions) {
options.mediaActions.currentTimeChanged(time)
}
} }
function updateSliderPosition(timeMs) { function updateSliderPosition(timeMs) {
@ -362,6 +382,12 @@
} }
function startMonitor(_playbackMonitorMode) { function startMonitor(_playbackMonitorMode) {
logger.debug("startMonitor: " + _playbackMonitorMode)
if(monitoring && _playbackMonitorMode == playbackMonitorMode) {
return;
}
monitoring = true; monitoring = true;
// resets everything to zero // resets everything to zero
init(); init();
@ -376,6 +402,11 @@
logger.debug("playbackControl.startMonitor " + playbackMonitorMode + "") logger.debug("playbackControl.startMonitor " + playbackMonitorMode + "")
styleControls(); styleControls();
if(monitorPlaybackTimeout != null) {
clearTimeout(monitorPlaybackTimeout);
monitorPlaybackTimeout = null;
}
monitorRecordingPlayback(); monitorRecordingPlayback();
} }
@ -407,6 +438,7 @@
this.setPlaybackMode = setPlaybackMode; this.setPlaybackMode = setPlaybackMode;
this.startMonitor = startMonitor; this.startMonitor = startMonitor;
this.stopMonitor = stopMonitor; this.stopMonitor = stopMonitor;
this.executeMonitor = executeMonitor;
this.onPlayStopEvent = onPlayStopEvent; this.onPlayStopEvent = onPlayStopEvent;
this.onPlayStartEvent = onPlayStartEvent; this.onPlayStartEvent = onPlayStartEvent;
this.onPlayPauseEvent = onPlayPauseEvent; this.onPlayPauseEvent = onPlayPauseEvent;

View File

@ -23,7 +23,7 @@
var $biography = $screen.find('#biography'); var $biography = $screen.find('#biography');
// musical experience // musical experience
var $instruments = $screen.find('#instruments'); var $instruments = $screen.find('.instruments-holder');
var $musicianStatus = $screen.find('#musician-status'); var $musicianStatus = $screen.find('#musician-status');
var $genres = $screen.find('#genres'); var $genres = $screen.find('#genres');
var $concertCount = $screen.find('#concert-count'); var $concertCount = $screen.find('#concert-count');
@ -36,7 +36,7 @@
var $youTubeSamples = $screen.find('.youtube-samples'); var $youTubeSamples = $screen.find('.youtube-samples');
// online presence // online presence
var $noOnlinePresence = $screen.find('.no-online-presence');
var $userWebsite = $screen.find('.user-website'); var $userWebsite = $screen.find('.user-website');
var $soundCloudPresence = $screen.find('.soundcloud-presence'); var $soundCloudPresence = $screen.find('.soundcloud-presence');
var $reverbNationPresence = $screen.find('.reverbnation-presence'); var $reverbNationPresence = $screen.find('.reverbnation-presence');
@ -95,7 +95,7 @@
var $age = $screen.find('#age'); var $age = $screen.find('#age');
// buttons // buttons
var $btnEdit = $screen.find('#btn-edit'); var $btnEdit = $screen.find('.edit-profile-btn');
var $btnAddFriend = $screen.find('#btn-add-friend'); var $btnAddFriend = $screen.find('#btn-add-friend');
var $btnFollowUser = $screen.find('#btn-follow-user'); var $btnFollowUser = $screen.find('#btn-follow-user');
var $btnMessageUser = $screen.find('#btn-message-user'); var $btnMessageUser = $screen.find('#btn-message-user');
@ -128,7 +128,7 @@
} }
function resetForm() { function resetForm() {
$instruments.empty(); //$instruments.empty();
$aboutContent.show(); $aboutContent.show();
$historyContent.hide(); $historyContent.hide();
@ -411,7 +411,7 @@
/****************** ABOUT TAB *****************/ /****************** ABOUT TAB *****************/
function renderAbout() { function renderAbout() {
$instruments.empty(); //$instruments.empty();
$aboutContent.show(); $aboutContent.show();
$historyContent.hide(); $historyContent.hide();
@ -477,7 +477,7 @@
function renderBio() { function renderBio() {
$biography.html(user.biography ? user.biography : NOT_SPECIFIED_TEXT); $biography.html(user.biography ? user.biography : NOT_SPECIFIED_TEXT);
if (isCurrentUser() && !user.biography) { if (isCurrentUser()) {
$btnEditBio.show(); $btnEditBio.show();
} else { } else {
$btnEditBio.hide(); $btnEditBio.hide();
@ -485,19 +485,26 @@
} }
function renderMusicalExperience() { function renderMusicalExperience() {
profileUtils.renderMusicalExperience(user, $screen) profileUtils.renderMusicalExperience(user, $screen, isCurrentUser())
} }
function renderPerformanceSamples() { function renderPerformanceSamples() {
profileUtils.renderPerformanceSamples(user, $screen) profileUtils.renderPerformanceSamples(user, $screen, isCurrentUser())
} }
function renderOnlinePresence() { function renderOnlinePresence() {
profileUtils.renderOnlinePresence(user, $screen) profileUtils.renderOnlinePresence(user, $screen, isCurrentUser())
} }
function renderInterests() { function renderInterests() {
// current interests // current interests
if (isCurrentUser()) {
$btnAddInterests.show();
}
else {
$btnAddInterests.hide();
}
var noInterests = !user.paid_sessions && !user.free_sessions && !user.cowriting && !user.virtual_band && !user.traditional_band; var noInterests = !user.paid_sessions && !user.free_sessions && !user.cowriting && !user.virtual_band && !user.traditional_band;
if (noInterests) { if (noInterests) {
$noInterests.show(); $noInterests.show();
@ -506,12 +513,7 @@
$cowritingSection.hide(); $cowritingSection.hide();
$traditionalBandSection.hide(); $traditionalBandSection.hide();
$virtualBandSection.hide(); $virtualBandSection.hide();
if (isCurrentUser()) {
$btnAddInterests.show();
}
} else { } else {
$btnAddInterests.hide();
$noInterests.hide(); $noInterests.hide();
// paid sessions // paid sessions

View File

@ -59,19 +59,17 @@
profileUtils.gigMap = { profileUtils.gigMap = {
"": "not specified", "": "not specified",
"0": "zero", "0": "under 10",
"1": "under 10", "1": "10 to 50",
"2": "10 to 50", "2": "50 to 100",
"3": "50 to 100", "3": "over 100"
"4": "over 100"
}; };
profileUtils.studioMap = { profileUtils.studioMap = {
"0": "zero", "0": "under 10",
"1": "under 10", "1": "10 to 50",
"2": "10 to 50", "2": "50 to 100",
"3": "50 to 100", "3": "over 100"
"4": "over 100"
}; };
profileUtils.cowritingPurposeMap = { profileUtils.cowritingPurposeMap = {
@ -99,6 +97,31 @@
return list; return list;
} }
// the server stores money in cents; display it as such
profileUtils.normalizeMoneyForDisplay = function(serverValue) {
if(serverValue || serverValue == 0) {
return (new Number(serverValue) / 100).toFixed(2)
}
else {
return 0;
}
}
// the server stores money in cents; normalize it from what user entered
profileUtils.normalizeMoneyForSubmit = function(clientValue) {
var money = new Number(clientValue);
if(!context._.isNaN(money)) {
money = Math.round(money * 100)
}
else {
// restore original value to allow server to reject with validation error
money = clientValue;
}
return money;
}
// Initialize standard profile help bubbles (topics stored as attributes on element): // Initialize standard profile help bubbles (topics stored as attributes on element):
profileUtils.initializeHelpBubbles = function(parentElement) { profileUtils.initializeHelpBubbles = function(parentElement) {
$(".help", parentElement).each(function( index ) { $(".help", parentElement).each(function( index ) {
@ -307,18 +330,25 @@
} }
function formatTitle(title) { function formatTitle(title) {
return title && title.length > 30 ? title.substring(0, 30) + "..." : title; return title;
} }
profileUtils.renderMusicalExperience = function(player, $root) { profileUtils.renderMusicalExperience = function(player, $root, isOwner) {
var $instruments = $root.find('#instruments'); var $instruments = $root.find('.instruments-holder');
var $musicianStatus = $root.find('#musician-status'); var $musicianStatus = $root.find('#musician-status');
var $genres = $root.find('#genres'); var $genres = $root.find('#genres');
var $concertCount = $root.find('#concert-count'); var $concertCount = $root.find('#concert-count');
var $studioCount = $root.find('#studio-count'); var $studioCount = $root.find('#studio-count');
var $btnAddExperiences = $root.find('.add-experiences')
$instruments.empty(); $instruments.find('.profile-instrument').remove()
if(isOwner) {
$btnAddExperiences.show()
}
else {
$btnAddExperiences.hide()
}
if (player.instruments) { if (player.instruments) {
for (var i = 0; i < player.instruments.length; i++) { for (var i = 0; i < player.instruments.length; i++) {
var instrument = player.instruments[i]; var instrument = player.instruments[i];
@ -335,7 +365,7 @@
proficiency_level_css: proficiencyCssMap[proficiency] proficiency_level_css: proficiencyCssMap[proficiency]
}); });
$instruments.append(instrumentHtml); $instruments.prepend(instrumentHtml);
} }
} }
@ -367,16 +397,22 @@
var $youTubeSamples = $root.find('.youtube-samples'); var $youTubeSamples = $root.find('.youtube-samples');
var $btnAddRecordings = $root.find('.add-recordings'); var $btnAddRecordings = $root.find('.add-recordings');
$jamkazamSamples.find('.playable').remove()
$soundCloudSamples.find('.playable').remove()
$youTubeSamples.find('.playable').remove()
if (isOwner) {
$btnAddRecordings.show();
}
else {
$btnAddRecordings.hide();
}
if (!performanceSamples || performanceSamples.length === 0) { if (!performanceSamples || performanceSamples.length === 0) {
$noSamples.show() $noSamples.show()
$jamkazamSamples.hide() $jamkazamSamples.hide()
$soundCloudSamples.hide() $soundCloudSamples.hide()
$youTubeSamples.hide() $youTubeSamples.hide()
if (isOwner) {
$btnAddRecordings.show();
}
} else { } else {
$btnAddRecordings.hide();
$noSamples.hide(); $noSamples.hide();
// show samples section // show samples section
@ -402,15 +438,15 @@
} }
$.each(jamkazamSamples, function(index, sample) { $.each(jamkazamSamples, function(index, sample) {
$jamkazamSamples.append("<a class='jamkazam-playable' href='/recordings/" + sample.claimed_recording.id + "' rel='external'>" + formatTitle(sample.claimed_recording.name) + "</a><br/>"); $jamkazamSamples.append("<a class='jamkazam-playable playable' href='/recordings/" + sample.claimed_recording.id + "' rel='external'>" + formatTitle(sample.claimed_recording.name) + "</a>");
}); });
$.each(soundCloudSamples, function(index, sample) { $.each(soundCloudSamples, function(index, sample) {
$soundCloudSamples.append("<a class='sound-cloud-playable' href='' soundcloud_url='" + sample.url + "'>" + formatTitle(sample.description) + "</a><br/>"); $soundCloudSamples.append("<a class='sound-cloud-playable playable' href='' soundcloud_url='" + sample.url + "'>" + formatTitle(sample.description) + "</a>");
}); });
$.each(youTubeSamples, function(index, sample) { $.each(youTubeSamples, function(index, sample) {
$youTubeSamples.append("<a class='youtube-playable' href='" + sample.url + "' rel='external'>" + formatTitle(sample.description) + "</a><br/>"); $youTubeSamples.append("<a class='youtube-playable playable' href='" + sample.url + "' rel='external'>" + formatTitle(sample.description) + "</a>");
}); });
} }
}// function renderPerformanceSamples }// function renderPerformanceSamples
@ -425,13 +461,17 @@
var $youTubePresence = $root.find('.youtube-presence'); var $youTubePresence = $root.find('.youtube-presence');
var $facebookPresence = $root.find('.facebook-presence'); var $facebookPresence = $root.find('.facebook-presence');
var $twitterPresence = $root.find('.twitter-presence'); var $twitterPresence = $root.find('.twitter-presence');
var $btnAddSites = $root.find('.add-sites'); var $btnAddSites = $root.find('.add-presences');
if (isOwner) {
$btnAddSites.show();
} else {
$btnAddSites.hide();
}
// online presences // online presences
var onlinePresences = player.online_presences; var onlinePresences = player.online_presences;
if ((!onlinePresences || onlinePresences.length === 0) && !player.website) { if (onlinePresences.length == 0 && !player.website) {
$noOnlinePresence.show() $noOnlinePresence.show()
$userWebsite.show() $userWebsite.show()
$soundCloudPresence.show() $soundCloudPresence.show()
@ -441,18 +481,19 @@
$youTubePresence.show() $youTubePresence.show()
$facebookPresence.show() $facebookPresence.show()
$twitterPresence.show() $twitterPresence.show()
if (isOwner) {
$btnAddSites.show();
} else { } else {
$btnAddSites.hide();
}
} else {
$btnAddSites.hide();
$noOnlinePresence.hide(); $noOnlinePresence.hide();
if (player.website) { if (player.website) {
$userWebsite.find('a').attr('href', player.website); // make sure website is rooted
var website = player.website;
if(website.indexOf('http') == -1) {
website = 'http://' + website;
}
$userWebsite.removeClass('hidden').find('a').attr('href', website)
}
else {
$userWebsite.addClass('hidden').find('a').attr('href', '')
} }
var soundCloudPresences = profileUtils.soundCloudPresences(onlinePresences); var soundCloudPresences = profileUtils.soundCloudPresences(onlinePresences);

View File

@ -1,3 +1,15 @@
//= require ./react-components/actions/BroadcastActions //= require_directory ./react-components/helpers
//= require ./react-components/stores/BroadcastStore //= require_directory ./react-components/actions
//= require ./react-components/stores/AppStore
//= require ./react-components/stores/RecordingStore
//= require ./react-components/stores/SessionStore
//= require ./react-components/stores/MixerStore
//= require ./react-components/stores/SessionNotificationStore
//= require ./react-components/stores/MediaPlaybackStore
//= require ./react-components/stores/SessionMyTracksStore
//= require ./react-components/stores/SessionOtherTracksStore
//= require ./react-components/stores/SessionMediaTracksStore
//= require_directory ./react-components/stores
//= require_directory ./react-components/mixins
//= require_directory ./react-components //= require_directory ./react-components
//= require_directory ./react-components/landing

View File

@ -0,0 +1,204 @@
context = window
PLAYBACK_MONITOR_MODE = context.JK.PLAYBACK_MONITOR_MODE
EVENTS = context.JK.EVENTS
logger = context.JK.logger
mixins = []
# this check ensures we attempt to listen if this component is created in a popup
reactContext = if window.opener? then window.opener else window
MixerStore = reactContext.MixerStore
MixerActions = reactContext.MixerActions
MediaPlaybackStore = reactContext.MediaPlaybackStore
SessionActions = reactContext.SessionActions
MediaPlaybackActions = reactContext.MediaPlaybackActions
mixins.push(Reflux.listenTo(MixerStore,"onInputsChanged"))
mixins.push(Reflux.listenTo(MediaPlaybackStore, 'onMediaStateChanged'))
@MediaControls = React.createClass({
mixins: mixins
tempos : [ 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 63, 66, 69, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 126, 132, 138, 144, 152, 160, 168, 176, 184, 192, 200, 208 ]
onMediaStateChanged: (changes) ->
if changes.playbackStateChanged
if @state.controls?
if changes.playbackState == 'play_start'
@state.controls.onPlayStartEvent()
else if changes.playbackState == 'play_stop'
@state.controls.onPlayStopEvent()
else if changes.playbackState == 'play_pause'
@state.controls.onPlayPauseEvent();
else if changes.positionUpdateChanged
if @state.controls?
@state.controls.executeMonitor(changes.positionMs, changes.durationMs, changes.isPlaying)
onInputsChanged: (sessionMixers) ->
session = sessionMixers.session
mixers = sessionMixers.mixers
if @state.controls?
mediaSummary = mixers.mediaSummary
metro = mixers.metro
@monitorControls(@state.controls, mediaSummary)
@setState({mediaSummary: mediaSummary, metro: metro})
@updateMetronomeDetails(metro, @state.initializedMetronomeControls)
updateMetronomeDetails: (metro, initializedMetronomeControls) ->
logger.debug("MediaControls: setting tempo/sound/cricket", metro)
$root = jQuery(this.getDOMNode())
$root.find("select.metro-tempo").val(metro.tempo)
$root.find("select.metro-sound").val(metro.sound)
if initializedMetronomeControls
mode = if metro.cricket then 'cricket' else 'self'
logger.debug("settingcricket", mode)
$root.find('#metronome-playback-select').metronomeSetPlaybackMode(mode)
monitorControls: (controls, mediaSummary) ->
if mediaSummary.mediaOpen
if mediaSummary.jamTrackOpen
controls.startMonitor(PLAYBACK_MONITOR_MODE.JAMTRACK)
else if mediaSummary.backingTrackOpen
controls.startMonitor(PLAYBACK_MONITOR_MODE.MEDIA_FILE)
else if mediaSummary.metronomeOpen
controls.startMonitor(PLAYBACK_MONITOR_MODE.METRONOME)
else if mediaSummary.recordingOpen
controls.startMonitor(PLAYBACK_MONITOR_MODE.MEDIA_FILE)
else
logger.debug("unable to determine mediaOpen type", mediaSummary)
controls.startMonitor(PLAYBACK_MONITOR_MODE.MEDIA_FILE)
else
controls.stopMonitor()
metronomePlaybackModeChanged: (e, data) ->
mode = data.playbackMode # will be either 'self' or 'cricket'
logger.debug("setting metronome playback mode: ", mode)
isCricket = mode == 'cricket';
SessionActions.metronomeCricketChange(isCricket)
onMetronomeChanged: () ->
@setMetronomeFromForm()
setMetronomeFromForm: () ->
$root = jQuery(this.getDOMNode())
tempo = $root.find("select.metro-tempo:visible option:selected").val()
sound = $root.find("select.metro-sound:visible option:selected").val()
t = parseInt(tempo)
s = null
if tempo == NaN || tempo == 0 || tempo == null
t = 120
if sound == null || typeof(sound)=='undefined' || sound == ""
s = "Beep"
else
s = sound
logger.debug("Setting tempo and sound:", t, s)
MixerActions.metronomeChanged(t, s, 1, 0)
render: () ->
tempo_options = []
for tempo in @tempos
tempo_options.push(`<option value={tempo}>{tempo}</option>`)
`<div className="media-controls has-mix">
<div className="jam-track-get-ready">
<div className="spinner-small"></div>
<span>Get Ready!</span>
</div>
<div className="play-buttons">
<a className="play-button" href="#">
<img src="/assets/content/icon_playbutton.png" width="20" height="20" className="playbutton" />
<img src="/assets/content/icon_pausebutton.png" width="20" height="20" className="pausebutton" />
</a>
<a className="stop-button" href="#">
<img src="/assets/content/icon_stopbutton.png" width="20" height="20" className="stopbutton" />
</a>
</div>
<div className="metronome-playback-options">
<span id="metronome-playback-select"></span>
</div>
<div className="metronome-options">
<div className="metronome-selects">
<div class="metronome-field">
<select className="metronome-select metro-sound" title="Metronome Sound" name="metronome_sound">
<option value="Beep">Knock</option>
<option value="Click">Tap</option>
<option value="Snare">Snare</option>
<option value="Kick">Kick</option>
</select>
<label htmlFor="metronome_sound">Sound:</label>
</div>
<div class="metronome-field">
<select className="metronome-select metro-tempo" title="Metronome Tempo" name="metronome_tempo">
{tempo_options}
</select>
<label htmlFor="metronome_tempo">Tempo:</label>
</div>
</div>
</div>
<div className="recording-time start-time">0:00</div>
<div className="recording-playback">
<div className="recording-slider"><img src="/assets/content/slider_playcontrols.png" height="16" width="5" /></div>
</div>
<div className="recording-time duration-time">0:00</div>
<div className="recording-current">0:00</div>
<div className="playback-mode-buttons icheckbuttons">
<input type="radio" name="playback-mode" defaultChecked="checked" value="preview-to-all" className="preview-to-all" /><label htmlFor="playback-mode-preview-all" className="radio-text">Preview to All</label>
<input type="radio" name="playback-mode" value="preview-to-me" className="preview-to-me" /><label htmlFor="playback-mode-preview-me" className="radio-text">Preview Only to Me</label>
</div>
</div>`
getInitialState: () ->
{controls: null, mediaSummary: {}, initializedMetronomeControls: false}
tryPrepareMetronome: (metro) ->
if @state.mediaSummary.metronomeOpen && !@state.initializedMetronomeControls
$root = jQuery(this.getDOMNode())
$root.on("change", ".metronome-select", @onMetronomeChanged)
$root.find('#metronome-playback-select').metronomePlaybackMode({positions:['bottom'], offsetParent:$('#minimal-container')}).on(EVENTS.METRONOME_PLAYBACK_MODE_SELECTED, @metronomePlaybackModeChanged)
@updateMetronomeDetails(metro, true)
@setState({initializedMetronomeControls: true})
componentDidUpdate: (prevProps, prevState) ->
@tryPrepareMetronome(@state.metro)
componentDidMount: () ->
$root = jQuery(this.getDOMNode())
controls = context.JK.PlaybackControls($root, {mediaActions: MediaPlaybackActions})
mediaSummary = MixerStore.mixers.mediaSummary
metro = MixerStore.mixers.metro
@monitorControls(controls, mediaSummary)
@tryPrepareMetronome(metro)
@setState({mediaSummary: mediaSummary, controls: controls, metro: metro})
})

View File

@ -0,0 +1,126 @@
context = window
logger = context.JK.logger
mixins = []
if window.opener?
SessionActions = window.opener.SessionActions
MediaPlaybackStore = window.opener.MediaPlaybackStore
MixerActions = window.opener.MixerActions
mixins.push(Reflux.listenTo(MediaPlaybackStore, 'onMediaStateChanged'))
@PopupMediaControls = React.createClass({
mixins: mixins
onMediaStateChanged: (changes) ->
if changes.currentTimeChanged && @root?
@setState({time: changes.time})
showMetronome: (e) ->
e.preventDefault()
SessionActions.showNativeMetronomeGui()
getInitialState: () ->
{time: '0:00'}
close: () ->
window.close()
render: () ->
closeLinkText = null
header = null
extraControls = null
# give the users options to close it
if @props.mediaSummary.jamTrackOpen
mediaType = "JamTrack"
mediaName = @props.jamTracks[0].name
closeLinkText = 'close JamTrack'
header = `<h3>{mediaType}: {mediaName} ({this.state.time})</h3>`
else if @props.mediaSummary.backingTrackOpen
mediaType = "Audio File"
mediaName = context.JK.getNameOfFile(@props.backingTracks[0].shortFilename)
closeLinkText = 'close audio file'
header = `<h3>{mediaType}: {mediaName} ({this.state.time})</h3>`
extraControls =
`<div>
<div className="field">
<input type="checkbox" name="loop" /><label htmlFor="loop">Loop audio file playback</label>
</div>
<br className="clearall"/>
</div>`
else if @props.mediaSummary.metronomeOpen
mediaType = "Metronome"
closeLinkText = 'close metronome'
header = `<h3>Metronome</h3>`
extraControls =
`<div>
<a className="display-metronome" onClick={this.showMetronome}>Display visual metronome</a>
</div>`
else if @props.mediaSummary.recordingOpen
mediaType = "Recording"
mediaName = @props.recordedTracks[0].recordingName
closeLinkText = 'close recording'
header = `<h3>{mediaType}: {mediaName} ({this.state.time})</h3>`
else
mediaType = ""
`<div className="media-controls-popup">
{header}
<MediaControls />
{extraControls}
<a className="close-link" onClick={this.close}>{closeLinkText}</a>
</div>`
windowUnloaded: () ->
SessionActions.closeMedia() unless window.DontAutoCloseMedia
componentDidMount: () ->
$(window).unload(@windowUnloaded)
@root = jQuery(this.getDOMNode())
$loop = @root.find('input[name="loop"]')
context.JK.checkbox($loop)
$loop.on('ifChecked', () =>
logger.debug("@props", @props)
# it doesn't matter if you do personal or master, because backend just syncs both
MixerActions.loopChanged(@props.backingTracks[0].mixers.personal.mixer, true)
)
$loop.on('ifUnchecked', () =>
# it doesn't matter if you do personal or master, because backend just syncs both
MixerActions.loopChanged(@props.backingTracks[0].mixers.personal.mixer, false)
)
@resizeWindow()
# this is necessary due to whatever the client's rendering behavior is.
setTimeout(@resizeWindow, 300)
componentDidUpdate: () ->
@resizeWindow()
resizeWindow: () =>
$container = $('#minimal-container')
width = $container.width()
height = $container.height()
# there is 20px or so of unused space at the top of the page. can't figure out why it's there. (above #minimal-container)
#mysteryTopMargin = 20
mysteryTopMargin = 0
# deal with chrome in real browsers
offset = (window.outerHeight - window.innerHeight) + mysteryTopMargin
# handle native client chrome that the above outer-inner doesn't catch
#if navigator.userAgent.indexOf('JamKazam') > -1
#offset += 25
window.resizeTo(width, height + offset)
})

View File

@ -0,0 +1,135 @@
context = window
mixins = []
# this check ensures we attempt to listen if this component is created in a popup
if window.opener
mixins.push(Reflux.listenTo(window.opener.RecordingStore,"onRecordingStateChanged"))
@PopupRecordingStartStop = React.createClass({
mixins: mixins
onRecordingStateChanged: (recordingState) ->
this.setState(isRecording: recordingState.isRecording, recordedOnce: this.state.recordedOnce || recordingState.isRecording)
startStopRecording: () ->
if this.state.isRecording
window.opener.RecordingActions.stopRecording()
else
window.opener.RecordingActions.startRecording()
onNoteShowHide: () ->
this.setState(showNote: !this.state.showNote)
getInitialState: () ->
{isRecording: window.ParentIsRecording, showNote: true, recordedOnce: false}
render: () ->
recordingVerb = if this.state.isRecording then 'Stop' else 'Start'
recordingBtnClasses = classNames({
"currently-recording" : this.state.isRecording,
"control" : true
})
noteJSX = `<div className="important-note">
<h5>
Important Note
</h5>
<div className="contents">
While playing in your session, you are listening to your own personal mix. This recording will use the master mix,
which may sound very different. To hear and adjust your master mix settings, click the MIXER button in the session toolbar.
</div>
</div>`
recordingJSX = `<div className="recording-options">
<div className="field">
<input type="radio" name="recording-input-type" id="recording-input-both" defaultChecked="checked" />
<label htmlFor="recording-input-both">Record both video and audio</label>
<div className="clearall"></div>
</div>
<div className="field">
<input type="radio" name="recording-input-type" id="recording-input-audio" />
<label htmlFor="recording-input-audio">Record audio only</label>
<div className="clearall"></div>
</div>
</div>`
if this.state.showNote
noteText = 'hide note'
else
noteText = 'show note'
noteShowHideJSX = `<a href="#" className="note-show-hide" onClick={this.onNoteShowHide}>{noteText}</a>`
note = null
recordingOptions = null
noteShowHide = null
if this.state.showNote && !this.state.isRecording && !this.state.recordedOnce
# should we show the note itself? Only if not recording, too
note = noteJSX
if !this.state.isRecording && !this.state.recordedOnce
noteShowHide = noteShowHideJSX
if gon.global.video_available == "full"
recordingOptions = recordingJSX
`<div className="recording-start-stop">
<div className="control-holder">
<a className={recordingBtnClasses} onClick={this.startStopRecording}>
<span className="helper" />
<img src="/assets/content/recordbutton-off.png" width="20" height="20" />
<span id="recording-status">{recordingVerb} Recording</span>
</a>
</div>
{recordingOptions}
{note}
{noteShowHide}
</div>`
windowUnloaded: () ->
window.opener.RecordingActions.recordingControlsClosed()
componentDidMount: () ->
$(window).unload(@windowUnloaded)
$root = jQuery(this.getDOMNode())
$recordingType = $root.find('input[type="radio"]')
context.JK.checkbox($recordingType)
@resizeWindow()
# this is necessary due to whatever the client's rendering behavior is.
setTimeout(@resizeWindow, 300)
componentDidUpdate: () ->
@resizeWindow()
resizeWindow: () =>
$container = $('#minimal-container')
width = $container.width()
height = $container.height()
# there is 20px or so of unused space at the top of the page. can't figure out why it's there. (above #minimal-container)
mysteryTopMargin = 20
# deal with chrome in real browsers
offset = (window.outerHeight - window.innerHeight) + mysteryTopMargin
# handle native client chrome that the above outer-inner doesn't catch
#if navigator.userAgent.indexOf('JamKazam') > -1
#offset += 25
window.resizeTo(width, height + offset)
})

View File

@ -0,0 +1,13 @@
context = window
logger = context.JK.logger
@PopupWrapper = React.createClass({
getInitialState: () ->
{ready: false}
render: () ->
logger.debug("PopupProps", window.PopupProps)
return React.createElement(window[this.props.component], window.PopupProps)
})

View File

@ -0,0 +1,89 @@
context = window
MixerActions = @MixerActions
@SessionBackingTrack = React.createClass({
mixins: [@MasterPersonalMixersMixin]
propTypes: {
mode: React.PropTypes.bool.isRequired
}
handleMute: (e) ->
e.preventDefault()
mixer = @mixer()
unless mixer?
logger.debug("ignoring mute because no media mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([mixer], muting)
render: () ->
mixers = @mixers()
muteMixer = mixers.mixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
componentClasses = classNames({
"session-track" : true
"backing-track" : true
})
pan = if mixers.mixer? then mixers.mixer?.pan else 0
panStyle = {
transform: "rotate(#{pan}deg)"
WebkitTransform: "rotate(#{pan}deg)"
}
`<div className={componentClasses}>
<div className="session-track-contents">
<div className="name">{this.props.shortFilename}</div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={mixers} />
<div className="track-buttons">
<div className="track-icon-pan" style={panStyle}/>
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
</div>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@mixers()}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:@mixers()}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -0,0 +1,75 @@
context = window
@SessionChatMixer= React.createClass({
handleMute: (e) ->
e.preventDefault()
unless @props.mixers.mixer
logger.debug("ignoring mute; no mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([@props.mixers.mixer], muting)
render: () ->
muteMixer = @props.mixers.muteMixer
vuMixer = @props.mixers.vuMixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
pan = if @props.mixers.mixer? then @props.mixers.mixer.pan else 0
panStyle = {
transform: "rotate(#{pan}deg)"
WebkitTransform: "rotate(#{pan}deg)"
}
`<div className="session-track chat-mixer">
<div className="disabled-track-overlay" />
<div className="session-track-contents">
<div className="name">Session Voice Chat Output</div>
<div className="track-instrument"><img height="45" src="/assets/content/icon_instrument_voice45.png" width="45" /></div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={this.props.mixers} />
<div className="track-buttons">
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
<div className="track-icon-pan" style={panStyle}/>
</div>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@props.mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:@props.mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -0,0 +1,26 @@
context = window
@SessionInviteMusiciansBtn = React.createClass({
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
onAppInit: (app) ->
@app = app
@inviteMusiciansUtil = new JK.InviteMusiciansUtil(@app)
@inviteMusiciansUtil.initialize(JK.FriendSelectorDialogInstance)
openInviteDialog : (e) ->
e.preventDefault()
friendInput = @inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians', context.SessionStore.currentSessionId)
@inviteMusiciansUtil.loadFriends()
$(friendInput).show()
@app.layout.showDialog('select-invites')
render: () ->
`<a className="session-invite-musicians" onClick={this.openInviteDialog}>
<img src="/assets/content/icon_add.png" width="19" height="19" />
Invite Musicians
</a>`
})

View File

@ -0,0 +1,89 @@
context = window
MixerActions = @MixerActions
@SessionJamTrack = React.createClass({
mixins: [@MasterPersonalMixersMixin]
propTypes: {
mode: React.PropTypes.bool.isRequired
}
handleMute: (e) ->
e.preventDefault()
mixer = @mixer()
unless mixer?
logger.debug("ignoring mute because no media mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([mixer], muting)
render: () ->
mixers = @mixers()
muteMixer = mixers.mixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
componentClasses = classNames({
"session-track" : true
"jam-track" : true
})
pan = if mixers.mixer? then mixers.mixer?.pan else 0
panStyle = {
transform: "rotate(#{pan}deg)"
WebkitTransform: "rotate(#{pan}deg)"
}
`<div className={componentClasses}>
<div className="session-track-contents">
<div className="name">{this.props.part}</div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={mixers} />
<div className="track-buttons">
<div className="track-instrument"><img height="24" src={this.props.instrumentIcon} width="24" /></div>
<div className="track-icon-pan" style={panStyle}/>
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
</div>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@mixers()}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:@mixers()}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -0,0 +1,81 @@
context = window
MixerActions = @MixerActions
@SessionJamTrackCategory = React.createClass({
handleMute: (e) ->
e.preventDefault()
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([this.props.mixers.mixer], muting)
render: () ->
# today, all mixers are the same for a remote participant; so just grab the 1st
mixers = @props.mixers
muteMixer = mixers.muteMixer
vuMixer = mixers.vuMixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
componentClasses = classNames({
"session-track" : true
"jam-track-category" : true
})
pan = mixers.mixer.pan
panStyle = {
transform: "rotate(#{pan}deg)"
WebkitTransform: "rotate(#{pan}deg)"
}
`<div className={componentClasses}>
<div className="session-track-contents">
<div className="jam-track-header">JamTrack:</div>
<div className="name">{this.props.jamTrackName}</div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={mixers} />
<div className="track-buttons">
<div className="track-icon-pan" style={panStyle}/>
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
</div>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@props.mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:@props.mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -0,0 +1,23 @@
context = window
@SessionLeaveBtn = React.createClass({
onLeave: (e) ->
e.preventDefault()
@rateSession()
SessionActions.leaveSession.trigger({location: '/client#/home'})
rateSession: () ->
unless @rateSessionDialog?
@rateSessionDialog = new context.JK.RateSessionDialog(context.JK.app);
@rateSessionDialog.initialize();
@rateSessionDialog.showDialog();
render: () ->
`<a className="session-leave button-grey right leave" onClick={this.onLeave}>
<img src="/assets/content/icon_leave.png" align="texttop" height="14" width="14"/>
LEAVE
</a>`
})

View File

@ -0,0 +1,53 @@
context = window
rest = context.JK.Rest()
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
MIX_MODES = context.JK.MIX_MODES
@SessionMasterCategoryControls = React.createClass({
mixins: [Reflux.listenTo(@SessionMediaTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
onInputsChanged: (sessionMixers) ->
mixers = sessionMixers.mixers
inputGroupMixers = mixers.getAudioInputCategoryMixer(MIX_MODES.MASTER)
chatGroupMixers = mixers.getChatCategoryMixer(MIX_MODES.MASTER)
@setState({inputGroupMixers: inputGroupMixers, chatGroupMixers: chatGroupMixers})
render: () ->
categoryControls = []
if @state.inputGroupMixers?
input =
mixers: @state.inputGroupMixers
categoryControls.push(`<SessionMusicMixer key={input.mixers.mixer.id} {...input} />`)
if @state.chatGroupMixers?
input =
mixers: @state.chatGroupMixers
categoryControls.push(`<SessionChatMixer key={input.mixers.mixer.id} {...input} />`)
`<div className="session-category-controls">
<h2>master output</h2>
<div className="session-tracks-scroller">
{categoryControls}
</div>
</div>`
getInitialState:() ->
{inputGroupMixers: null, chatGroupMixers: null}
onAppInit: (app) ->
@app = app
})

View File

@ -0,0 +1,55 @@
context = window
rest = context.JK.Rest()
SessionActions = @SessionActions
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
MIX_MODES = context.JK.MIX_MODES
EVENTS = context.JK.EVENTS
ChannelGroupIds = context.JK.ChannelGroupIds
@SessionMasterMediaTracks = React.createClass({
mixins: [@SessionMediaTracksMixin, Reflux.listenTo(@SessionMediaTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
render: () ->
mediaTracks = []
if this.state.mediaSummary.mediaOpen
if this.state.mediaSummary.backingTrackOpen
for backingTrack in @state.backingTracks
backingTrack.mode = MIX_MODES.MASTER
mediaTracks.push(`<SessionBackingTrack key={backingTrack.track.id} {...backingTrack} />`)
else if this.state.mediaSummary.jamTrackOpen
mediaTracks.push(`<SessionJamTrackCategory key="JamTrackCategory" jamTrackName={this.state.jamTrackName} mixers={this.state.mediaCategoryMixer} mode={MIX_MODES.MASTER} />`)
for jamTrack in @state.jamTracks
jamTrack.mode = MIX_MODES.MASTER
mediaTracks.push(`<SessionJamTrack key={jamTrack.id} {...jamTrack} />`)
else if this.state.mediaSummary.recordingOpen
mediaTracks.push(`<SessionRecordedCategory key="RecordedCategory" recordingName={this.state.recordingName} mixers={this.state.mediaCategoryMixer} mode={MIX_MODES.MASTER} />`)
for recordedTrack in @state.recordedTracks
recordedTrack.mode = MIX_MODES.MASTER
mediaTracks.push(`<SessionRecordedTrack key={recordedTrack.track.id} {...recordedTrack} />`)
else if this.state.mediaSummary.metronomeOpen
@state.metronome.mode = MIX_MODES.MASTER
mediaTracks.push(`<SessionMetronome key={this.state.metronome.id} {...this.state.metronome} />`)
`<div className="session-media-tracks">
<h2>recorded audio</h2>
<div className="session-tracks-scroller">
{mediaTracks}
</div>
</div>`
getInitialState:() ->
{mediaSummary:{mediaOpen: false}, isRecording: false, backingTracks: [], jamTracks: [], recordedTracks: [], metronome: null}
onAppInit: (app) ->
@app = app
})

View File

@ -0,0 +1,13 @@
context = window
MIX_MODES = context.JK.MIX_MODES
@SessionMasterMix = React.createClass({
render: () ->
`<div id="master-tracks">
<SessionMasterMyTracks mode={MIX_MODES.MASTER} />
<SessionMasterOtherTracks mode={MIX_MODES.MASTER} />
<SessionMasterMediaTracks mode={MIX_MODES.MASTER} />
<SessionMasterCategoryControls mode={MIX_MODES.MASTER} />
</div>`
})

View File

@ -0,0 +1,40 @@
context = window
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
MIX_MODES = context.JK.MIX_MODES
logger = context.JK.logger
@SessionMasterMyTracks = React.createClass({
mixins: [@SessionMyTracksMixin, Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
render: () ->
content = null
tracks = []
if this.state.tracks.length > 0
for track in this.state.tracks
track.mode = MIX_MODES.MASTER
tracks.push(`<SessionMyTrack key={track.track.client_track_id} {...track} />`)
if @state.chat
@state.chat.mode = @props.mode
tracks.push(`<SessionMyChat key="chat" {...this.state.chat} />`)
else if this.state.session? && this.state.session.inSession()
logger.debug("no 'my inputs' for master mix")
`<div className="session-my-tracks">
<h2>my live tracks</h2>
<div className="session-tracks-scroller">
{content}
{tracks}
</div>
</div>`
getInitialState:() ->
{tracks:[], session: null}
onAppInit: (app) ->
@app = app
})

View File

@ -0,0 +1,93 @@
context = window
MixerActions = @MixerActions
@SessionMasterOtherTrack = React.createClass({
handleMute: (e) ->
e.preventDefault()
unless @props.mixers.mixer?
logger.debug("ignoring mute; no mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([@props.mixers.mixer], muting)
render: () ->
muteMixer = @props.mixers.muteMixer
vuMixer = @props.mixers.vuMixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
pan = if @props.mixers.mixer? then @props.mixers.mixer.pan else 0
panStyle = {
transform: "rotate(#{pan}deg)"
WebkitTransform: "rotate(#{pan}deg)"
}
# <div className="track-icon-equalizer" />
`<div className="session-track my-track">
<div className="disabled-track-overlay" />
<div className="session-track-contents">
<div className="name">{this.props.name}</div>
<div className="track-avatar"><img src={this.props.photoUrl}/></div>
<div className="track-instrument"><img height="45" src={this.props.instrumentIcon} width="45" /></div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={this.props.mixers} />
<div className="track-buttons">
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
<div className="track-icon-pan" style={panStyle}/>
</div>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:this.props.mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:this.props.mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
componentWillUpdate: (nextProps, nextState) ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
# disable hover effects if there is no mixer
if nextProps.mixers.mixer?
$mute.off("click", false)
$pan.off("click", false)
else
$mute.on("click", false)
$pan.on("click", false)
})

View File

@ -0,0 +1,66 @@
context = window
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
@SessionMasterOtherTracks = React.createClass({
mixins: [Reflux.listenTo(@SessionOtherTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
onInputsChanged: (sessionMixers) ->
session = sessionMixers.session
mixers = sessionMixers.mixers
noAudioUsers = mixers.noAudioUsers
tracks = []
if session.inSession()
for participant in session.otherParticipants()
name = participant.user.name;
firstTrack = participant.tracks[0]
photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url)
for track in participant.tracks
mixerData = mixers.findMixerForTrack(participant.client_id, track, false, @props.mode)
instrumentIcon = context.JK.getInstrumentIcon45(firstTrack.instrument_id)
trackState = {
participant: participant,
track: track,
mixers: mixerData,
name: name,
instrumentIcon: instrumentIcon,
photoUrl: photoUrl,
hasMixer: mixerData.mixer? ,
noAudio: noAudioUsers[participant.client_id]
}
tracks.push(trackState)
# todo: sessionModel.setAudioEstablished
this.setState(tracks: tracks, session: session)
render: () ->
tracks = []
for track in @state.tracks
tracks.push(`<SessionMasterOtherTrack key={track.track.client_track_id} {...track} />`)
`<div className="session-other-tracks">
<h2>other live tracks</h2>
<div className="session-tracks-scroller">
{tracks}
</div>
</div>`
getInitialState:() ->
{tracks:[], session: null}
onAppInit: (app) ->
@app = app
})

View File

@ -0,0 +1,307 @@
context = window
rest = context.JK.Rest()
SessionActions = @SessionActions
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
MIX_MODES = context.JK.MIX_MODES
EVENTS = context.JK.EVENTS
ChannelGroupIds = context.JK.ChannelGroupIds
@SessionMediaTracks = React.createClass({
mixins: [@SessionMediaTracksMixin, Reflux.listenTo(@SessionMediaTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
inputsChangedProcessed: (state) ->
if state.mediaSummary.mediaOpen
if !@state.childWindow?
childWindow = window.open("/popups/media-controls", 'Media Controls', 'scrollbars=yes,toolbar=no,status=no,height=155,width=350')
childWindow.PopupProps = state
state.childWindow = childWindow
else
if !state.metronomeFlickerTimeout? # if the metronomeFlickerTimeout is active, we don't consider closing the childWindow
@checkCloseWindow()
state.childWindow = null
checkCloseWindow: () ->
if @state.childWindow?
@state.childWindow.DontAutoCloseMedia = true
@state.childWindow.close()
closeAudio: (e) ->
e.preventDefault()
SessionActions.closeMedia()
cancelDownloadJamTrack: (e) ->
e.preventDefault()
logger.debug("closing DownloadJamTrack widget")
@state.downloadJamTrack.root.remove()
@state.downloadJamTrack.destroy()
SessionActions.downloadingJamTrack(false)
@setState({downloadJamTrack: null})
openRecording: (e) ->
e.preventDefault()
# just ignore the click if they are currently recording for now
if @state.isRecording
@app.notify({
"title": "Currently Recording",
"text": "You can't open a recording while creating a recording.",
"icon_url": "/assets/content/icon_alert_big.png"
})
return
@app.layout.showDialog('localRecordings') unless @app.layout.isDialogShowing('localRecordings')
openBackingTrack: (e) ->
e.preventDefault()
if @state.backingTrackDialogOpen
logger.debug("backing track dialog already open")
return
# just ignore the click if they are currently recording for now
if @state.isRecording
@app.notify({
"title": "Currently Recording",
"text": "You can't open a backing track while creating a recording.",
"icon_url": "/assets/content/icon_alert_big.png"
});
return
@setState({backingTrackDialogOpen: true})
context.jamClient.ShowSelectBackingTrackDialog("window.JK.HandleBackingTrackSelectedCallback2");
openMetronome: (e) ->
if @state.isRecording
@app.notify({
"title": "Currently Recording",
"text": "You can't open a metronome while creating a recording.",
"icon_url": "/assets/content/icon_alert_big.png"
})
return
SessionActions.openMetronome()
openJamTrack: (e) ->
e.preventDefault()
if @state.isRecording
@app.notify({
"title": "Currently Recording",
"text": "You can't open a jam track while creating a recording.",
"icon_url": "/assets/content/icon_alert_big.png"
})
return
@app.layout.showDialog('open-jam-track-dialog').one(EVENTS.DIALOG_CLOSED, (e, data) =>
# once the dialog is closed, see if the user has a jamtrack selected
if !data.canceled && data.result.jamTrack
@loadJamTrack(data.result.jamTrack)
else
logger.debug("OpenJamTrack dialog closed with no selection; ignoring", data)
)
loadJamTrack: (jamTrack) ->
if @state.downloadJamTrack
# if there was one showing before somehow, destroy it.
logger.warn("destroying existing JamTrack")
@state.downloadJamTrack.root.remove()
@state.downloadJamTrack.destroy()
#set to null
downloadJamTrack = new context.JK.DownloadJamTrack(@app, jamTrack, 'large');
# the widget indicates when it gets to any transition; we can hide it once it reaches completion
$(downloadJamTrack).on(EVENTS.JAMTRACK_DOWNLOADER_STATE_CHANGED, (e, data) =>
if data.state == downloadJamTrack.states.synchronized
logger.debug("jamtrack synchronized; hide widget and show tracks")
downloadJamTrack.root.remove()
downloadJamTrack.destroy()
downloadJamTrack = null
this.setState({downloadJamTrack: null})
# XXX: test with this removed; it should be unnecessary
context.jamClient.JamTrackStopPlay();
sampleRate = context.jamClient.GetSampleRate()
sampleRateForFilename = if sampleRate == 48 then '48' else '44'
fqId = jamTrack.id + '-' + sampleRateForFilename
if jamTrack.jmep
logger.debug("setting jmep data")
context.jamClient.JamTrackLoadJmep(fqId, jamTrack.jmep)
else
logger.debug("no jmep data for jamtrack")
# JamTrackPlay means 'load'
result = context.jamClient.JamTrackPlay(fqId);
SessionActions.downloadingJamTrack(false)
console.log("JamTrackPlay: result", )
if !result
@app.notify(
{
title: "JamTrack Can Not Open",
text: "Unable to open your JamTrack. Please contact support@jamkazam.com"
}
, null, true)
else
participantCnt = context.SessionStore.participants().length
rest.playJamTrack(jamTrack.id)
.done(() =>
@app.refreshUser();
)
context.stats.write('web.jamtrack.open', {
value: 1,
session_size: participantCnt,
user_id: context.JK.currentUserId,
user_name: context.JK.currentUserName
})
)
@setState({downloadJamTrack: downloadJamTrack})
render: () ->
scrollerClassData = {'session-tracks-scroller': true}
mediaOptions = `<div className="media-options">
<div className="open-media-file-header">
<div className="vertical-helper" />
<img src="/assets/content/icon_open_folder.png" width="21" height="21" />
<span className="open-text">Open:</span>
</div>
<ul className="open-media-file-options">
<li>
<a className="open-recording" onClick={this.openRecording}>Recording</a>
</li>
<li>
<a className="open-jamtrack" onClick={this.openJamTrack}>JamTrack</a>
</li>
<li>
<a className="open-backingtrack" onClick={this.openBackingTrack}>Audio File</a>
</li>
</ul>
<div className="use-metronome-header">
<img src="/assets/content/icon_instrument_metronome21.png" width="21" height="21" />
<a className="open-metronome" onClick={this.openMetronome}>Use Metronome</a>
</div>
</div>`
contents = null
mediaTracks = []
if this.state.downloadJamTrack?
closeOptions =
`<div>
<a className="closeAudio" onClick={this.cancelDownloadJamTrack}>
<img src="/assets/content/icon_close.png" width="18" height="20" />
Close JamTrack
</a>
<div className="download-jamtrack-holder"></div>
</div>`
contents = closeOptions
else if this.state.mediaSummary.mediaOpen
# give the users options to close it
if this.state.mediaSummary.jamTrackOpen
mediaType = "JamTrack"
else if this.state.mediaSummary.backingTrackOpen
mediaType = "Audio File"
else if this.state.mediaSummary.metronomeOpen
mediaType = "Metronome"
else if this.state.mediaSummary.recordingOpen
mediaType = "Recording"
else
mediaType = ""
closeOptions = `<a className="closeAudio" onClick={this.closeAudio}>
<img src="/assets/content/icon_close.png" width="18" height="20" />
Close {mediaType}
</a>`
if this.state.mediaSummary.backingTrackOpen
for backingTrack in @state.backingTracks
backingTrack.mode = MIX_MODES.PERSONAL
mediaTracks.push(`<SessionBackingTrack key={backingTrack.track.id} {...backingTrack} />`)
else if this.state.mediaSummary.jamTrackOpen
mediaTracks.push(`<SessionJamTrackCategory key="JamTrackCategory" jamTrackName={this.state.jamTrackName} mixers={this.state.mediaCategoryMixer} mode={MIX_MODES.PERSONAL} />`)
for jamTrack in @state.jamTracks
jamTrack.mode = MIX_MODES.PERSONAL
mediaTracks.push(`<SessionJamTrack key={jamTrack.id} {...jamTrack} />`)
else if this.state.mediaSummary.recordingOpen
mediaTracks.push(`<SessionRecordedCategory key="RecordedCategory" recordingName={this.state.recordingName} mixers={this.state.mediaCategoryMixer} mode={MIX_MODES.PERSONAL} />`)
for recordedTrack in @state.recordedTracks
recordedTrack.mode = MIX_MODES.PERSONAL
mediaTracks.push(`<SessionRecordedTrack key={recordedTrack.track.id} {...recordedTrack} />`)
else if this.state.mediaSummary.metronomeOpen
@state.metronome.mode = MIX_MODES.PERSONAL
mediaTracks.push(`<SessionMetronome key={this.state.metronome.id} {...this.state.metronome} />`)
contents = closeOptions
else
scrollerClassData['media-options-showing'] = true
contents = mediaOptions
scrollerClasses = classNames(scrollerClassData)
`<div className="session-media-tracks">
<h2>recorded audio</h2>
{contents}
<div className={scrollerClasses}>
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
{mediaTracks}
</ReactCSSTransitionGroup>
</div>
</div>`
getInitialState:() ->
{mediaSummary:{mediaOpen: false}, isRecording: false, backingTracks: [], jamTracks: [], recordedTracks: [], metronome: null}
onAppInit: (app) ->
@app = app
handleBackingTrackSelectedCallback: (result) ->
@setState({backingTrackDialogOpen: false})
SessionActions.openBackingTrack(result)
componentDidMount: () ->
context.JK.HandleBackingTrackSelectedCallback2 = @handleBackingTrackSelectedCallback
componentDidUpdate: () ->
if @state.downloadJamTrack?
$holder = $(@getDOMNode()).find('.download-jamtrack-holder')
if $holder.find('.download-jamtrack').length == 0
SessionActions.downloadingJamTrack(true)
$holder.append(@state.downloadJamTrack.root)
# kick off the download JamTrack process
@state.downloadJamTrack.init()
@checkCloseWindow() if !@state.mediaSummary.mediaOpen && !@state.metronomeFlickerTimeout?
})

View File

@ -0,0 +1,81 @@
context = window
MixerActions = @MixerActions
@SessionMetronome = React.createClass({
handleMute: (e) ->
e.preventDefault()
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([this.props.mixers.mixer], muting)
render: () ->
# today, all mixers are the same for a remote participant; so just grab the 1st
mixers = @props.mixers
muteMixer = mixers.muteMixer
vuMixer = mixers.vuMixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
componentClasses = classNames({
"session-track" : true
"metronome" : true
})
pan = if mixers.mixer? then mixers.mixer?.pan else 0
panStyle = {
transform: "rotate(#{pan}deg)"
WebkitTransform: "rotate(#{pan}deg)"
}
`<div className={componentClasses}>
<div className="session-track-contents">
<div className="name">Metronome</div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={mixers} />
<div className="track-buttons">
<div className="track-instrument"><img height="24" src={this.props.instrumentIcon} width="24" /></div>
<div className="track-icon-pan" style={panStyle}/>
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
</div>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@props.mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:@props.mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -0,0 +1,14 @@
context = window
@SessionMixerBtn = React.createClass({
openDialog: (e) ->
e.preventDefault()
context.JK.app.layout.showDialog('session-master-mix-dialog')
render: () ->
`<a className="session-mixer button-grey left" onClick={this.openDialog}>
<img src="/assets/content/icon_mixer.png" align="texttop" height="14" width="14"/>
MIXER
</a>`
})

View File

@ -0,0 +1,75 @@
context = window
@SessionMusicMixer= React.createClass({
handleMute: (e) ->
e.preventDefault()
unless @props.mixers.mixer
logger.debug("ignoring mute; no mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([@props.mixers.mixer], muting)
render: () ->
muteMixer = @props.mixers.muteMixer
vuMixer = @props.mixers.vuMixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
pan = if @props.mixers.mixer? then @props.mixers.mixer.pan else 0
panStyle = {
transform: "rotate(#{pan}deg)"
WebkitTransform: "rotate(#{pan}deg)"
}
`<div className="session-track music-mixer">
<div className="disabled-track-overlay" />
<div className="session-track-contents">
<div className="name">Session Music Output</div>
<div className="track-instrument"><img height="45" src="/assets/content/icon_instrument_voice45.png" width="45" /></div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={this.props.mixers} />
<div className="track-buttons">
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
<div className="track-icon-pan" style={panStyle}/>
</div>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@props.mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:@props.mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -0,0 +1,69 @@
context = window
MixerActions = @MixerActions
@SessionMyChat = React.createClass({
mixins: [@MasterPersonalMixersMixin]
handleMute: (e) ->
e.preventDefault()
mixers = @mixers()
unless mixers.mixer
logger.debug("ignoring mute; no mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([mixers.mixer, mixers.oppositeMixer], muting)
render: () ->
mixers = @mixers()
muteMixer = mixers.muteMixer
vuMixer = mixers.vuMixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
# <div className="track-icon-equalizer" />
`<div className="session-track chat-track">
<div className="disabled-track-overlay" />
<div className="session-track-contents">
<div className="name">{this.props.name}</div>
<div className="track-avatar"><img src={this.props.photoUrl}/></div>
<div className="track-instrument"><img height="45" src='/assets/content/icon_instrument_headphones45.png' width="45" /></div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={mixers} />
<div className="track-buttons">
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
</div>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@mixers()}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -0,0 +1,96 @@
context = window
MixerActions = @MixerActions
@SessionMyTrack = React.createClass({
handleMute: (e) ->
e.preventDefault()
unless this.props.mixers.mixer
logger.debug("ignoring mute; no mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([this.props.mixers.mixer, this.props.mixers.oppositeMixer], muting)
render: () ->
muteMixer = this.props.mixers.muteMixer
vuMixer = this.props.mixers.vuMixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
pan = if this.props.mixers.mixer? then this.props.mixers.mixer.pan else 0
panStyle = {
transform: "rotate(#{pan}deg)"
WebkitTransform: "rotate(#{pan}deg)"
}
# <div className="track-icon-equalizer" />
`<div className="session-track my-track">
<div className="disabled-track-overlay" />
<div className="session-track-contents">
<div className="name">{this.props.name}</div>
<div className="track-avatar"><img src={this.props.photoUrl}/></div>
<div className="track-instrument"><img height="45" src={this.props.instrumentIcon} width="45" /></div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={this.props.mixers} />
<div className="track-buttons">
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
<div className="track-icon-pan" style={panStyle}/>
</div>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
context.jamClient.SessionSetUserName(this.props.clientId, this.props.name)
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:this.props.mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:this.props.mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
componentWillUpdate: (nextProps, nextState) ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
# disable hover effects if there is no mixer
if nextProps.mixers.mixer?
$mute.off("click", false)
$pan.off("click", false)
else
$mute.on("click", false)
$pan.on("click", false)
})

Some files were not shown because too many files have changed in this diff Show More