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-failed-job-mailer'
gem 'resque-lonely_job', '~> 1.0.0'
gem 'eventmachine', '1.0.3'
gem 'eventmachine', '1.0.4'
gem 'amqp', '0.9.8'
gem 'logging-rails', :require => 'logging/rails'
gem 'pg_migrate'

View File

@ -77,7 +77,7 @@ EOF
#bundle install --path vendor/bundle --local
# prepare production acssets
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
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.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 = {
:address => Rails.application.config.email_smtp_address,
:port => Rails.application.config.email_smtp_port,

5
build
View File

@ -62,8 +62,9 @@ popd > /dev/null
if [ ! -z "$PACKAGE" ]; then
DEB_SERVER=http://localhost:9010/apt-`uname -p`
GEM_SERVER=http://localhost:9000/gems
source /etc/lsb-release
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 [[ "$GIT_BRANCH" == *develop* || "$GIT_BRANCH" == *master* || "$GIT_BRANCH" == *release* || "$GIT_BRANCH" == *feature* || "$GIT_BRANCH" == *hotfix* ]]; then

View File

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

View File

@ -276,12 +276,6 @@ jam_track_duration.sql
sales.sql
show_whats_next_count.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
packaging_notices.sql
first_played_jamtrack_at.sql
@ -292,7 +286,14 @@ signing.sql
optimized_redeemption.sql
optimized_redemption_warn_mode.sql
affiliate_partners2.sql
enhance_band_profile.sql
broadcast_notifications.sql
broadcast_notifications_fk.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
GEM_SERVER=http://localhost:9000/gems
GEM_SERVER=https://int.jamkazam.com:9000/gems
echo "starting build..."
./build

View File

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

View File

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

View File

@ -23,6 +23,8 @@ module JamRuby
validate :validate_photo_info
validate :require_at_least_one_genre, :unless => :skip_genre_validation
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_website_url
@ -192,6 +194,17 @@ module JamRuby
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.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]
# loop through each genre in the array and save to the db
genres = []

View File

@ -125,6 +125,7 @@ module JamRuby
ms
end
# XXX SQL INJECTION
def _genres(rel)
gids = json[KEY_GENRES]
unless gids.blank?
@ -135,11 +136,12 @@ module JamRuby
rel
end
# XXX SQL INJECTION
def _instruments(rel)
unless (instruments = json['instruments']).blank?
instsql = "SELECT player_id FROM musicians_instruments WHERE (("
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 (")
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.'
end
jj = self.json
str = 'Current Search: '
str += "Sort = #{SORT_ORDERS[json_value(MusicianSearch::KEY_SORT_ORDER)]}"
str = ''
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]
str += "; Interest = #{INTERESTS[val]}"
str += ", Interest = #{INTERESTS[val]}"
end
if (val = jj[KEY_SKILL].to_i) != SKILL_VALS[0]
str += "; Skill = #{SKILL_LEVELS[val]}"
str += ", Skill = #{SKILL_LEVELS[val]}"
end
if (val = jj[KEY_STUDIOS].to_i) != STUDIO_COUNTS[0]
str += "; Studio Sessions = #{STUDIOS_LABELS[val]}"
str += ", Studio Sessions = #{STUDIOS_LABELS[val]}"
end
if (val = jj[KEY_GIGS].to_i) != GIG_COUNTS[0]
str += "; Concert Gigs = #{GIG_LABELS[val]}"
str += ", Concert Gigs = #{GIG_LABELS[val]}"
end
val = jj[KEY_AGES].map(&:to_i)
val.sort!
if !val.blank?
str += "; Ages = "
str += ", Ages = "
val.each_with_index do |vv, idx|
str += "#{AGES[vv]}"
str += ', ' unless idx==(val.length-1)
end
end
if 0 < (val = jj[KEY_GENRES]).length
str += "; Genres = "
str += ", Genres = "
genres = Genre.where(["id IN (?)", val]).order('description').pluck(:description)
genres.each_with_index do |gg, idx|
str += "#{gg}"
str += ', ' unless idx==(genres.length-1)
end
end
if 0 < (val = jj[KEY_INSTRUMENTS]).length
str += "; Instruments = "
instr_ids = val.collect { |vv| vv['instrument_id'] }
instrs = Instrument.where(["id IN (?)", instr_ids]).order(:description)
instrs.each_with_index do |ii, idx|
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
str += ", Sort = #{SORT_ORDERS[json_value(MusicianSearch::KEY_SORT_ORDER)]}"
if str.start_with?(', ')
# trim off any leading ,
str = str[2..-1]
end
str = 'Current Search: ' + str
str
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 :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
validate :validate_musician_instruments
validate :validate_current_password

View File

@ -67,6 +67,40 @@ describe Band do
band.country.should == band_params[:country]
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
expect{ Band.save(fan, band_params) }.to raise_error("must be a musician")
end

View File

@ -206,6 +206,10 @@ def app_config
1
end
def google_public_server_key
"AIzaSyCPTPq5PEcl4XWcm7NZ2IGClZlbsiE8JNo"
end
private
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 'rabl', '0.11.0' # for JSON API development
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 'amqp', '0.9.8'
gem 'logging-rails', :require => 'logging/rails'
@ -95,6 +95,7 @@ gem 'react-rails', '~> 1.0'
source 'https://rails-assets.org' do
gem 'rails-assets-reflux'
gem 'rails-assets-classnames'
end
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,50 +198,56 @@
function loggedIn(header, payload) {
server.signedIn = true;
server.clientID = payload.client_id;
server.publicIP = payload.public_ip;
// 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...
if (context.jamClient !== undefined) {
context.jamClient.connected = true;
context.jamClient.clientID = server.clientID;
}
setTimeout(function() {
server.signedIn = true;
server.clientID = payload.client_id;
server.publicIP = payload.public_ip;
clearConnectTimeout();
if (context.jamClient !== undefined) {
context.jamClient.connected = true;
context.jamClient.clientID = server.clientID;
}
heartbeatStateReset();
clearConnectTimeout();
app.clientId = payload.client_id;
heartbeatStateReset();
if(isClientMode()) {
// tell the backend that we have logged in
context.jamClient.OnLoggedIn(payload.user_id, payload.token); // ACTS AS CONTINUATION
$.cookie('client_id', payload.client_id);
}
app.clientId = payload.client_id;
// this has to be after context.jamclient.OnLoggedIn, because it hangs in scenarios
// where there is no device on startup for the current profile.
// So, in that case, it's possible that a reconnect loop will attempt, but we *do not want*
// it to go through unless we've passed through .OnLoggedIn
server.connected = true;
server.reconnecting = false;
server.connecting = false;
initialConnectAttempt = false;
if (isClientMode()) {
// tell the backend that we have logged in
context.jamClient.OnLoggedIn(payload.user_id, payload.token); // ACTS AS CONTINUATION
$.cookie('client_id', payload.client_id);
}
heartbeatMS = payload.heartbeat_interval * 1000;
connection_expire_time = payload.connection_expire_time * 1000;
logger.info("loggedIn(): clientId=" + app.clientId + " heartbeat=" + payload.heartbeat_interval + "s expire_time=" + payload.connection_expire_time + 's');
heartbeatInterval = context.setInterval(_heartbeat, heartbeatMS);
heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000);
lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat
connectDeferred.resolve();
$self.triggerHandler(EVENTS.CONNECTION_UP)
// this has to be after context.jamclient.OnLoggedIn, because it hangs in scenarios
// where there is no device on startup for the current profile.
// So, in that case, it's possible that a reconnect loop will attempt, but we *do not want*
// it to go through unless we've passed through .OnLoggedIn
server.connected = true;
server.reconnecting = false;
server.connecting = false;
initialConnectAttempt = false;
activeElementEvent('afterConnect', payload);
heartbeatMS = payload.heartbeat_interval * 1000;
connection_expire_time = payload.connection_expire_time * 1000;
logger.info("loggedIn(): clientId=" + app.clientId + " heartbeat=" + payload.heartbeat_interval + "s expire_time=" + payload.connection_expire_time + 's');
heartbeatInterval = context.setInterval(_heartbeat, heartbeatMS);
heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000);
lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat
connectDeferred.resolve();
$self.triggerHandler(EVENTS.CONNECTION_UP)
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)
}
activeElementEvent('afterConnect', payload);
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)
}
}, 0)
}
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-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-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; } );

View File

@ -207,7 +207,7 @@
rest.getLinks(type)
.done(populateLinkTable)
.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')
context.JK.dropdown($traditionalTouringOption)
$hourlyRate.val(userDetail.paid_sessions_hourly_rate)
$dailyRate.val(userDetail.paid_sessions_daily_rate)
// convert the value to cents
$hourlyRate.val(profileUtils.normalizeMoneyForDisplay(userDetail.paid_sessions_hourly_rate));
$dailyRate.val(profileUtils.normalizeMoneyForDisplay(userDetail.paid_sessions_daily_rate));
$cowritingPurpose.val(userDetail.cowriting_purpose)
context.JK.dropdown($cowritingPurpose)
@ -238,11 +239,11 @@
paid_sessions: $screen.find('input[name=paid_sessions]:checked').val(),
paid_session_genres: $paidSessionsGenreList.html() === NONE_SPECIFIED ? [] : $paidSessionsGenreList.html().split(GENRE_LIST_DELIMITER),
paid_sessions_hourly_rate: $hourlyRate.val(),
paid_sessions_daily_rate: $dailyRate.val(),
paid_sessions_hourly_rate: profileUtils.normalizeMoneyForSubmit($hourlyRate.val()),
paid_sessions_daily_rate: profileUtils.normalizeMoneyForSubmit($dailyRate.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_genres: $cowritingGenreList.html() === NONE_SPECIFIED ? [] : $cowritingGenreList.html().split(GENRE_LIST_DELIMITER),
@ -263,7 +264,12 @@
var errors = JSON.parse(xhr.responseText)
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 {
app.ajaxError(xhr, textStatus, errorMessage)

View File

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

View File

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

View File

@ -161,7 +161,7 @@
/**
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:
// 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.payment
//= require jquery.visible
//= require classnames
//= require reflux
//= require howler.core.js
//= require jstz
@ -54,11 +55,12 @@
//= require react
//= require react_ujs
//= require react-init
//= require react-components
//= require web/signup_helper
//= require web/signin_helper
//= require web/signin
//= require web/tracking
//= require webcam_viewer
//= require react-components
//= require_directory .
//= require_directory ./dialog
//= require_directory ./wizard

View File

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

View File

@ -151,8 +151,8 @@
$("#play-commitment").val('1')
$("#hourly-rate").val("0.0")
$("#gig-minimum").val("0.0")
$screen.find("#hourly-rate").val("0")
$screen.find("#gig-minimum").val("0")
resetGenres();
renderDesiredExperienceLabel([])
@ -225,9 +225,14 @@
band.free_gigs=$('input[name="free_gigs"]:checked').val()=="yes"
band.touring_option=$('#touring-option').val()=="yes"
band.play_commitment=$("#play-commitment").val()
band.hourly_rate=$("#hourly-rate").val()
band.gig_minimum=$("#gig-minimum").val()
if ($screen.find("#play-commitment").length == 0) {
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) {
band.genres = getSelectedGenres();
@ -424,8 +429,8 @@
$('#touring-option').val(band.touring_option ? 'yes' : 'no')
$("#play-commitment").val(band.play_commitment)
$("#hourly-rate").val(band.hourly_rate)
$("#gig-minimum").val(band.gig_minimum)
$("#hourly-rate").val(profileUtils.normalizeMoneyForDisplay(band.hourly_rate))
$("#gig-minimum").val(profileUtils.normalizeMoneyForDisplay(band.gig_minimum))
// Initialize avatar
if (band.photo_url) {

View File

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

View File

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

View File

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

View File

@ -120,11 +120,11 @@
openingRecording = true;
// 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) {
// update session info
context.JK.CurrentSessionModel.updateSession(response);
context.SessionActions.updateSession.trigger(response);
var recordingId = $(this).attr('data-recording-id');
var openRecordingResult = context.jamClient.OpenRecording(claimedRecording.recording);
@ -138,7 +138,7 @@
"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) {
app.notify({
"title": "Couldn't Stop Recording Playback",

View File

@ -85,7 +85,7 @@
var backingTrack = $(this).data('server-model');
// 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) {
var result = context.jamClient.SessionOpenBackingTrackFile(backingTrack.name, false);
@ -99,7 +99,7 @@
// else {
// logger.error("unable to open backing track")
// }
context.JK.CurrentSessionModel.refreshCurrentSession(true);
context.SessionActions.syncWithServer()
})
.fail(function(jqXHR) {

View File

@ -86,10 +86,10 @@
var jamTrack = $(this).data('server-model');
// 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) {
$dialog.data('result', {success:true, jamTrack: jamTrack})
context.JK.CurrentSessionModel.updateSession(response);
context.SessionActions.updateSession.trigger(response);
app.layout.closeDialog('open-jam-track-dialog');
})
.fail(function(jqXHR) {

View File

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

View File

@ -57,7 +57,7 @@
});
}
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({
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.",

View File

@ -2,7 +2,7 @@
"use strict";
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 rest = context.JK.Rest();
var recordingUtils = context.JK.RecordingUtils;
@ -10,173 +10,26 @@
var dialogId = 'recording-selector-dialog';
var $screen = $('#' + dialogId);
var $btnSelect = $screen.find(".btn-select-recordings");
var $instructions = $screen.find('#instructions');
var $recordings = $screen.find('.recordings');
var $paginatorHolder = null;
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 afterShow(data) {
$recordings.empty();
$.each(recordings, function(index, val) {
bindRecordingItem(val);
});
feedHelper.setUser(context.JK.currentUserId)
feedHelper.refresh()
// hide the avatars
$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);
//$screen.find('.avatar-small.ib').hide();
}
function afterHide() {
@ -187,10 +40,10 @@
}
function events() {
$btnSelect.click(function(evt) {
$btnSelect.off('click').on('click', function(evt) {
evt.preventDefault();
var preSelectedRecordings = [];
$recordings.find('input[type=checkbox]:checked').each(function(index) {
$recordings.find('.select-box input[type=checkbox]:checked').each(function(index) {
preSelectedRecordings.push({
"id": $(this).attr('data-recording-id'),
"name": $(this).attr('data-recording-title')
@ -198,6 +51,7 @@
});
if (selectCallback) {
console.log("calling selectCallback", preSelectedRecordings)
selectCallback(preSelectedRecordings);
}
@ -217,8 +71,6 @@
app.bindDialog(dialogId, dialogBindings);
$instructions.html('Select one or more recordings and click ADD to add JamKazam recordings to your performance samples.');
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,$) {
context.JK = context.JK || {};
context.JK.SessionSettingsDialog = function(app, sessionScreen) {
context.JK.SessionSettingsDialog = function(app) {
var logger = context.JK.logger;
var gearUtils = context.JK.GearUtilsInstance;
var $dialog;
var $screen = $('#session-settings');
var $selectedFilenames = $screen.find('#selected-filenames');
var $uploadSpinner = $screen.find('.upload-spinner');
var $selectedFilenames = $('#settings-selected-filenames');
//var $selectedFilenames = $screen.find('#selected-filenames');
var $uploadSpinner = $screen.find('.spinner-small');
//var $selectedFilenames = $('#settings-selected-filenames');
var $inputFiles = $screen.find('#session-select-files');
var $btnSelectFiles = $screen.find('.btn-select-files');
var $inputBox = $screen.find('.inputbox')
var rest = new JK.Rest();
var sessionId;
@ -21,7 +22,7 @@
context.JK.GenreSelectorHelper.render('#session-settings-genre');
$dialog = $('[layout-id="session-settings"]');
var currentSession = sessionScreen.getCurrentSession();
var currentSession = context.SessionStore.currentSession;
sessionId = currentSession.id;
// id
@ -65,13 +66,21 @@
$('#session-settings-fan-access').val('listen-chat-band');
}
// notation files
/**
// notation files in the account screen. ugh.
$selectedFilenames.empty();
for (var i=0; i < currentSession.music_notations.length; i++) {
var notation = currentSession.music_notations[i];
$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-musician-access'));
context.JK.dropdown($('#session-settings-fan-access'));
@ -81,6 +90,29 @@
$('#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) {
var data = {};
@ -111,16 +143,14 @@
data.fan_access = false;
data.fan_chat = false;
}
else if (fanAccess == 'listen-chat-each') {
data.fan_access = true;
data.fan_chat = false;
}
else if (fanAccess == 'listen-chat-band') {
else if (fanAccess == 'listen-chat') {
data.fan_access = true;
data.fan_chat = true;
}
rest.updateSession($('#session-settings-id').val(), data).done(settingsSaved);
return false;
}
function uploadNotations(notations) {
@ -177,7 +207,7 @@
}
})
.always(function() {
$btnSelectFiles.text('SELECT FILES...').data('uploading', null)
$btnSelectFiles.text('ADD FILES...').data('uploading', null)
$uploadSpinner.hide();
});
}
@ -203,10 +233,9 @@
else {
// upload as soon as user picks their files.
uploadNotations($inputFiles.get(0).files)
.done(function() {
context._.each(fileNames, function(fileName) {
var $text = $('<span>').text(fileName);
$selectedFilenames.append($text);
.done(function(response) {
context._.each(response, function(notation) {
addNotation(notation)
})
})
}
@ -225,13 +254,13 @@
function settingsSaved(response) {
// No response returned from this call. 204.
sessionScreen.refreshCurrentSession(true);
context.SessionActions.syncWithServer()
app.layout.closeDialog('session-settings');
}
function events() {
$('#session-settings-dialog-submit').on('click', saveSettings);
$('#session-settings-dialog').on('submit', saveSettings)
$inputFiles.on('change', changeSelectedFiles);
$btnSelectFiles.on('click', toggleSelectFiles);
}

View File

@ -15,7 +15,8 @@ context.JK.SoundCloudPlayerDialog = class SoundCloudPlayerDialog
initialize:(@url, @caption) =>
dialogBindings = {
'beforeShow' : @beforeShow,
'afterShow' : @afterShow
'afterShow' : @afterShow,
'afterHide' : @afterHide
}
@dialog = $('[layout-id="' + @dialogId + '"]')
@ -27,9 +28,15 @@ context.JK.SoundCloudPlayerDialog = class SoundCloudPlayerDialog
beforeShow:() =>
@player.addClass("hidden")
@player.attr("src", "")
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"
@player.attr("src", src)
# the Windows client does not play back correctly
if context.jamClient.IsNativeClient()
context.JK.popExternalLink(@url)
return false
else
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"
@player.attr("src", src)
afterShow:() =>
@player.removeClass("hidden")
@ -37,4 +44,7 @@ context.JK.SoundCloudPlayerDialog = class SoundCloudPlayerDialog
showDialog:() =>
@app.layout.showDialog(@dialogId)
afterHide: () =>
@player.attr('src', '')

View File

@ -11,6 +11,7 @@
var $draggingFaderHandle = null;
var $draggingFader = null;
var $floater = null;
var draggingOrientation = null;
var logger = g.JK.logger;
@ -20,6 +21,7 @@
e.stopPropagation();
var $fader = $(this);
var floaterConvert = $fader.data('floaterConverter')
var sessionModel = window.JK.CurrentSessionModel || null;
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 position = { top: e.pageY - offset.top, left: e.pageX - offset.left}
@ -53,6 +55,10 @@
return false;
}
if(floaterConvert) {
window.JK.FaderHelpers.setFloaterValue($fader.find('.floater'), floaterConvert(faderPct))
}
$fader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: false})
setHandlePosition($fader, faderPct);
@ -61,9 +67,9 @@
function setHandlePosition($fader, value) {
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);
// required because this method is entered directly when from a callback
@ -81,7 +87,7 @@
}
function faderValue($fader, e, offset) {
var orientation = $fader.attr('orientation');
var orientation = $fader.attr('data-orientation');
var getPercentFunction = getVerticalFaderPercent;
var relativePosition = offset.top;
if (orientation && orientation == 'horizontal') {
@ -92,7 +98,7 @@
}
function getHandleCssAttribute($fader) {
var orientation = $fader.attr('orientation');
var orientation = $fader.attr('data-orientation');
return (orientation === 'horizontal') ? 'left' : 'top';
}
@ -134,12 +140,34 @@
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})
}
function onFaderDragStart(e, ui) {
$draggingFaderHandle = $(this);
$draggingFader = $draggingFaderHandle.closest('div[control="fader"]');
$draggingFader = $draggingFaderHandle.closest('div[data-control="fader"]');
$floater = $draggingFaderHandle.find('.floater')
draggingOrientation = $draggingFader.attr('orientation');
var mediaControlsDisabled = $draggingFaderHandle.data('media-controls-disabled');
@ -210,12 +238,12 @@
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-track-opener', selector.data('media-track-opener'))
.data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))
selector.find('div[control="fader-handle"]').draggable({
selector.find('div[data-control="fader-handle"]').draggable({
drag: onFaderDrag,
start: onFaderDragStart,
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) {
// deal with extremes better
@ -263,27 +328,48 @@
// composite function resembling audio taper
if (input <= 1) { return -80; }
if (input <= 28) { return (2 * input - 80); }
if (input <= 79) { return (0.5 * input - 38); }
if (input < 99) { return (0.875 * input - 67.5); }
if (input <= 28) { return Math.round((2 * input - 80)); } // -78 to -24 db
if (input <= 79) { return Math.round((0.5 * input - 38)); } // -24 to 1.5 db
if (input < 99) { return Math.round((0.875 * input - 67.5)); } // 1.625 - 19.125 db
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);
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) {
draggingOrientation = $fader.attr('orientation');
draggingOrientation = $fader.attr('data-orientation');
setHandlePosition($fader, faderValue);
draggingOrientation = null;
},
setFloaterValue: function($floater, floaterValue) {
$floater.text(floaterValue)
},
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.FakeJamClient = function(app, p2pMessageFactory) {
var ChannelGroupIds = context.JK.ChannelGroupIds;
var ChannelGroupIds = context.JK.ChannelGroupIds
var logger = context.JK.logger;
logger.info("*** Fake JamClient instance initialized. ***");
@ -170,22 +170,22 @@
function FTUEGetMusicInputs() {
dbg('FTUEGetMusicInputs');
return {
"i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1":
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
"i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1":
"Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
};
}
function FTUEGetMusicOutputs() {
dbg('FTUEGetMusicOutputs');
return {
"o~11~Multichannel (FW AP Multi)~0^o~11~Multichannel (FW AP Multi)~1":
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
"o~11~Multichannel (FWAPMulti)~0^o~11~Multichannel (FWAPMulti)~1":
"Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
};
}
function FTUEGetChatInputs() {
dbg('FTUEGetChatInputs');
return {
"i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1":
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
"i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1":
"Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
};
}
function FTUEGetChannels() {
@ -450,7 +450,7 @@
}
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;
}
@ -475,8 +475,8 @@
}
function SessionGetControlState(mixerIds, isMasterOrPersonal) {
dbg("SessionGetControlState");
var groups = [
ChannelGroupIds.MasterGroup,
var groups =
[ChannelGroupIds.MasterGroup,
ChannelGroupIds.MonitorGroup,
ChannelGroupIds.AudioInputMusicGroup,
ChannelGroupIds.AudioInputChatGroup,
@ -485,13 +485,12 @@
ChannelGroupIds.UserChatInputGroup,
ChannelGroupIds.PeerMediaTrackGroup,
ChannelGroupIds.JamTrackGroup,
ChannelGroupIds.MetronomeGroup
]
ChannelGroupIds.MetronomeGroup];
var names = [
"FW AP Multi",
"FW AP Multi",
"FW AP Multi",
"FW AP Multi",
"FWAPMulti",
"FWAPMulti",
"FWAPMulti",
"FWAPMulti",
"",
"",
"",
@ -545,6 +544,7 @@
stereo: true,
volume_left: -40,
volume_right:-40,
pan: 0,
instrument_id:50, // see globals.js
mode: isMasterOrPersonal,
rid: mixerIds[i]
@ -554,10 +554,10 @@
}
function SessionGetIDs() {
return [
"FW AP Multi_0_10000",
"FW AP Multi_1_10100",
"FW AP Multi_2_10200",
"FW AP Multi_3_10500",
"FWAPMulti_0_10000",
"FWAPMulti_1_10100",
"FWAPMulti_2_10200",
"FWAPMulti_3_10500",
"User@208.191.152.98#",
"User@208.191.152.98_*"
];
@ -624,9 +624,9 @@
function doCallbacks() {
var names = ["vu"];
//var ids = ["FW AP Multi_2_10200", "FW AP Multi_0_10000"];
var ids= ["i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1",
"i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~2"];
//var ids = ["FWAPMulti_2_10200", "FWAPMulti_0_10000"];
var ids= ["i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1",
"i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~2"];
var args = [];
for (var i=0; i<ids.length; i++) {

View File

@ -93,8 +93,8 @@
}
function onStartRecording(from, payload) {
logger.debug("received start recording request from " + from);
if(context.JK.CurrentSessionModel.recordingModel.isRecording()) {
logger.debug("received start recording request from " + from, context.SessionStore.isRecording);
if(context.SessionStore.isRecording) {
// reject the request to start the recording
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 didLoadAllFeeds = false, isLoading = false;
var $templateRecordingDiscardedSoon = null;
var defaults;
function defaultQuery() {
var query = { limit: feedBatchSize };
@ -47,9 +48,9 @@
var currentQuery = defaultQuery();
// specify search criteria based on form
currentQuery.sort = $sortFeedBy.val();
currentQuery.time_range = $includeDate.val();
currentQuery.type = $includeType.val();
currentQuery.sort = $sortFeedBy.length == 0 ? defaults.sort : $sortFeedBy.val();
currentQuery.time_range = $includeDate.length == 0 ? defaults.time_range : $includeDate.val();
currentQuery.type = $includeType.length == 0 ? defaults.type : $includeType.val();
return currentQuery;
}
@ -423,6 +424,11 @@
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
renderFeed($feedItem);
@ -448,6 +454,7 @@
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 $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()})
// 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
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;
$scroller = _$scroller;
$content = _$content;
@ -596,12 +611,10 @@
if($scroller.length == 0) throw "$scroller must be specified";
if($content.length == 0) throw "$content 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
$sortFeedBy.val(defaults.date)
$includeDate.val(defaults.time_range)

View File

@ -123,23 +123,23 @@
0: {"title": "", "message": ""}, // NO_EVENT,
1: {"title": "", "message": ""}, // BACKEND_ERROR: generic error - eg P2P message error
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,
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
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,
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,
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,
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,
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,
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
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, 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, 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, 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, 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, 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, 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, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // BANDWIDTH_LOW
// 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
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,
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
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,
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, 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, 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, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // OUTPUT_IO_JTR,
// 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,
17: {"title": "", "message": ""}, // LAST_THRESHOLD
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
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)
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
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
@ -314,19 +314,17 @@
MASTER_VS_PERSONAL_MIX : 'master_vs_personal_mix'
}
// Recreate ChannelGroupIDs ENUM from C++
context.JK.ChannelGroupIds =
{
context.JK.ChannelGroupIds = {
"MasterGroup": 0,
"MonitorGroup": 1,
"MasterCatGroup": 2,
"MonitorCatGroup": 3,
"MasterCatGroup" : 2,
"MonitorCatGroup" : 3,
"AudioInputMusicGroup": 4,
"AudioInputChatGroup": 5,
"MediaTrackGroup": 6,
"StreamOutMusicGroup": 7,
"StreamOutChatGroup": 8,
"StreamOutMediaGroup": 9,
"StreamOutMediaGroup" : 9,
"UserMusicInputGroup": 10,
"UserChatInputGroup": 11,
"UserMediaInputGroup": 12,
@ -335,4 +333,34 @@
"JamTrackGroup": 15,
"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) {
return {positions: options.positions, offsetParent: options.offsetParent,
width:options.width,
spikeGirth: 15,
spikeLength: 20,
fill: 'white',
@ -68,13 +69,13 @@
helpBubble.jamtrackLandingPreview($preview, $preview.offsetParent())
setTimeout(function() {
helpBubble.jamtrackLandingVideo($video, $video.offsetParent())
helpBubble.jamtrackLandingVideo($video, $video.closest('.row'))
setTimeout(function() {
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
}, 1500)
}, 15000)
})
@ -101,18 +102,19 @@
}
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) {
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) {
if ($element.visible()) {
context.JK.prodBubble($element, 'jamtrack-landing-cta', {}, bigHelpOptions({positions:['left']}))
if (!$alternativeElement || $element.visible()) {
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']}))
}
}

View File

@ -10,6 +10,7 @@
var rest = new context.JK.Rest();
var _instruments = []; // will be list of structs: [ {label:xxx, value:yyy}, {...}, ... ]
var _rsvp = false;
var _noICheck = false;
if (typeof(_parentSelector)=="undefined") {_parentSelector=null}
var _parentSelector = parentSelector;
var deferredInstruments = null;
@ -109,13 +110,15 @@
return;
}
$.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')
.iCheck({
if(!_noICheck) {
$item.iCheck({
checkboxClass: 'icheckbox_minimal',
radioClass: 'iradio_minimal',
inheritClass: true
});
})
}
if (_rsvp) {
$('select[session-instrument-id="' + value.id + '"].rsvp-count', _parentSelector).val(value.count);
$('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;
_noICheck = noICheck;
// XXX; _instruments should be populated in a template, rather than round-trip to server
if(!context.JK.InstrumentSelectorDeferred) {
// 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) {
var sessionId = 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) {
var url = '/api/auths/login';
@ -507,16 +523,12 @@
function getUserProfile(options) {
var id = getId(options);
var profile = null;
if (id != null && typeof(id) != 'undefined') {
profile = $.ajax({
return $.ajax({
type: "GET",
dataType: "json",
url: "/api/users/" + id + "/profile",
processData: false
});
}
return profile;
}
function createAffiliatePartner(options) {
@ -1827,6 +1839,7 @@
this.createScheduledSession = createScheduledSession;
this.uploadMusicNotations = uploadMusicNotations;
this.getMusicNotation = getMusicNotation;
this.deleteMusicNotation = deleteMusicNotation;
this.getBroadcastNotification = getBroadcastNotification;
this.quietBroadcastNotification = quietBroadcastNotification;
this.legacyJoinSession = legacyJoinSession;
@ -1882,6 +1895,7 @@
this.addRecordingLike = addRecordingLike;
this.addPlayablePlay = addPlayablePlay;
this.getSession = getSession;
this.deleteParticipant = deleteParticipant;
this.getClientDownloads = getClientDownloads;
this.createEmailInvitations = createEmailInvitations;
this.createMusicianInvite = createMusicianInvite;

View File

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

View File

@ -226,7 +226,7 @@
var errors = JSON.parse(jqXHR.responseText);
var $errors = context.JK.format_all_errors(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) {
logger.debug("permission error sent from server:", jqXHR.responseText)

View File

@ -24,7 +24,7 @@
$.fn.metronomePlaybackMode = function(options) {
options = options || {mode: 'self'}
options = $.extend(false, {mode: 'self', positions: ['top']}, options);
return this.each(function(index) {
@ -78,8 +78,8 @@
spikeLength:0,
width:180,
closeWhenOthersOpen: true,
offsetParent: $parent.offsetParent(),
positions:['top'],
offsetParent: options.offsetParent || $parent.offsetParent(),
positions: options.positions,
preShow: function() {
$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
@pageNumber = 1
@instrument_logo_map = context.JK.getInstrumentIconMap24()
@instrumentSelector = null
init: (app) =>
@app = app
@ -23,14 +24,18 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
@screen = $('#musicians-screen')
@resultsListContainer = @screen.find('#musician-search-filter-results-list')
@spinner = @screen.find('.paginate-wait')
@instrumentSelector = new context.JK.InstrumentSelector(JK.app)
@instrumentSelector.initialize(false, true)
this.registerResultsPagination()
@screen.find('#btn-musician-search-builder').on 'click', =>
this.showBuilder()
false
@screen.find('#btn-musician-search-reset').on 'click', =>
this.resetFilter()
false
afterShow: () =>
@screen.find('#musician-search-filter-results').show()
@ -64,9 +69,11 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
@screen.find('#btn-perform-musician-search').on 'click', =>
this.performSearch()
false
@screen.find('#btn-musician-search-cancel').on 'click', =>
this.cancelFilter()
false
this._populateSkill()
this._populateStudio()
@ -86,15 +93,16 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
blankOption = $ '<option value=""></option>'
blankOption.text label
blankOption.attr 'value', value
blankOption.attr 'selected', '' if value == selection
element.append(blankOption)
element.val(selection)
context.JK.dropdown(element)
_populateSelectIdentifier: (identifier) =>
elem = $ '#musician-search-filter-builder select[name='+identifier+']'
struct = gon.musician_search_meta[identifier]['map']
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) =>
struct =
@ -125,24 +133,23 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
ages_map = gon.musician_search_meta['ages']['map']
$.each gon.musician_search_meta['ages']['keys'], (index, key) =>
ageTemplate = @screen.find('#template-search-filter-setup-ages').html()
selected = ''
ageLabel = ages_map[key]
if 0 < @searchFilter.data_blob.ages.length
key_val = key.toString()
ageMatch = $.grep(@searchFilter.data_blob.ages, (n, i) ->
n == key_val)
selected = 'checked' if ageMatch.length > 0
ageHtml = context.JK.fillTemplate(ageTemplate,
id: key
ageHtml = context._.template(ageTemplate,
{ id: key
description: ageLabel
checked: selected)
checked: selected},
{variable: 'data'})
@screen.find('#search-filter-ages').append ageHtml
_populateGenres: () =>
@screen.find('#search-filter-genres').empty()
@rest.getGenres().done (genres) =>
genreTemplate = @screen.find('#template-search-filter-setup-genres').html()
selected = ''
$.each genres, (index, genre) =>
if 0 < @searchFilter.data_blob.genres.length
genreMatch = $.grep(@searchFilter.data_blob.genres, (n, i) ->
@ -150,35 +157,19 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
else
genreMatch = []
selected = 'checked' if genreMatch.length > 0
genreHtml = context.JK.fillTemplate(genreTemplate,
id: genre.id
genreHtml = context._.template(genreTemplate,
{ id: genre.id
description: genre.description
checked: selected)
checked: selected },
{ variable: 'data' })
@screen.find('#search-filter-genres').append genreHtml
_populateInstruments: () =>
@screen.find('#search-filter-instruments').empty()
@rest.getInstruments().done (instruments) =>
$.each instruments, (index, instrument) =>
instrumentTemplate = @screen.find('#template-search-filter-setup-instrument').html()
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
# TODO hydrate user's selection from json_store
@instrumentSelector.render(@screen.find('.session-instrumentlist'), [])
@instrumentSelector.setSelectedInstruments(@searchFilter.data_blob.instruments)
_builderSelectValue: (identifier) =>
elem = $ '#musician-search-filter-builder select[name='+identifier+']'
@ -188,12 +179,7 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
vals = []
elem = $ '#search-filter-'+identifier+' input[type=checkbox]:checked'
if 'instruments' == identifier
elem.each (idx) ->
row = $(this).parent().parent()
instrument =
instrument_id: row.data('instrument-id')
proficiency_level: row.find('select').val()
vals.push instrument
vals = @instrumentSelector.getSelectedInstruments()
else
elem.each (idx) ->
vals.push $(this).val()
@ -231,18 +217,20 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
performSearch: () =>
if this.willSearch(true)
filter = {}
$.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) =>
@searchFilter[key] = this._builderSelectMultiValue(key)
@rest.postMusicianSearchFilter({ filter: JSON.stringify(@searchFilter), page: @pageNumber }).done(this.didSearch)
filter[key] = this._builderSelectMultiValue(key)
@rest.postMusicianSearchFilter({ filter: JSON.stringify(filter), page: @pageNumber }).done(this.didSearch)
renderResultsHeader: () =>
@screen.find('#musician-search-filter-description').html(@searchResults.description)
if @searchResults.is_blank_filter
@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
@screen.find('#btn-musician-search-reset').show()
@screen.find('.musician-search-text').text(@searchResults.summary)
renderMusicians: () =>
this.renderResultsHeader() if @pageNumber == 1
@ -338,20 +326,25 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
@screen.find('.search-m-message').on 'click', (evt) ->
userId = $(this).parent().data('musician-id')
objThis.app.layout.showDialog 'text-message', d1: userId
return false
_bindFriendMusician: () =>
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 0 == $(this).closest('.button-orange').size()
$self = $(evt.target)
if 0 == $self.closest('.button-orange').size()
return false
$(this).click (ee) ->
logger.debug("evt.target", evt.target)
$self.click (ee) ->
ee.preventDefault()
return
return false
evt.stopPropagation()
uid = $(this).parent().data('musician-id')
uid = $self.parent().data('musician-id')
objThis.rest.sendFriendRequest objThis.app, uid, this.friendRequestCallback
@app.notify({text: 'A friend request has been sent.'})
return false
_bindFollowMusician: () =>
objThis = this
@ -361,7 +354,7 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
return false
$(this).click (ee) ->
ee.preventDefault()
return
return false
evt.stopPropagation()
newFollowing = {}
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
# @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'
return
return false
error: objThis.app.ajaxError
return false
_formatLocation: (musician) ->
if musician.city and musician.state
@ -394,10 +388,11 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter
# TODO:
paginate: () =>
if @pageNumber < @searchResults.page_count && this.willSearch(false)
@screen.find('.paginate-wait').show()
@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
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)
*/
(function(context, $) {
"use strict";
@ -18,7 +19,7 @@
context.JK = context.JK || {};
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;
if($parentElement.length == 0) {
@ -68,23 +69,42 @@
if(endReached) {
update(0, playbackDurationMs, playbackPlaying);
}
$self.triggerHandler('play', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode});
if(options.mediaActions) {
options.mediaActions.mediaStartPlay({playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode})
}
else {
$self.triggerHandler('play', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode});
}
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
var sessionModel = context.JK.CurrentSessionModel || null;
context.JK.GA.trackJamTrackPlaySession(sessionModel.id(), true)
context.JK.GA.trackJamTrackPlaySession(context.SessionStore.id(), true)
}
}
function stopPlay(endReached) {
logger.debug("STOP PLAY CLICKED")
updateIsPlaying(false);
$self.triggerHandler('stop', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached});
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});
}
}
function pausePlay(endReached) {
updateIsPlaying(false);
$self.triggerHandler('pause', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached});
if(options.mediaActions) {
options.mediaActions.mediaPausePlay({playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached})
}
else {
$self.triggerHandler('pause', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached});
}
}
function updateOffsetBasedOnPosition(offsetLeft) {
@ -93,8 +113,13 @@
playbackPositionMs = parseInt((offsetLeft / sliderBarWidth) * playbackDurationMs);
updateCurrentTimeText(playbackPositionMs);
if(canUpdateBackend) {
if(options.mediaActions) {
options.mediaActions.mediaChangePosition({positionMs: playbackPositionMs, playbackMonitorMode: playbackMonitorMode})
}
else {
$self.triggerHandler('change-position', {positionMs: playbackPositionMs, playbackMonitorMode: playbackMonitorMode});
canUpdateBackend = false;
}
canUpdateBackend = false;
}
}
@ -127,34 +152,17 @@
}
$playButton.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($fader, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $playButton})
// return false;
//}
console.log("CLICKED PLAY")
startPlay();
return false;
});
$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();
return false;
});
$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();
return false;
});
@ -211,53 +219,61 @@
throw "unknown playbackMonitorMode: " + playbackMonitorMode;
}
}
function executeMonitor(positionMs, durationMs, isPlaying) {
if(positionMs < 0) {
// bug in backend?
positionMs = 0;
}
if(positionMs > 0) {
seenActivity = true;
}
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.METRONOME) {
updateIsPlaying(isPlaying);
}
else {
update(positionMs, durationMs, isPlaying);
}
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
if(playbackPlaying) {
$jamTrackGetReady.attr('data-current-time', positionMs)
}
else {
// this is so the jamtrack 'Get Ready!' stays hidden when it's not playing
$jamTrackGetReady.attr('data-current-time', -1)
}
}
monitorPlaybackTimeout = setTimeout(monitorRecordingPlayback, 500);
}
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
if(options.mediaActions) {
options.mediaActions.positionUpdate(playbackMonitorMode)
}
else {
var positionMs = context.jamClient.SessionCurrrentPlayPosMs();
var durationMs = context.jamClient.SessionGetTracksPlayDurationMs();
}
var isPlaying = context.jamClient.isSessionTrackPlaying();
if(positionMs < 0) {
// bug in backend?
positionMs = 0;
}
if(positionMs > 0) {
seenActivity = true;
}
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.METRONOME) {
updateIsPlaying(isPlaying);
}
else {
update(positionMs, durationMs, isPlaying);
}
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
if(playbackPlaying) {
$jamTrackGetReady.attr('data-current-time', positionMs)
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
var positionMs = context.jamClient.SessionCurrrentJamTrackPlayPosMs();
var duration = context.jamClient.SessionGetJamTracksPlayDurationMs();
var durationMs = duration.media_len;
}
else {
// this is so the jamtrack 'Get Ready!' stays hidden when it's not playing
$jamTrackGetReady.attr('data-current-time', -1)
var positionMs = context.jamClient.SessionCurrrentPlayPosMs();
var durationMs = context.jamClient.SessionGetTracksPlayDurationMs();
}
}
var isPlaying = context.jamClient.isSessionTrackPlaying();
monitorPlaybackTimeout = setTimeout(monitorRecordingPlayback, 500);
executeMonitor(positionMs, durationMs, isPlaying)
}
}
function update(currentTimeMs, durationTimeMs, isPlaying, offsetStart) {
@ -304,7 +320,11 @@
}
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) {
@ -362,6 +382,12 @@
}
function startMonitor(_playbackMonitorMode) {
logger.debug("startMonitor: " + _playbackMonitorMode)
if(monitoring && _playbackMonitorMode == playbackMonitorMode) {
return;
}
monitoring = true;
// resets everything to zero
init();
@ -376,6 +402,11 @@
logger.debug("playbackControl.startMonitor " + playbackMonitorMode + "")
styleControls();
if(monitorPlaybackTimeout != null) {
clearTimeout(monitorPlaybackTimeout);
monitorPlaybackTimeout = null;
}
monitorRecordingPlayback();
}
@ -407,6 +438,7 @@
this.setPlaybackMode = setPlaybackMode;
this.startMonitor = startMonitor;
this.stopMonitor = stopMonitor;
this.executeMonitor = executeMonitor;
this.onPlayStopEvent = onPlayStopEvent;
this.onPlayStartEvent = onPlayStartEvent;
this.onPlayPauseEvent = onPlayPauseEvent;

View File

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

View File

@ -59,19 +59,17 @@
profileUtils.gigMap = {
"": "not specified",
"0": "zero",
"1": "under 10",
"2": "10 to 50",
"3": "50 to 100",
"4": "over 100"
"0": "under 10",
"1": "10 to 50",
"2": "50 to 100",
"3": "over 100"
};
profileUtils.studioMap = {
"0": "zero",
"1": "under 10",
"2": "10 to 50",
"3": "50 to 100",
"4": "over 100"
"0": "under 10",
"1": "10 to 50",
"2": "50 to 100",
"3": "over 100"
};
profileUtils.cowritingPurposeMap = {
@ -99,6 +97,31 @@
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):
profileUtils.initializeHelpBubbles = function(parentElement) {
$(".help", parentElement).each(function( index ) {
@ -307,18 +330,25 @@
}
function formatTitle(title) {
return title && title.length > 30 ? title.substring(0, 30) + "..." : title;
return title;
}
profileUtils.renderMusicalExperience = function(player, $root) {
var $instruments = $root.find('#instruments');
profileUtils.renderMusicalExperience = function(player, $root, isOwner) {
var $instruments = $root.find('.instruments-holder');
var $musicianStatus = $root.find('#musician-status');
var $genres = $root.find('#genres');
var $concertCount = $root.find('#concert-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) {
for (var i = 0; i < player.instruments.length; i++) {
var instrument = player.instruments[i];
@ -335,7 +365,7 @@
proficiency_level_css: proficiencyCssMap[proficiency]
});
$instruments.append(instrumentHtml);
$instruments.prepend(instrumentHtml);
}
}
@ -367,16 +397,22 @@
var $youTubeSamples = $root.find('.youtube-samples');
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) {
$noSamples.show()
$jamkazamSamples.hide()
$soundCloudSamples.hide()
$youTubeSamples.hide()
if (isOwner) {
$btnAddRecordings.show();
}
} else {
$btnAddRecordings.hide();
$noSamples.hide();
// show samples section
@ -402,15 +438,15 @@
}
$.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) {
$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) {
$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
@ -425,13 +461,17 @@
var $youTubePresence = $root.find('.youtube-presence');
var $facebookPresence = $root.find('.facebook-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
var onlinePresences = player.online_presences;
if ((!onlinePresences || onlinePresences.length === 0) && !player.website) {
if (onlinePresences.length == 0 && !player.website) {
$noOnlinePresence.show()
$userWebsite.show()
$soundCloudPresence.show()
@ -441,18 +481,19 @@
$youTubePresence.show()
$facebookPresence.show()
$twitterPresence.show()
if (isOwner) {
$btnAddSites.show();
} else {
$btnAddSites.hide();
}
} else {
$btnAddSites.hide();
$noOnlinePresence.hide();
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);

View File

@ -1,3 +1,15 @@
//= require ./react-components/actions/BroadcastActions
//= require ./react-components/stores/BroadcastStore
//= require_directory ./react-components/helpers
//= 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/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