merge develop
This commit is contained in:
commit
199ef029bc
|
|
@ -13,7 +13,7 @@ ActiveAdmin.register_page "Download CSV" do
|
|||
end
|
||||
start_time = "#{start_time}"
|
||||
if end_time.blank?
|
||||
end_time = Time.now + 1.days
|
||||
end_time = (Time.now + 1.days).strftime('%F')
|
||||
else
|
||||
end_time = "#{end_time}"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ ActiveAdmin.register JamRuby::ScoreHistory, :as => 'Score History' do
|
|||
menu :parent => 'Score'
|
||||
|
||||
config.batch_actions = false
|
||||
config.sort_order = 'score_dt_desc'
|
||||
config.clear_action_items!
|
||||
config.filters = true
|
||||
config.per_page = 100
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<%= semantic_form_for([:admin, resource], :url => resource.new_record? ? admin_batch_emails_path : "/admin/batch_emails/#{resource.id}") do |f| %>
|
||||
<%= semantic_form_for([:admin, resource], :url => resource.new_record? ? admin_batch_emails_path : "#{Gon.global.prefix}/admin/batch_emails/#{resource.id}") do |f| %>
|
||||
<%= f.inputs do %>
|
||||
<%= f.input(:from_email, :label => "From Email", :input_html => {:maxlength => 64}) %>
|
||||
<%= f.input(:subject, :label => "Subject", :input_html => {:maxlength => 128}) %>
|
||||
<%= f.input(:test_emails, :label => "Test Emails", :input_html => {:maxlength => 1024, :size => '3x3'}) %>
|
||||
<%= f.input(:body, :label => "Body", :input_html => {:maxlength => 3096, :size => '10x20'}) %>
|
||||
<%= f.input(:body, :label => "Body", :input_html => {:maxlength => 60000, :size => '10x20'}) %>
|
||||
<% end %>
|
||||
<%= f.actions %>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -198,4 +198,7 @@ connection_allow_null_locidispid.sql
|
|||
track_user_in_scores.sql
|
||||
median_aggregate.sql
|
||||
current_scores_use_median.sql
|
||||
current_scores_ams_index_sms_index_use_user_instrument.sql
|
||||
locidispid_in_score_histories.sql
|
||||
define_environment_in_db.sql
|
||||
drop_session_invite_constraint.sql
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
-- this adds the user's latency, if available
|
||||
|
||||
DROP VIEW current_scores;
|
||||
CREATE OR REPLACE VIEW current_scores AS
|
||||
|
||||
SELECT * FROM (SELECT * , row_number() OVER (PARTITION BY alocidispid, blocidispid, scorer ORDER BY full_score DESC) AS pcnum FROM
|
||||
(SELECT * FROM
|
||||
(SELECT percent_rank() over (PARTITION BY alocidispid, blocidispid ORDER BY full_score ASC) AS pc, * FROM
|
||||
(SELECT tmp.*, (COALESCE(a_users.last_jam_audio_latency, 13) + COALESCE(b_users.last_jam_audio_latency, 13) + tmp.score) AS full_score, a_users.last_jam_audio_latency AS a_audio_latency, b_users.last_jam_audio_latency AS b_audio_latency FROM
|
||||
(SELECT *, row_number() OVER (PARTITION BY alocidispid, blocidispid ORDER BY scores.created_at DESC) AS rownum FROM scores) tmp
|
||||
LEFT JOIN users as a_users ON a_users.id = tmp.auserid
|
||||
LEFT JOIN users as b_users ON b_users.id = tmp.buserid
|
||||
WHERE rownum < 6) AS score_ranked)
|
||||
AS tmp2 WHERE pc <= .5 ORDER BY pc DESC) pcs )
|
||||
AS final WHERE pcnum < 2;
|
||||
|
||||
|
||||
-- check that the music_sessions does not currently have an active_music_sessions
|
||||
CREATE OR REPLACE FUNCTION sms_index (my_user_id VARCHAR, my_locidispid BIGINT, my_audio_latency INTEGER) RETURNS VOID STRICT VOLATILE AS $$
|
||||
BEGIN
|
||||
-- output table to hold tagged music sessions with latency
|
||||
CREATE TEMPORARY TABLE sms_music_session_tmp (music_session_id VARCHAR(64) NOT NULL, tag INTEGER, latency INTEGER) ON COMMIT DROP;
|
||||
|
||||
-- populate sms_music_session_tmp as all music sessions
|
||||
-- XXX: we should pass in enough info to match pagination/query to reduce the impact of this step
|
||||
INSERT INTO sms_music_session_tmp SELECT DISTINCT id, NULL::INTEGER AS tag, NULL::INTEGER AS latency
|
||||
FROM music_sessions
|
||||
WHERE (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute')))
|
||||
AND canceled = FALSE
|
||||
AND id NOT IN (SELECT id FROM active_music_sessions);
|
||||
|
||||
-- tag accepted rsvp as 1
|
||||
UPDATE sms_music_session_tmp q SET tag = 1 FROM rsvp_slots s, rsvp_requests_rsvp_slots rrs, rsvp_requests r WHERE
|
||||
q.music_session_id = s.music_session_id AND
|
||||
s.id = rrs.rsvp_slot_id AND
|
||||
rrs.rsvp_request_id = r.id AND
|
||||
r.user_id = my_user_id AND
|
||||
rrs.chosen = TRUE AND
|
||||
q.tag is NULL;
|
||||
|
||||
-- tag invitation as 2
|
||||
UPDATE sms_music_session_tmp q SET tag = 2 FROM invitations i WHERE
|
||||
q.music_session_id = i.music_session_id AND
|
||||
i.receiver_id = my_user_id AND
|
||||
q.tag IS NULL;
|
||||
|
||||
-- musician access as 3
|
||||
UPDATE sms_music_session_tmp q SET tag = 3 FROM music_sessions m WHERE
|
||||
q.music_session_id = m.id AND
|
||||
m.open_rsvps = TRUE AND
|
||||
q.tag IS NULL;
|
||||
|
||||
-- delete anything not tagged
|
||||
DELETE FROM sms_music_session_tmp WHERE tag IS NULL;
|
||||
|
||||
-- output table to hold users involved in the sms_music_session_tmp sessions and their latency
|
||||
CREATE TEMPORARY TABLE sms_users_tmp (music_session_id VARCHAR(64), user_id VARCHAR(64) NOT NULL, latency INTEGER) ON COMMIT DROP;
|
||||
|
||||
IF my_audio_latency > -1 THEN
|
||||
-- populate sms_users_tmp with users that have an approved RSVP for sessions in the sms_music_session_tmp table, accompanied with full latency and music session
|
||||
INSERT INTO sms_users_tmp SELECT q.music_session_id, users.id, s.full_score/2 AS latency
|
||||
FROM sms_music_session_tmp q
|
||||
INNER JOIN rsvp_slots ON rsvp_slots.music_session_id = q.music_session_id
|
||||
INNER JOIN rsvp_requests_rsvp_slots ON rsvp_requests_rsvp_slots.rsvp_slot_id = rsvp_slots.id
|
||||
INNER JOIN rsvp_requests ON rsvp_requests.id = rsvp_requests_rsvp_slots.rsvp_request_id
|
||||
INNER JOIN users ON rsvp_requests.user_id = users.id
|
||||
LEFT OUTER JOIN current_scores s ON s.alocidispid = users.last_jam_locidispid
|
||||
WHERE
|
||||
s.blocidispid = my_locidispid AND
|
||||
rsvp_requests_rsvp_slots.chosen = TRUE;
|
||||
|
||||
-- populate sms_users_tmp with invited users for session in the sms_music_session_tmp table, accompanied with full latency and music session
|
||||
-- specify NULL for music_session_id, because we don't want RSVP users to affect the AVG computed for each session later
|
||||
INSERT INTO sms_users_tmp SELECT NULL, users.id, s.full_score/2 AS latency
|
||||
FROM sms_music_session_tmp q
|
||||
INNER JOIN invitations ON invitations.music_session_id = q.music_session_id
|
||||
INNER JOIN users ON invitations.receiver_id = users.id
|
||||
LEFT OUTER JOIN current_scores s ON s.alocidispid = users.last_jam_locidispid
|
||||
WHERE
|
||||
s.blocidispid = my_locidispid AND
|
||||
users.id NOT IN (SELECT user_id FROM sms_users_tmp);
|
||||
END IF;
|
||||
|
||||
-- calculate the average latency
|
||||
UPDATE sms_music_session_tmp q SET latency = (select AVG(u.latency) FROM sms_users_tmp u WHERE
|
||||
q.music_session_id = u.music_session_id);
|
||||
|
||||
RETURN;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- my_audio_latency can have a special value of -1, which means 'unknown'.
|
||||
CREATE OR REPLACE FUNCTION ams_index (my_user_id VARCHAR, my_locidispid BIGINT, my_audio_latency INTEGER) RETURNS VOID STRICT VOLATILE AS $$
|
||||
BEGIN
|
||||
-- output table to hold tagged music sessions with latency
|
||||
CREATE TEMPORARY TABLE ams_music_session_tmp (music_session_id VARCHAR(64) NOT NULL, tag INTEGER, latency INTEGER) ON COMMIT DROP;
|
||||
|
||||
-- populate ams_music_session_tmp as all music sessions
|
||||
INSERT INTO ams_music_session_tmp SELECT DISTINCT id, NULL::INTEGER AS tag, NULL::INTEGER AS latency
|
||||
FROM active_music_sessions;
|
||||
|
||||
-- TODO worry about active music session where my_user_id is the creator?
|
||||
-- eh, maybe, but if the music session is active and you're the creator wouldn't you already be in it?
|
||||
-- so maybe you're on another computer, so why care? plus seth is talking about auto rsvp'ing the session
|
||||
-- for you, so maybe not a problem.
|
||||
|
||||
-- tag accepted rsvp as 1
|
||||
UPDATE ams_music_session_tmp q SET tag = 1 FROM rsvp_slots s, rsvp_requests_rsvp_slots rrs, rsvp_requests r WHERE
|
||||
q.music_session_id = s.music_session_id AND
|
||||
s.id = rrs.rsvp_slot_id AND
|
||||
rrs.rsvp_request_id = r.id AND
|
||||
r.user_id = my_user_id AND
|
||||
rrs.chosen = TRUE AND
|
||||
q.tag is NULL;
|
||||
|
||||
-- tag invitation as 2
|
||||
UPDATE ams_music_session_tmp q SET tag = 2 FROM invitations i WHERE
|
||||
q.music_session_id = i.music_session_id AND
|
||||
i.receiver_id = my_user_id AND
|
||||
q.tag IS NULL;
|
||||
|
||||
-- musician access as 3
|
||||
UPDATE ams_music_session_tmp q SET tag = 3 FROM music_sessions m WHERE
|
||||
q.music_session_id = m.id AND
|
||||
m.musician_access = TRUE AND
|
||||
q.tag IS NULL;
|
||||
|
||||
-- delete anything not tagged
|
||||
DELETE FROM ams_music_session_tmp WHERE tag IS NULL;
|
||||
|
||||
-- output table to hold users involved in the ams_music_session_tmp sessions and their latency
|
||||
CREATE TEMPORARY TABLE ams_users_tmp (music_session_id VARCHAR(64), user_id VARCHAR(64) NOT NULL, latency INTEGER) ON COMMIT DROP;
|
||||
|
||||
IF my_audio_latency > -1 THEN
|
||||
-- populate ams_users_tmp with users that have a connection for sessions in the ams_music_session_tmp table, accompanied with full latency and music session
|
||||
INSERT INTO ams_users_tmp SELECT c.music_session_id, c.user_id, s.full_score/2 AS latency
|
||||
FROM ams_music_session_tmp q
|
||||
INNER JOIN connections c ON c.music_session_id = q.music_session_id
|
||||
LEFT OUTER JOIN current_scores s ON s.alocidispid = c.locidispid
|
||||
WHERE s.blocidispid = my_locidispid;
|
||||
|
||||
-- populate ams_users_tmp with users that have an approved RSVP for sessions inthe ams_music_session_tmp table, accompanied with full latency and music session
|
||||
-- specify NULL for music_session_id, because we don't want RSVP users to affect the AVG computed for each session later
|
||||
INSERT INTO ams_users_tmp SELECT NULL, users.id, s.full_score/2 AS latency
|
||||
FROM ams_music_session_tmp q
|
||||
INNER JOIN rsvp_slots ON rsvp_slots.music_session_id = q.music_session_id
|
||||
INNER JOIN rsvp_requests_rsvp_slots ON rsvp_requests_rsvp_slots.rsvp_slot_id = rsvp_slots.id
|
||||
INNER JOIN rsvp_requests ON rsvp_requests.id = rsvp_requests_rsvp_slots.rsvp_request_id
|
||||
INNER JOIN users ON rsvp_requests.user_id = users.id
|
||||
LEFT OUTER JOIN current_scores s ON s.alocidispid = users.last_jam_locidispid
|
||||
WHERE
|
||||
s.blocidispid = my_locidispid AND
|
||||
rsvp_requests_rsvp_slots.chosen = TRUE AND
|
||||
users.id NOT IN (SELECT user_id FROM ams_users_tmp);
|
||||
END IF;
|
||||
|
||||
-- calculate the average latency
|
||||
UPDATE ams_music_session_tmp q SET latency = (select AVG(u.latency) FROM ams_users_tmp u WHERE
|
||||
q.music_session_id = u.music_session_id);
|
||||
|
||||
RETURN;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
-- https://jamkazam.atlassian.net/browse/VRFS-1951
|
||||
-- we need to know the environment in the database for advanced behaviors
|
||||
ALTER TABLE generic_state ADD COLUMN env VARCHAR(255) NOT NULL DEFAULT 'development';
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
-- https://jamkazam.atlassian.net/browse/VRFS-1968
|
||||
-- store both locids in score_histories
|
||||
|
||||
ALTER TABLE score_histories ADD COLUMN from_locidispid BIGINT NOT NULL;
|
||||
ALTER TABLE score_histories ADD COLUMN to_locidispid BIGINT NOT NULL;
|
||||
|
|
@ -86,10 +86,10 @@ CREATE OR REPLACE FUNCTION generate_scores_dataset () RETURNS VOID STRICT VOLATI
|
|||
INSERT INTO cities (city, region, countrycode) select distinct city, region, countrycode from geoiplocations where length(city) > 0 and length(countrycode) > 0;
|
||||
|
||||
DELETE FROM regions;
|
||||
INSERT INTO regions (region, countrycode) select distinct region, countrycode from cities;
|
||||
INSERT INTO regions (region, regionname, countrycode) select distinct region, region, countrycode from cities;
|
||||
|
||||
DELETE FROM countries;
|
||||
INSERT INTO countries (countrycode) select distinct countrycode from regions;
|
||||
INSERT INTO countries (countrycode, countryname) select distinct countrycode, countrycode from regions;
|
||||
|
||||
END IF;
|
||||
RETURN;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ describe "Deployed site at #{www}", :js => true, :type => :feature, :capybara_fe
|
|||
Capybara.run_server = false
|
||||
end
|
||||
|
||||
before { puts "\n" }
|
||||
after { puts "\n\n" }
|
||||
|
||||
TestUser = Class.new do
|
||||
attr_accessor :email, :password, :first_name, :last_name, :id
|
||||
|
||||
|
|
@ -46,6 +49,7 @@ describe "Deployed site at #{www}", :js => true, :type => :feature, :capybara_fe
|
|||
|
||||
it "is possible for #{user1} to sign in and not get disconnected within 30 seconds" do
|
||||
in_client(user1) do
|
||||
puts "\n *** #{user1}'s client *** \n"
|
||||
sign_in_poltergeist user1
|
||||
repeat_for(30.seconds) do
|
||||
expect(page).to_not have_selector('.no-websocket-connection') #looks for reconnect dialog every 1 second
|
||||
|
|
@ -57,6 +61,7 @@ describe "Deployed site at #{www}", :js => true, :type => :feature, :capybara_fe
|
|||
# this example heavily based on text_message_spec.rb in 'web'
|
||||
|
||||
in_client(user1) do
|
||||
puts "\n *** #{user1}'s client *** \n"
|
||||
sign_in_poltergeist(user1)
|
||||
end
|
||||
|
||||
|
|
@ -65,6 +70,7 @@ describe "Deployed site at #{www}", :js => true, :type => :feature, :capybara_fe
|
|||
test_goodbye = "#{SecureRandom.uuid} - OK bye!"
|
||||
|
||||
in_client(user2) do
|
||||
puts "\n *** #{user2}'s client *** \n"
|
||||
sign_in_poltergeist(user2)
|
||||
expect(page).to have_xpath(
|
||||
"//div[@class='friend-name' and @user-id='#{user1.id}']/span[@class='friend-status']",
|
||||
|
|
@ -78,6 +84,7 @@ describe "Deployed site at #{www}", :js => true, :type => :feature, :capybara_fe
|
|||
end
|
||||
|
||||
in_client(user1) do
|
||||
puts "\n *** #{user1}'s client *** \n"
|
||||
expect(page).to have_xpath(
|
||||
"//div[@class='friend-name' and @user-id='#{user2.id}']/span[@class='friend-status']",
|
||||
:text => "Available" )
|
||||
|
|
@ -88,12 +95,13 @@ describe "Deployed site at #{www}", :js => true, :type => :feature, :capybara_fe
|
|||
end
|
||||
|
||||
in_client(user2) do
|
||||
puts "\n *** #{user2}'s client *** \n"
|
||||
find('.previous-message-text', text: test_response)
|
||||
send_text_message(test_goodbye, close_on_send: true)
|
||||
end
|
||||
|
||||
in_client(user1) { sign_out_poltergeist }
|
||||
in_client(user2) { sign_out_poltergeist }
|
||||
in_client(user1) { puts "\n *** #{user1}'s client *** \n"; sign_out_poltergeist }
|
||||
in_client(user2) { puts "\n *** #{user2}'s client *** \n"; sign_out_poltergeist }
|
||||
end
|
||||
|
||||
let(:queue_limit) { 5 }
|
||||
|
|
|
|||
|
|
@ -34,11 +34,11 @@ RSpec.configure do |config|
|
|||
end
|
||||
end
|
||||
|
||||
#Capybara.register_driver :poltergeist do |app|
|
||||
# Capybara::Poltergeist::Driver.new(app, { phantomjs_logger: File.open('console.log', 'w') })
|
||||
#end
|
||||
Capybara.javascript_driver = :poltergeist
|
||||
Capybara.default_driver = :poltergeist
|
||||
Capybara.register_driver :poltergeist do |app|
|
||||
Capybara::Poltergeist::Driver.new(app, { :timeout=>120, js_errors: false })
|
||||
end
|
||||
#Capybara.javascript_driver = :poltergeist
|
||||
#Capybara.default_driver = :poltergeist
|
||||
Capybara.run_server = false # since we're testing an app outside this project
|
||||
Capybara.default_wait_time = 15 # ^^ ditto
|
||||
|
||||
|
|
|
|||
|
|
@ -320,6 +320,7 @@ message SessionJoin {
|
|||
optional string photo_url = 2;
|
||||
optional string msg = 3;
|
||||
optional int32 track_changes_counter = 4;
|
||||
optional string source_user_id = 5;
|
||||
}
|
||||
|
||||
message SessionDepart {
|
||||
|
|
|
|||
|
|
@ -3,35 +3,90 @@
|
|||
<p>Hello <%= @user.first_name %> --
|
||||
</p>
|
||||
|
||||
<p>The following new sessions that that have been posted during the last 24 hours:
|
||||
</p>
|
||||
<ol>
|
||||
<li>Need someone who plays an instrument that you play</li>
|
||||
<li>Were posted by someone to whom you have either a good or medium latency connection</li>
|
||||
</ol>
|
||||
<p>The following new sessions have been posted within the last 24 hours, and you have good or acceptable latency to the organizer of each session below. If a session looks interesting, click the Details link to see the session page. You can RSVP to a session from the session page, and you'll be notified if/when the session organizer approves your RSVP.</p>
|
||||
|
||||
<p>Take a look through these new sessions below, and just click the RSVP button on the far right side of the row for any session in which you'd like to play. This will let the session organizer know you're interested, and you'll be notified if the session organizer accepts your request to play in that session!
|
||||
</p>
|
||||
<style>
|
||||
#scheduled-sessions {
|
||||
margin-top:15px;
|
||||
width:98%;
|
||||
font-size:11px;
|
||||
color:#fff;
|
||||
background-color:#262626;
|
||||
border-color:#cccccc;
|
||||
border-width:1px 0;
|
||||
border-style:solid;
|
||||
}
|
||||
|
||||
<table style="margin-top:6px; width:98%; font-size:11px; color:#fff; background-color:#262626; border:solid 1px #4d4d4d;" cellspacing="0" cellpadding="0" border="0">
|
||||
#scheduled-sessions td {
|
||||
padding:10px 2px;
|
||||
}
|
||||
|
||||
#scheduled-sessions th {
|
||||
padding:2px;
|
||||
}
|
||||
|
||||
#scheduled-sessions thead {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
|
||||
#scheduled-sessions thead th {
|
||||
|
||||
border-width:0 0 1px 0;
|
||||
border-style:solid;
|
||||
border-color:#cccccc;
|
||||
}
|
||||
#scheduled-sessions tbody td {
|
||||
|
||||
border-width:1px 0 0 0;
|
||||
border-style:solid;
|
||||
border-color:#4d4d4d;
|
||||
vertical-align:top;
|
||||
}
|
||||
|
||||
#scheduled-sessions span.latency img {
|
||||
margin-top:2px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<table id="scheduled-sessions" cellspacing="0" cellpadding="0" border="0">
|
||||
<!-- header -->
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left" width="20%">GENRE</th>
|
||||
<th align="left" width="60%">DESCRIPTION</th>
|
||||
<th align="left" width="15%">GENRE</th>
|
||||
<th align="left" width="20%">NAME</th>
|
||||
<th align="left" width="45%">DESCRIPTION</th>
|
||||
<th width="20%" style="text-align:center">LATENCY</th>
|
||||
</tr>
|
||||
<!-- session row goes here -->
|
||||
<% @sessions_and_latency.each do |sess| %>
|
||||
<tr>
|
||||
<td><%= sess.genre.description %></td>
|
||||
<td><%= sess.description %></td>
|
||||
<td style="text-align:center"><%= sess.latency %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- session row goes here -->
|
||||
<% @sessions_and_latency.each do |sess| %>
|
||||
<tr>
|
||||
<td><%= sess.genre.description %></td>
|
||||
<td>
|
||||
<%= sess.name %><br/>
|
||||
<a href="<%= "http://www.jamkazam.com/sessions/#{sess.id}/details" %>">Details</a>
|
||||
</td>
|
||||
<td><%= sess.description %></td>
|
||||
<td style="text-align:center">
|
||||
<span class="latency">
|
||||
<span class="latency-value"><%= (sess.latency / 2).round %> ms</span>
|
||||
<% if sess.latency <= (APP_CONFIG.max_good_full_score * 2) %>
|
||||
<%= image_tag("http://www.jamkazam.com/assets/content/icon_green_score.png", alt: 'good score icon') %>
|
||||
<% else %>
|
||||
<%= image_tag("http://www.jamkazam.com/assets/content/icon_yellow_score.png", alt: 'fair score icon') %>
|
||||
<% end %>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>To see ALL the scheduled sessions that you might be interested in joining, view our Find Session page at: <a href="http://www.jamkazam.com/client#/findSession">http://www.jamkazam.com/client#/findSession</a>.
|
||||
</p>
|
||||
<p>To see ALL the scheduled sessions that you might be interested in joining, view our <a href="http://www.jamkazam.com/client#/findSession">Find Session page</a>.</p>
|
||||
|
||||
<p>Best Regards,</p>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,16 +2,11 @@
|
|||
|
||||
Hello <%= @user.first_name %> --
|
||||
|
||||
The following new sessions that that have been posted during the last 24 hours:
|
||||
The following new sessions have been posted within the last 24 hours, and you have good or acceptable latency to the organizer of each session below. If a session looks interesting, click the Details link to see the session page. You can RSVP to a session from the session page, and you'll be notified if/when the session organizer approves your RSVP.
|
||||
|
||||
1. Need someone who plays an instrument that you play
|
||||
2. Were posted by someone to whom you have either a good or medium latency connection
|
||||
|
||||
Take a look through these new sessions below, and just click the RSVP button on the far right side of the row for any session in which you'd like to play. This will let the session organizer know you're interested, and you'll be notified if the session organizer accepts your request to play in that session!
|
||||
|
||||
GENRE | DESCRIPTION | LATENCY
|
||||
GENRE | NAME | DESCRIPTION | LATENCY
|
||||
<% @sessions_and_latency.each do |sess| %>
|
||||
<%= sess.genre.description %> | <%= sess.description %> | <%= sess.latency %>
|
||||
<%= sess.genre.description %> | <%= sess.name %> | <%= sess.description %> | <%= sess.latency %> ms
|
||||
<% end %>
|
||||
|
||||
To see ALL the scheduled sessions that you might be interested in joining, view our Find Session page at: http://www.jamkazam.com/client#/findSession.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
margin-bottom:0px;
|
||||
line-height:140%;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
|
|
|||
|
|
@ -54,5 +54,35 @@ module JamRuby
|
|||
GeoIpBlocks.connection.execute "CREATE TABLE #{copied_name} (LIKE #{table_name} INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE)"
|
||||
copied_name
|
||||
end
|
||||
|
||||
def self.dump(query)
|
||||
result = ActiveRecord::Base.connection.execute(query)
|
||||
fields = result.fields
|
||||
header = ""
|
||||
fields.each do |field|
|
||||
header << field
|
||||
header << ', '
|
||||
end
|
||||
header = header[0..header.length - 3]
|
||||
puts header
|
||||
|
||||
result.each do |row|
|
||||
row_out = ''
|
||||
fields.each do |field|
|
||||
if row[field].nil?
|
||||
row_out << 'NULL'
|
||||
else
|
||||
if block_given?
|
||||
row_out << yield(field, row[field]).to_s
|
||||
else
|
||||
row_out << row[field].to_s
|
||||
end
|
||||
end
|
||||
row_out << ', '
|
||||
end
|
||||
row_out = row_out[0..row_out.length - 3]
|
||||
puts row_out
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -395,10 +395,11 @@ module JamRuby
|
|||
)
|
||||
end
|
||||
|
||||
def session_join(session_id, photo_url, msg, track_changes_counter)
|
||||
def session_join(session_id, photo_url, source_user_id, msg, track_changes_counter)
|
||||
join = Jampb::SessionJoin.new(
|
||||
:session_id => session_id,
|
||||
:photo_url => photo_url,
|
||||
:source_user_id => source_user_id,
|
||||
:msg => msg,
|
||||
:track_changes_counter => track_changes_counter
|
||||
)
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ module JamRuby
|
|||
[music_sessions, user_scores]
|
||||
end
|
||||
|
||||
def self.participant_create user, music_session_id, client_id, as_musician, tracks
|
||||
def self.participant_create user, music_session_id, client_id, as_musician, tracks, audio_latency
|
||||
music_session = MusicSession.find(music_session_id)
|
||||
|
||||
if music_session.active_music_session
|
||||
|
|
@ -462,7 +462,7 @@ module JamRuby
|
|||
|
||||
active_music_session.with_lock do # VRFS-1297
|
||||
active_music_session.tick_track_changes
|
||||
connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, 10)
|
||||
connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, audio_latency)
|
||||
|
||||
if connection.errors.any?
|
||||
# rollback the transaction to make sure nothing is disturbed in the database
|
||||
|
|
@ -519,7 +519,7 @@ module JamRuby
|
|||
|
||||
# auto-join this user into the newly created session
|
||||
as_musician = true
|
||||
connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, 10)
|
||||
connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, audio_latency)
|
||||
|
||||
unless connection.errors.any?
|
||||
user.update_progression_field(:first_music_session_at)
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ module JamRuby
|
|||
# if user joins the session as a musician, update their addr and location
|
||||
if as_musician
|
||||
user.update_addr_loc(self, User::JAM_REASON_JOIN)
|
||||
user.update_audio_latency(self, audio_latency)
|
||||
user.update_audio_latency(self, audio_latency) if audio_latency # try not to let a previously recorded value get nil'ed
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -19,16 +19,45 @@ module JamRuby
|
|||
def persisted?; false; end
|
||||
end
|
||||
|
||||
# Temporary Tables created by this class:
|
||||
|
||||
# tmp_candidate_sessions
|
||||
# ----------------------
|
||||
#
|
||||
# These are 'open' sessions that have any open slots left, and fall within a certain start time.
|
||||
# The session creator must also have a locidispid.
|
||||
#
|
||||
# session_id - music_session.id
|
||||
# creator_id - music_session.user_id
|
||||
# creator_score_idx - this is the creator's users.last_jam_locidispid
|
||||
# instrument_id - instruments that are open as gleamed from the RSVP. If this is NULL, it means 'ANY INSTRUMENT'
|
||||
# invited_user_id - the ID of a user who was invited. Can be NULL.
|
||||
#
|
||||
# tmp_candidate_recipients
|
||||
# ------------------------
|
||||
#
|
||||
# These are musicians, that allow email notifications, that have an instrument which matches the session's open RSVP slot's instrument.
|
||||
# The musician must also have a locidispid.
|
||||
#
|
||||
# receiver_id - user ID that could be in the session
|
||||
# receiver_score_idx - the user's last_jam_locidispid
|
||||
# instrument_id - the user's matching instrument for a open session slot. If this is NULL, it means 'ANY INSTRUMENT'
|
||||
# invited_user_id
|
||||
#
|
||||
# tmp_matches
|
||||
# -----------
|
||||
#
|
||||
# These are 'candidate_recipients' that have a decent enough score with the creator of the music sessions in tmp_candidate_sessions
|
||||
#
|
||||
# receiver_id - the user.id that should receive an Daily Session email
|
||||
# session_id - the music_session.id for the email
|
||||
# latency - the score.score between the creator and the candidate (needs to be full score soon)
|
||||
class EmailBatchScheduledSessions < EmailBatchPeriodic
|
||||
|
||||
BATCH_SIZE = 500
|
||||
SINCE_DAYS = 2
|
||||
MIN_HOURS_START = 2
|
||||
|
||||
TMP_SESS = 'tmp_candidate_sessions'
|
||||
TMP_RECIP = 'tmp_candidate_recipients'
|
||||
TMP_MATCH = 'tmp_matches'
|
||||
|
||||
ENV_MAX_LATENCY = 'env_max_latency'
|
||||
ENV_QUERY_LIMIT = 'env_query_limit'
|
||||
SNAPSHOT_QUERY_LIMIT = '500'
|
||||
|
|
@ -57,18 +86,18 @@ module JamRuby
|
|||
end
|
||||
|
||||
def snapshot_eligible_sessions
|
||||
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM #{TMP_SESS}")
|
||||
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM #{TMP_SESS}")]
|
||||
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_candidate_sessions")
|
||||
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM tmp_candidate_sessions")]
|
||||
end
|
||||
|
||||
def snapshot_eligible_recipients
|
||||
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM #{TMP_RECIP}")
|
||||
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM #{TMP_RECIP}")]
|
||||
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_candidate_recipients")
|
||||
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM tmp_candidate_recipients")]
|
||||
end
|
||||
|
||||
def snapshot_scored_recipients
|
||||
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM #{TMP_MATCH}")
|
||||
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM #{TMP_MATCH}")]
|
||||
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_matches")
|
||||
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM tmp_matches")]
|
||||
end
|
||||
|
||||
def take_snapshot
|
||||
|
|
@ -77,6 +106,62 @@ module JamRuby
|
|||
self.update_attribute(:test_emails, @counters.inspect)
|
||||
end
|
||||
|
||||
def query
|
||||
ActiveRecord::Base.connection.execute(<<SQL
|
||||
SELECT DISTINCT
|
||||
users.id AS receiver_id,
|
||||
music_sessions.id AS session_id,
|
||||
current_scores.full_score AS latency
|
||||
FROM current_scores
|
||||
INNER JOIN
|
||||
(SELECT
|
||||
msess.id AS session_id,
|
||||
msess.user_id AS creator_id,
|
||||
users.last_jam_locidispid AS creator_score_idx,
|
||||
rs.instrument_id,
|
||||
invitations.receiver_id AS invited_user_id,
|
||||
msess.is_unstructured_rsvp
|
||||
FROM music_sessions msess
|
||||
INNER JOIN users ON users.id = msess.user_id
|
||||
INNER JOIN rsvp_slots AS rs ON rs.music_session_id = msess.id
|
||||
LEFT JOIN rsvp_requests_rsvp_slots AS rrrs ON rrrs.rsvp_slot_id = rs.id
|
||||
LEFT JOIN invitations ON invitations.music_session_id = msess.id
|
||||
WHERE
|
||||
open_rsvps = TRUE OR invitations.id IS NOT NULL AND
|
||||
users.last_jam_locidispid IS NOT NULL AND
|
||||
msess.created_at > '#{earliest_session_create_time}' AND
|
||||
msess.created_at < '#{latest_session_create_time}' AND
|
||||
scheduled_start >= '#{earliest_session_start_time}' AND
|
||||
(rrrs.rsvp_slot_id IS NULL OR rrrs.chosen != TRUE)) AS tmp_candidate_sessions
|
||||
INNER JOIN
|
||||
(SELECT
|
||||
users.id AS receiver_id,
|
||||
users.last_jam_locidispid AS receiver_score_idx,
|
||||
mi.instrument_id
|
||||
INTO TEMP TABLE tmp_candidate_recipients
|
||||
FROM users
|
||||
INNER JOIN musicians_instruments AS mi ON mi.user_id = users.id
|
||||
INNER JOIN tmp_candidate_sessions ON tmp_candidate_sessions.instrument_id = mi.instrument_id OR
|
||||
tmp_candidate_sessions.is_unstructured_rsvp = TRUE OR
|
||||
tmp_candidate_sessions.invited_user_id = users.id
|
||||
|
||||
WHERE
|
||||
users.last_jam_locidispid IS NOT NULL AND
|
||||
users.musician = TRUE AND
|
||||
users.subscribe_email = TRUE) AS tmp_candidate_recipients
|
||||
INNER JOIN tmp_candidate_sessions ON tmp_candidate_sessions.creator_score_idx = current_scores.alocidispid
|
||||
INNER JOIN tmp_candidate_recipients ON tmp_candidate_recipients.receiver_score_idx = current_scores.blocidispid
|
||||
WHERE
|
||||
current_scores.full_score < #{max_score} AND
|
||||
tmp_candidate_recipients.receiver_id != tmp_candidate_sessions.creator_id
|
||||
GROUP BY
|
||||
tmp_candidate_recipients.receiver_id,
|
||||
tmp_candidate_sessions.session_id,
|
||||
latency
|
||||
SQL
|
||||
)
|
||||
end
|
||||
|
||||
def fetch_recipients(per_page=BATCH_SIZE)
|
||||
objs = []
|
||||
|
||||
|
|
@ -91,9 +176,11 @@ module JamRuby
|
|||
# now just get the sessions/latency for each distinct mail recipient
|
||||
_select_scored_recipients(offset).each do |result|
|
||||
receiver = User.find_by_id(result['receiver_id'])
|
||||
sessions = MusicSession.select("music_sessions.*, #{TMP_MATCH}.latency")
|
||||
.joins("INNER JOIN #{TMP_MATCH} ON #{TMP_MATCH}.session_id = music_sessions.id")
|
||||
.where(["#{TMP_MATCH}.receiver_id = ?", receiver.id])
|
||||
sessions = MusicSession.select("music_sessions.*, tmp_matches.latency")
|
||||
.joins("INNER JOIN tmp_matches ON tmp_matches.session_id = music_sessions.id")
|
||||
.where(["tmp_matches.receiver_id = ?", receiver.id])
|
||||
.order('tmp_matches.latency')
|
||||
.limit(20)
|
||||
.includes([:genre, :creator])
|
||||
block_given? ? yield(receiver, sessions) : objs << [receiver, sessions]
|
||||
end
|
||||
|
|
@ -126,75 +213,83 @@ module JamRuby
|
|||
|
||||
# inserts eligible sessions to temp table
|
||||
def _collect_eligible_sessions
|
||||
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{TMP_SESS}")
|
||||
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS tmp_candidate_sessions")
|
||||
limit_sql = (self.snapshot? && 0 < ENV[ENV_QUERY_LIMIT].to_i) ? "LIMIT #{ENV[ENV_QUERY_LIMIT]}" : ''
|
||||
sql =<<SQL
|
||||
SELECT
|
||||
msess.id AS session_id,
|
||||
msess.user_id AS creator_id,
|
||||
users.last_jam_locidispid AS creator_score_idx,
|
||||
rs.instrument_id
|
||||
INTO TEMP TABLE #{TMP_SESS}
|
||||
rs.instrument_id,
|
||||
invitations.receiver_id AS invited_user_id,
|
||||
msess.is_unstructured_rsvp,
|
||||
msess.open_rsvps
|
||||
INTO TEMP TABLE tmp_candidate_sessions
|
||||
FROM music_sessions msess
|
||||
INNER JOIN users ON users.id = msess.user_id
|
||||
INNER JOIN rsvp_slots AS rs ON rs.music_session_id = msess.id
|
||||
LEFT JOIN rsvp_requests_rsvp_slots AS rrrs ON rrrs.rsvp_slot_id = rs.id
|
||||
LEFT JOIN invitations ON open_rsvps = FALSE AND invitations.music_session_id = msess.id
|
||||
WHERE
|
||||
musician_access = 't' AND
|
||||
approval_required = 'f' AND
|
||||
(msess.is_unstructured_rsvp = TRUE OR (rrrs.id IS NULL OR rrrs.chosen != TRUE)) AND
|
||||
users.last_jam_locidispid IS NOT NULL AND
|
||||
msess.created_at > '#{earliest_session_create_time}' AND
|
||||
msess.created_at < '#{latest_session_create_time}' AND
|
||||
scheduled_start >= '#{earliest_session_start_time}' AND
|
||||
(rrrs.rsvp_slot_id IS NULL OR rrrs.chosen != 't')
|
||||
scheduled_start >= '#{earliest_session_start_time}'
|
||||
#{limit_sql}
|
||||
SQL
|
||||
ActiveRecord::Base.connection.execute(sql)
|
||||
end
|
||||
|
||||
def _collect_eligible_recipients
|
||||
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{TMP_RECIP}")
|
||||
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS tmp_candidate_recipients").check
|
||||
limit_sql = (self.snapshot? && 0 < ENV[ENV_QUERY_LIMIT].to_i) ? "LIMIT #{ENV[ENV_QUERY_LIMIT]}" : ''
|
||||
# load eligible recipients into tmp table
|
||||
sql =<<SQL
|
||||
SELECT
|
||||
users.id AS receiver_id,
|
||||
users.last_jam_locidispid AS receiver_score_idx,
|
||||
mi.instrument_id
|
||||
INTO TEMP TABLE #{TMP_RECIP}
|
||||
mi.instrument_id,
|
||||
tmp_candidate_sessions.invited_user_id,
|
||||
tmp_candidate_sessions.session_id AS session_id,
|
||||
tmp_candidate_sessions.creator_score_idx AS creator_score_idx
|
||||
INTO TEMP TABLE tmp_candidate_recipients
|
||||
FROM users
|
||||
INNER JOIN musicians_instruments AS mi ON mi.user_id = users.id
|
||||
INNER JOIN #{TMP_SESS} ON #{TMP_SESS}.instrument_id = mi.instrument_id
|
||||
INNER JOIN tmp_candidate_sessions ON tmp_candidate_sessions.is_unstructured_rsvp = TRUE OR
|
||||
(tmp_candidate_sessions.open_rsvps = TRUE AND tmp_candidate_sessions.instrument_id = mi.instrument_id) OR
|
||||
tmp_candidate_sessions.invited_user_id = users.id
|
||||
WHERE
|
||||
users.last_jam_locidispid IS NOT NULL AND
|
||||
users.musician = 't' AND
|
||||
users.subscribe_email = 't'
|
||||
users.musician = TRUE AND
|
||||
users.subscribe_email = TRUE AND
|
||||
users.id != tmp_candidate_sessions.creator_id
|
||||
#{limit_sql}
|
||||
SQL
|
||||
ActiveRecord::Base.connection.execute(sql)
|
||||
end
|
||||
|
||||
def _collect_scored_recipients
|
||||
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{TMP_MATCH}")
|
||||
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS tmp_matches")
|
||||
if !self.snapshot? || 0 == (max_score = ENV[ENV_MAX_LATENCY].to_i)
|
||||
max_score = Score::MAX_YELLOW_LATENCY
|
||||
max_score = APP_CONFIG.max_yellow_full_score * 2
|
||||
end
|
||||
limit_sql = (self.snapshot? && 0 < ENV[ENV_QUERY_LIMIT].to_i) ? "LIMIT #{ENV[ENV_QUERY_LIMIT]}" : ''
|
||||
sql =<<SQL
|
||||
SELECT
|
||||
DISTINCT #{TMP_RECIP}.receiver_id,
|
||||
#{TMP_SESS}.session_id,
|
||||
scores.score AS latency
|
||||
INTO TEMP TABLE #{TMP_MATCH}
|
||||
FROM scores
|
||||
INNER JOIN #{TMP_SESS} ON #{TMP_SESS}.creator_score_idx = scores.alocidispid
|
||||
INNER JOIN #{TMP_RECIP} ON #{TMP_RECIP}.receiver_score_idx = scores.blocidispid
|
||||
SELECT DISTINCT
|
||||
tmp_candidate_recipients.receiver_id,
|
||||
tmp_candidate_recipients.session_id,
|
||||
current_scores.full_score AS latency
|
||||
INTO TEMP TABLE tmp_matches
|
||||
FROM current_scores
|
||||
INNER JOIN tmp_candidate_recipients ON
|
||||
tmp_candidate_recipients.creator_score_idx = current_scores.alocidispid AND
|
||||
tmp_candidate_recipients.receiver_score_idx = current_scores.blocidispid
|
||||
WHERE
|
||||
scores.score < #{max_score} AND
|
||||
#{TMP_RECIP}.receiver_id != #{TMP_SESS}.creator_id
|
||||
GROUP BY
|
||||
#{TMP_RECIP}.receiver_id,
|
||||
#{TMP_SESS}.session_id,
|
||||
current_scores.full_score < #{max_score}
|
||||
GROUP BY
|
||||
tmp_candidate_recipients.receiver_id,
|
||||
tmp_candidate_recipients.session_id,
|
||||
latency
|
||||
#{limit_sql}
|
||||
SQL
|
||||
|
|
@ -204,13 +299,13 @@ SQL
|
|||
# select recipients whose score is below minimum threshold
|
||||
def _select_scored_recipients(offset=0)
|
||||
if 0 > offset
|
||||
sql = "SELECT COUNT(DISTINCT receiver_id) AS num FROM #{TMP_MATCH}"
|
||||
sql = "SELECT COUNT(DISTINCT receiver_id) AS num FROM tmp_matches"
|
||||
rr = ActiveRecord::Base.connection.execute(sql)
|
||||
return 0 < rr.count ? rr[0]['num'].to_i : 0
|
||||
else
|
||||
sql =<<SQL
|
||||
SELECT DISTINCT receiver_id
|
||||
FROM #{TMP_MATCH}
|
||||
FROM tmp_matches
|
||||
ORDER BY receiver_id ASC
|
||||
LIMIT #{@per_page}
|
||||
OFFSET #{offset}
|
||||
|
|
@ -234,11 +329,11 @@ SQL
|
|||
return @counters if @counters || !self.snapshot?
|
||||
_load_recipients if load_tmp_tables
|
||||
|
||||
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM #{TMP_SESS}")
|
||||
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_candidate_sessions")
|
||||
session_count = 0 < rr.count ? rr[0]['num'].to_i : 0
|
||||
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM #{TMP_RECIP}")
|
||||
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_candidate_recipients")
|
||||
receiver_candidate_count = 0 < rr.count ? rr[0]['num'].to_i : 0
|
||||
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM #{TMP_MATCH}")
|
||||
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_matches")
|
||||
receiver_match_count = 0 < rr.count ? rr[0]['num'].to_i : 0
|
||||
|
||||
@counters = {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,21 @@ module JamRuby
|
|||
|
||||
self.table_name = 'generic_state'
|
||||
|
||||
validates :env, :inclusion => {:in => ['development', 'staging', 'production', 'test']}
|
||||
|
||||
def self.allow_emails?
|
||||
|
||||
database_environment = singleton.env
|
||||
|
||||
# if the database says we are in production/staging, then the config has to also agree and say we are in production/staging to send emails
|
||||
# this is to protect against developers loading a staging or production environment and possibly sending emails to
|
||||
# we even go one step further, and make sure ENV['BUILD_NUMBER'] is set, which is something you do in production, but would be very rare in development
|
||||
|
||||
# or if your database says 'development' and config say 'development', then we allow emails to go out too
|
||||
(!ENV['BUILD_NUMBER'].nil? && (Environment.mode == 'production' || Environment.mode == 'staging') && (database_environment == 'production' || database_environment == 'staging')) ||
|
||||
(database_environment == 'development' && Environment.mode == 'development')
|
||||
end
|
||||
|
||||
def self.singleton
|
||||
GenericState.find('default')
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ module JamRuby
|
|||
|
||||
RECURRING_MODES = [NO_RECURRING, RECURRING_WEEKLY]
|
||||
|
||||
attr_accessor :legal_terms, :language_description, :access_description
|
||||
attr_accessor :legal_terms, :language_description, :access_description, :scheduling_info_changed
|
||||
|
||||
attr_accessor :approved_rsvps, :open_slots, :pending_invitations
|
||||
|
||||
|
|
@ -58,11 +58,17 @@ module JamRuby
|
|||
before_create :generate_share_token
|
||||
before_create :add_to_feed
|
||||
#before_save :update_scheduled_start
|
||||
before_save :check_scheduling_info_changed
|
||||
|
||||
SHARE_TOKEN_LENGTH = 8
|
||||
|
||||
SEPARATOR = '|'
|
||||
|
||||
def check_scheduling_info_changed
|
||||
@scheduling_info_changed = scheduled_start_changed?
|
||||
true
|
||||
end
|
||||
|
||||
def add_to_feed
|
||||
feed = Feed.new
|
||||
feed.music_session = self
|
||||
|
|
@ -111,6 +117,7 @@ module JamRuby
|
|||
new_slot = RsvpSlot.new
|
||||
new_slot.instrument_id = slot.instrument_id
|
||||
new_slot.proficiency_level = slot.proficiency_level
|
||||
new_slot.is_unstructured_rsvp = slot.is_unstructured_rsvp
|
||||
new_session.rsvp_slots << new_slot
|
||||
|
||||
# get the request for this slot that was approved (should only be ONE)
|
||||
|
|
@ -409,7 +416,7 @@ module JamRuby
|
|||
end
|
||||
|
||||
def has_mount?
|
||||
active_music_session && active_music_session.mount
|
||||
!active_music_session.nil? && !active_music_session.mount.nil?
|
||||
end
|
||||
|
||||
def can_cancel? user
|
||||
|
|
|
|||
|
|
@ -492,6 +492,7 @@ module JamRuby
|
|||
msg = @@message_factory.session_join(
|
||||
music_session.id,
|
||||
user.photo_url,
|
||||
user.id,
|
||||
notification_msg,
|
||||
music_session.track_changes_counter
|
||||
)
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ module JamRuby
|
|||
|
||||
# send notification
|
||||
if music_session.creator.id == user.id
|
||||
Notification.send_scheduled_session_rsvp_cancelled_org(music_session, user)
|
||||
Notification.send_scheduled_session_rsvp_cancelled_org(music_session, rsvp_request.user)
|
||||
else
|
||||
Notification.send_scheduled_session_rsvp_cancelled(music_session, user)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ require 'ipaddr'
|
|||
module JamRuby
|
||||
class Score < ActiveRecord::Base
|
||||
|
||||
MAX_YELLOW_LATENCY = 40
|
||||
MAX_YELLOW_LATENCY = 40 # this round-trip internet latency, not full score
|
||||
|
||||
self.table_name = 'scores'
|
||||
|
||||
|
|
|
|||
|
|
@ -15,14 +15,15 @@ module JamRuby
|
|||
|
||||
result = connection.execute(
|
||||
"INSERT INTO score_histories
|
||||
(from_client_id, from_user_id, from_latency_tester_id, from_addr, from_isp, from_country, from_region, from_city, from_postal, from_latitude, from_longitude,
|
||||
to_client_id, to_user_id, to_latency_tester_id, to_addr, to_isp, to_country, to_region, to_city, to_postal, to_latitude, to_longitude,
|
||||
(from_client_id, from_user_id, from_latency_tester_id, from_addr, from_locidispid, from_isp, from_country, from_region, from_city, from_postal, from_latitude, from_longitude,
|
||||
to_client_id, to_user_id, to_latency_tester_id, to_addr, to_locidispid, to_isp, to_country, to_region, to_city, to_postal, to_latitude, to_longitude,
|
||||
score, score_dt, scoring_data)
|
||||
SELECT
|
||||
s.anodeid AS from_client_id,
|
||||
s.auserid AS from_user_id,
|
||||
s.alatencytestid AS from_latency_tester_id,
|
||||
s.aaddr AS from_addr,
|
||||
s.alocidispid AS from_locidispid,
|
||||
x.company AS from_isp,
|
||||
a.countrycode AS from_country,
|
||||
a.region AS from_region,
|
||||
|
|
@ -34,6 +35,7 @@ module JamRuby
|
|||
s.buserid AS to_user_id,
|
||||
s.blatencytestid AS to_latency_tester_id,
|
||||
s.baddr AS to_addr,
|
||||
s.blocidispid AS to_locidispid,
|
||||
y.company AS to_isp,
|
||||
b.countrycode AS to_country,
|
||||
b.region AS to_region,
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ module JamRuby
|
|||
score_join = 'left outer' # or 'inner'
|
||||
score_min = nil
|
||||
score_max = nil
|
||||
# these score_min, score_max come from here (doubled): https://jamkazam.atlassian.net/browse/VRFS-1962
|
||||
case score_limit
|
||||
when GOOD_SCORE
|
||||
score_join = 'inner'
|
||||
|
|
@ -193,14 +194,14 @@ module JamRuby
|
|||
when MODERATE_SCORE
|
||||
score_join = 'inner'
|
||||
score_min = 40
|
||||
score_max = 80
|
||||
score_max = 70
|
||||
when POOR_SCORE
|
||||
score_join = 'inner'
|
||||
score_min = 80
|
||||
score_max = 120
|
||||
score_max = 100
|
||||
when UNACCEPTABLE_SCORE
|
||||
score_join = 'inner'
|
||||
score_min = 120
|
||||
score_min = 100
|
||||
score_max = nil
|
||||
when SCORED_SCORE
|
||||
score_join = 'inner'
|
||||
|
|
@ -221,11 +222,11 @@ module JamRuby
|
|||
|
||||
rel = rel.joins('LEFT JOIN regions ON regions.countrycode = users.country AND regions.region = users.state')
|
||||
|
||||
rel = rel.where(['current_scores.score > ?', score_min]) unless score_min.nil?
|
||||
rel = rel.where(['current_scores.score <= ?', score_max]) unless score_max.nil?
|
||||
rel = rel.where(['current_scores.full_score > ?', score_min]) unless score_min.nil?
|
||||
rel = rel.where(['current_scores.full_score <= ?', score_max]) unless score_max.nil?
|
||||
|
||||
rel = rel.select('current_scores.score, regions.regionname')
|
||||
rel = rel.group('current_scores.score, regions.regionname')
|
||||
rel = rel.select('current_scores.full_score, current_scores.score, current_scores.b_audio_latency as audio_latency, regions.regionname')
|
||||
rel = rel.group('current_scores.full_score, current_scores.score, current_scores.b_audio_latency, regions.regionname')
|
||||
end
|
||||
|
||||
ordering = self.order_param(params)
|
||||
|
|
@ -249,7 +250,7 @@ module JamRuby
|
|||
end
|
||||
|
||||
unless locidispid.nil?
|
||||
rel = rel.order('current_scores.score ASC NULLS LAST')
|
||||
rel = rel.order('current_scores.full_score ASC NULLS LAST')
|
||||
end
|
||||
|
||||
rel = rel.order('users.created_at DESC')
|
||||
|
|
@ -393,15 +394,15 @@ module JamRuby
|
|||
# an offline process and thus uses the last jam location as "home base"
|
||||
|
||||
locidispid = usr.last_jam_locidispid
|
||||
score_limit = 60
|
||||
score_limit = 70
|
||||
limit = 50
|
||||
|
||||
rel = User.musicians_geocoded
|
||||
.where(['users.created_at >= ? AND users.id != ?', since_date, usr.id])
|
||||
.joins('inner join current_scores on users.last_jam_locidispid = current_scores.alocidispid')
|
||||
.where(['current_scores.blocidispid = ?', locidispid])
|
||||
.where(['current_scores.score <= ?', score_limit])
|
||||
.order('current_scores.score') # best scores first
|
||||
.where(['current_scores.full_score <= ?', score_limit])
|
||||
.order('current_scores.full_score') # best scores first
|
||||
.order('users.created_at DESC') # then most recent
|
||||
.limit(limit)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ module JamRuby
|
|||
end
|
||||
|
||||
def run
|
||||
# get all weekly sessions that have ended in the last 15 minutes
|
||||
criteria = "recurring_mode = 'weekly' AND session_removed_at is not null AND canceled = false AND next_session_scheduled = false"
|
||||
# get all weekly sessions that started at least 4 hours ago
|
||||
criteria = "recurring_mode = 'weekly' AND scheduled_start + interval '4hours' < NOW() AND canceled = false AND next_session_scheduled = false"
|
||||
MusicSession.find_each(:conditions => criteria) do |music_session|
|
||||
music_session.copy
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ require 'faker'
|
|||
|
||||
FactoryGirl.define do
|
||||
factory :user, :class => JamRuby::User do
|
||||
ignore do
|
||||
specific_instruments nil
|
||||
end
|
||||
|
||||
sequence(:email) { |n| "person_#{n}@example.com"}
|
||||
sequence(:first_name) { |n| "Person" }
|
||||
sequence(:last_name) { |n| "#{n}" }
|
||||
|
|
@ -17,8 +21,14 @@ FactoryGirl.define do
|
|||
|
||||
#u.association :musician_instrument, factory: :musician_instrument, user: u
|
||||
|
||||
before(:create) do |user|
|
||||
user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user)
|
||||
before(:create) do |user, evaluator|
|
||||
if evaluator.specific_instruments
|
||||
evaluator.specific_instruments.each do |instrument|
|
||||
user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user, instrument: instrument)
|
||||
end
|
||||
else
|
||||
user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user)
|
||||
end
|
||||
end
|
||||
|
||||
factory :fan do
|
||||
|
|
|
|||
|
|
@ -354,8 +354,8 @@ describe ActiveMusicSession do
|
|||
user = FactoryGirl.create(:user, last_jam_locidispid: 1, last_jam_audio_latency: 5)
|
||||
c3 = FactoryGirl.create(:connection, user: user, locidispid: 1, last_jam_audio_latency: 5)
|
||||
|
||||
Score.createx(c1.locidispid, c1.client_id, c1.addr, c3.locidispid, c3.client_id, c3.addr, 20, nil)
|
||||
Score.createx(c2.locidispid, c2.client_id, c2.addr, c3.locidispid, c3.client_id, c3.addr, 30, nil)
|
||||
Score.createx(c1.locidispid, c1.client_id, c1.addr, c3.locidispid, c3.client_id, c3.addr, 20, nil, nil, {auserid: creator.id, buserid: user.id})
|
||||
Score.createx(c2.locidispid, c2.client_id, c2.addr, c3.locidispid, c3.client_id, c3.addr, 30, nil, nil, {auserid: creator2.id, buserid: user.id})
|
||||
|
||||
# make a transaction
|
||||
|
||||
|
|
|
|||
|
|
@ -10,36 +10,62 @@ describe EmailBatchScheduledSessions do
|
|||
UserMailer.deliveries.clear
|
||||
end
|
||||
|
||||
def find_user_sessions(obj, user)
|
||||
found = obj.find{|item| item[0] == user}
|
||||
found.should_not be_nil
|
||||
found[1]
|
||||
end
|
||||
|
||||
describe 'daily scheduled' do
|
||||
# before { pending }
|
||||
|
||||
let (:scheduled_batch) { FactoryGirl.create(:email_batch_scheduled_session) }
|
||||
|
||||
let (:drums) { FactoryGirl.create(:instrument, :description => 'drums') }
|
||||
let (:guitar) { FactoryGirl.create(:instrument, :description => 'guitar') }
|
||||
let (:bass) { FactoryGirl.create(:instrument, :description => 'bass') }
|
||||
let (:vocals) { FactoryGirl.create(:instrument, :description => 'vocal') }
|
||||
let (:drums) { Instrument.find('drums') }
|
||||
let (:guitar) { Instrument.find('acoustic guitar') }
|
||||
let (:bass) { Instrument.find('bass guitar') }
|
||||
let (:vocals) { Instrument.find('voice') }
|
||||
let (:electric_guitar) { Instrument.find('electric guitar')}
|
||||
|
||||
let (:drummer) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) }
|
||||
let (:guitarist) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) }
|
||||
let (:bassist) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) }
|
||||
let (:vocalist) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) }
|
||||
let (:loser) { FactoryGirl.create(:user, :last_jam_locidispid => 2, :last_jam_addr => 2) }
|
||||
let (:drummer) { FactoryGirl.create(:user, first_name: 'drummer', :last_jam_locidispid => 1, :last_jam_addr => 1, specific_instruments: [drums, guitar]) }
|
||||
let (:guitarist) { FactoryGirl.create(:user, first_name: 'guitarist', :last_jam_locidispid => 1, :last_jam_addr => 1, specific_instruments: [guitar, drums]) }
|
||||
let (:bassist) { FactoryGirl.create(:user, first_name: 'bassist', :last_jam_locidispid => 1, :last_jam_addr => 1, specific_instruments: [bass]) }
|
||||
let (:vocalist) { FactoryGirl.create(:user, first_name: 'vocalist', :last_jam_locidispid => 1, :last_jam_addr => 1, specific_instruments: [vocals, electric_guitar]) }
|
||||
let (:loser) { FactoryGirl.create(:user, first_name: 'loser', :last_jam_locidispid => 2, :last_jam_addr => 2, specific_instruments: [vocals, drums]) }
|
||||
|
||||
let (:session3_creator) {FactoryGirl.create(:user, first_name: 'session3_creator', :last_jam_locidispid => 3, :last_jam_addr => 3, specific_instruments: [vocals]) }
|
||||
let (:session4_creator) {FactoryGirl.create(:user, first_name: 'session4_creator', :last_jam_locidispid => 4, :last_jam_addr => 4, specific_instruments: [vocals]) }
|
||||
|
||||
let (:session1) do
|
||||
FactoryGirl.create(:music_session,
|
||||
:creator => drummer,
|
||||
:scheduled_start => Time.now() + 2.days,
|
||||
:musician_access => true,
|
||||
:approval_required => false,
|
||||
:open_rsvps=> true,
|
||||
:is_unstructured_rsvp => false,
|
||||
:created_at => Time.now - 1.hour)
|
||||
end
|
||||
let (:session2) do
|
||||
FactoryGirl.create(:music_session,
|
||||
:creator => drummer,
|
||||
:open_rsvps => false,
|
||||
:is_unstructured_rsvp => false,
|
||||
:scheduled_start => Time.now() + 2.days,
|
||||
:created_at => Time.now - 1.hour)
|
||||
end
|
||||
let (:session3) do
|
||||
FactoryGirl.create(:music_session,
|
||||
:creator => session3_creator,
|
||||
:open_rsvps => true,
|
||||
:is_unstructured_rsvp => true,
|
||||
:scheduled_start => Time.now() + 2.days,
|
||||
:created_at => Time.now - 1.hour)
|
||||
end
|
||||
let (:session4) do
|
||||
FactoryGirl.create(:music_session,
|
||||
:creator => session4_creator,
|
||||
:open_rsvps => true,
|
||||
:is_unstructured_rsvp => true,
|
||||
:scheduled_start => Time.now() + 2.days,
|
||||
:musician_access => true,
|
||||
:approval_required => false,
|
||||
:created_at => Time.now - 1.hour)
|
||||
end
|
||||
|
||||
|
|
@ -49,23 +75,9 @@ describe EmailBatchScheduledSessions do
|
|||
JamRuby::Score.delete_all
|
||||
scheduled_batch.reset!
|
||||
|
||||
drummer.musician_instruments << FactoryGirl.build(:musician_instrument, user: drummer, instrument: drums, proficiency_level: 2)
|
||||
drummer.musician_instruments << FactoryGirl.build(:musician_instrument, user: drummer, instrument: guitar, proficiency_level: 2)
|
||||
|
||||
guitarist.musician_instruments << FactoryGirl.build(:musician_instrument, user: guitarist, instrument: guitar, proficiency_level: 2)
|
||||
guitarist.musician_instruments << FactoryGirl.build(:musician_instrument, user: guitarist, instrument: bass, proficiency_level: 2)
|
||||
|
||||
bassist.musician_instruments << FactoryGirl.build(:musician_instrument, user: bassist, instrument: bass, proficiency_level: 2)
|
||||
bassist.musician_instruments << FactoryGirl.build(:musician_instrument, user: bassist, instrument: guitar, proficiency_level: 2)
|
||||
|
||||
vocalist.musician_instruments << FactoryGirl.build(:musician_instrument, user: vocalist, instrument: vocals, proficiency_level: 2)
|
||||
|
||||
loser.musician_instruments << FactoryGirl.build(:musician_instrument, user: loser, instrument: vocals, proficiency_level: 2)
|
||||
loser.musician_instruments << FactoryGirl.build(:musician_instrument, user: loser, instrument: drums, proficiency_level: 2)
|
||||
|
||||
FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => session1)
|
||||
FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => session1)
|
||||
FactoryGirl.create(:rsvp_slot, :instrument => bass, :music_session => session1)
|
||||
@drums_rsvp_slot = FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => session1)
|
||||
@guitar_rsvp_slot = FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => session1)
|
||||
@bass_rsvp_slot = FactoryGirl.create(:rsvp_slot, :instrument => bass, :music_session => session1)
|
||||
FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => session2)
|
||||
FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => session2)
|
||||
FactoryGirl.create(:rsvp_slot, :instrument => bass, :music_session => session2)
|
||||
|
|
@ -73,22 +85,176 @@ describe EmailBatchScheduledSessions do
|
|||
# oo.rsvp_request_slot.update_attributes(chosen: true)
|
||||
# oo = FactoryGirl.create(:rsvp_request, :user => vocalist, :rsvp_slot => oo)
|
||||
# oo.rsvp_request_slot.update_attributes(chosen: true)
|
||||
|
||||
end
|
||||
|
||||
it 'sets up data properly' do
|
||||
JamRuby::Score.createx(1, 'a', 1, 1, 'a', 1, 10)
|
||||
JamRuby::Score.createx(1, 'a', 1, 2, 'a', 2, Score::MAX_YELLOW_LATENCY + 1)
|
||||
|
||||
expect(drummer.instruments.include?(drums)).to eq(true)
|
||||
expect(drummer.instruments.include?(guitar)).to eq(true)
|
||||
obj = scheduled_batch.fetch_recipients
|
||||
expect(obj.count).to eq(2)
|
||||
|
||||
scheduled_batch.deliver_batch
|
||||
expect(UserMailer.deliveries.length).to eq(2)
|
||||
end
|
||||
|
||||
describe "everyone but loser has good enough scores" do
|
||||
|
||||
before(:each) do
|
||||
JamRuby::Score.createx(1, 'a', 1, 1, 'a', 1, 10)
|
||||
JamRuby::Score.createx(1, 'a', 1, 2, 'a', 2, (APP_CONFIG.max_yellow_full_score * 2) + 1)
|
||||
@matching_instruments_users = [guitarist, bassist]
|
||||
@good_score_users = [guitarist, bassist, vocalist] # not adding the drummer because he's the creator (he does have a good score to these others though)
|
||||
end
|
||||
|
||||
describe "session1 is_unstructured" do
|
||||
before(:each) do
|
||||
session1.is_unstructured_rsvp = true
|
||||
session1.save!
|
||||
end
|
||||
|
||||
it 'finds anyone with good enough scores' do
|
||||
obj = scheduled_batch.fetch_recipients
|
||||
found_users = obj.map{ |user_and_sessions| user_and_sessions[0]}
|
||||
@good_score_users.should =~ found_users
|
||||
|
||||
scheduled_batch.deliver_batch
|
||||
expect(UserMailer.deliveries.length).to eq(@good_score_users.length)
|
||||
end
|
||||
end
|
||||
|
||||
it 'finds anyone with good enough scores and matching instruments' do
|
||||
obj = scheduled_batch.fetch_recipients
|
||||
|
||||
#Database.dump("SELECT * FROM tmp_candidate_recipients") do |field, value|
|
||||
# if field == 'receiver_id'
|
||||
# User.find(value).first_name
|
||||
# else
|
||||
# value.to_s
|
||||
# end
|
||||
#end
|
||||
|
||||
found_users = obj.map{ |user_and_sessions| user_and_sessions[0]}
|
||||
@matching_instruments_users.should =~ found_users
|
||||
|
||||
scheduled_batch.deliver_batch
|
||||
expect(UserMailer.deliveries.length).to eq(@matching_instruments_users.length)
|
||||
end
|
||||
|
||||
describe "closes a RSVP slot in session1" do
|
||||
before(:each) do
|
||||
request = FactoryGirl.create(:rsvp_request, user: loser)
|
||||
FactoryGirl.create(:rsvp_request_rsvp_slot, chosen: true, rsvp_request: request, rsvp_slot: @bass_rsvp_slot)
|
||||
end
|
||||
|
||||
it "finds one less" do
|
||||
# the bass slot is closed; so we should not longer find the bassist in the results
|
||||
@matching_instruments_users.delete(bassist).should_not be_nil
|
||||
|
||||
obj = scheduled_batch.fetch_recipients
|
||||
found_users = obj.map{ |user_and_sessions| user_and_sessions[0]}
|
||||
@matching_instruments_users.should =~ found_users
|
||||
|
||||
scheduled_batch.deliver_batch
|
||||
expect(UserMailer.deliveries.length).to eq(@matching_instruments_users.length)
|
||||
end
|
||||
end
|
||||
|
||||
describe "2 more open sessions" do
|
||||
before(:each) do
|
||||
JamRuby::Score.createx(1, 'a', 1, session3_creator.last_jam_locidispid, 'a', 1, 5)
|
||||
JamRuby::Score.createx(1, 'a', 1, session4_creator.last_jam_locidispid, 'a', 2, 20)
|
||||
session3.touch
|
||||
session4.touch
|
||||
end
|
||||
|
||||
it "sessions are sorted by latency" do
|
||||
obj = scheduled_batch.fetch_recipients
|
||||
obj.length.should == @matching_instruments_users.length + [drummer, vocalist].length
|
||||
|
||||
# verify that all 4 users have the right sessions, in the right order
|
||||
@matching_instruments_users.each do |user|
|
||||
user_sessions = find_user_sessions(obj, user)
|
||||
user_sessions.should == [session3, session1, session4]
|
||||
end
|
||||
|
||||
user_sessions = find_user_sessions(obj, drummer)
|
||||
user_sessions.should == [session3, session4]
|
||||
|
||||
user_sessions = find_user_sessions(obj, vocalist)
|
||||
user_sessions.should == [session3, session4]
|
||||
end
|
||||
end
|
||||
|
||||
describe "21 more sessions" do
|
||||
before(:each) do
|
||||
20.times do |i|
|
||||
creator = FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1)
|
||||
FactoryGirl.create(:music_session,
|
||||
:creator => creator,
|
||||
:open_rsvps => true,
|
||||
:is_unstructured_rsvp => true,
|
||||
:scheduled_start => Time.now() + 2.days,
|
||||
:created_at => Time.now - 1.hour)
|
||||
end
|
||||
end
|
||||
|
||||
it "prevents more than 20 sessions per user" do
|
||||
obj = scheduled_batch.fetch_recipients
|
||||
obj.length.should == 20 + 4 # 4 is vocalist, drummer, guitarist, and bassist. 20 is the users made in the before block
|
||||
|
||||
|
||||
# the bassist should *potentially* the drummer's session, and these new 20 sessions. But we limit to 20.
|
||||
user_sessions = find_user_sessions(obj, bassist)
|
||||
user_sessions.length.should == 20
|
||||
end
|
||||
end
|
||||
|
||||
it 'finds anyone with good enough scores and matching instruments' do
|
||||
obj = scheduled_batch.fetch_recipients
|
||||
|
||||
#Database.dump("SELECT * FROM tmp_candidate_recipients") do |field, value|
|
||||
# if field == 'receiver_id'
|
||||
# User.find(value).first_name
|
||||
# else
|
||||
# value.to_s
|
||||
# end
|
||||
#end
|
||||
|
||||
found_users = obj.map{ |user_and_sessions| user_and_sessions[0]}
|
||||
@matching_instruments_users.should =~ found_users
|
||||
|
||||
scheduled_batch.deliver_batch
|
||||
expect(UserMailer.deliveries.length).to eq(@matching_instruments_users.length)
|
||||
end
|
||||
|
||||
describe "open_rsvp=false" do
|
||||
before(:each) do
|
||||
session1.open_rsvps = false
|
||||
session1.save!
|
||||
session2.open_rsvps = false
|
||||
session2.save!
|
||||
end
|
||||
|
||||
it "won't find an open_rsvps=false session without an invitation" do
|
||||
obj = scheduled_batch.fetch_recipients
|
||||
expect(obj.count).to eq(0)
|
||||
end
|
||||
|
||||
describe "with invitations" do
|
||||
let(:invitation) {friend(drummer, guitarist); FactoryGirl.create(:invitation, sender: drummer, receiver:guitarist, music_session: session1)}
|
||||
|
||||
before(:each) do
|
||||
invitation.touch
|
||||
end
|
||||
|
||||
it "finds user with the invite" do
|
||||
obj = scheduled_batch.fetch_recipients
|
||||
found_users = obj.map{ |user_and_sessions| user_and_sessions[0]}
|
||||
[guitarist].should =~ found_users
|
||||
|
||||
scheduled_batch.deliver_batch
|
||||
expect(UserMailer.deliveries.length).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
it 'handles large batches' do
|
||||
creators = []
|
||||
8.downto(1) do |nn|
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe GenericState do
|
||||
|
||||
def database_env (env)
|
||||
singleton = GenericState.singleton
|
||||
singleton.env = env
|
||||
singleton.save!
|
||||
end
|
||||
|
||||
def rails_env (env)
|
||||
JamRuby::Environment.should_receive(:mode).any_number_of_times.and_return(env)
|
||||
end
|
||||
|
||||
describe "allow_emails?" do
|
||||
|
||||
it "allows emails if database is production and env is production, with build number" do
|
||||
database_env('production')
|
||||
rails_env('production')
|
||||
stub_const("ENV", {'BUILD_NUMBER' => 1})
|
||||
|
||||
GenericState.allow_emails?.should be_true
|
||||
end
|
||||
|
||||
it "no emails if database is production and env is production, no build number" do
|
||||
database_env('production')
|
||||
rails_env('production')
|
||||
stub_const("ENV", {'BUILD_NUMBER' => nil})
|
||||
|
||||
GenericState.allow_emails?.should be_false
|
||||
end
|
||||
|
||||
it "allows emails if database is development and env is development" do
|
||||
database_env('development')
|
||||
rails_env('development')
|
||||
|
||||
GenericState.allow_emails?.should be_true
|
||||
end
|
||||
|
||||
it "no emails if database development, and environment is test" do
|
||||
database_env('development')
|
||||
rails_env('test')
|
||||
GenericState.allow_emails?.should be_false
|
||||
end
|
||||
|
||||
it "no emails if database production, and environment is development" do
|
||||
database_env('production')
|
||||
rails_env('development')
|
||||
stub_const("ENV", {'BUILD_NUMBER' => 1})
|
||||
|
||||
GenericState.allow_emails?.should be_false
|
||||
end
|
||||
|
||||
it "no emails if database production, and environment is test" do
|
||||
database_env('production')
|
||||
rails_env('development')
|
||||
|
||||
GenericState.allow_emails?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -421,7 +421,7 @@ describe MusicSession do
|
|||
let(:network_score) { 20 }
|
||||
|
||||
before(:each) do
|
||||
Score.createx(conn.locidispid, conn.client_id, conn.addr, searcher_conn.locidispid, searcher_conn.client_id, searcher_conn.addr, network_score, nil)
|
||||
Score.createx(conn.locidispid, conn.client_id, conn.addr, searcher_conn.locidispid, searcher_conn.client_id, searcher_conn.addr, network_score, nil, nil, {auserid: creator.id, buserid: searcher.id})
|
||||
end
|
||||
|
||||
it "no results" do
|
||||
|
|
@ -499,7 +499,7 @@ describe MusicSession do
|
|||
FactoryGirl.create(:invitation, receiver:invitee, sender:creator, music_session: music_session)
|
||||
|
||||
# create a score between invitee, and searcher
|
||||
Score.createx(invitee.last_jam_locidispid, 'immaterial', 1, searcher_conn.locidispid, searcher_conn.client_id, searcher_conn.addr, network_score, nil)
|
||||
Score.createx(invitee.last_jam_locidispid, 'immaterial', 1, searcher_conn.locidispid, searcher_conn.client_id, searcher_conn.addr, network_score, nil, nil, {auserid: invitee.id, buserid: searcher.id})
|
||||
|
||||
music_sessions, user_scores = sms(searcher, default_opts)
|
||||
music_sessions.length.should == 1
|
||||
|
|
@ -580,14 +580,16 @@ describe MusicSession do
|
|||
end
|
||||
|
||||
it "searcher_1" do
|
||||
|
||||
|
||||
# create a bad score between searcher_1 and creator_1 (but we should still see it sort 1st because it's got an RSVP to the searcher)
|
||||
Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_1.locidispid, creator_conn_1.client_id, creator_conn_1.addr, bad_network_score, nil)
|
||||
Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_1.locidispid, creator_conn_1.client_id, creator_conn_1.addr, bad_network_score, nil, nil, {auserid: searcher_1.id, buserid: creator_1.id})
|
||||
|
||||
# create a fair score between searcher_1 and creator_2 (but we should still see it sort 2st because it's got an invitation to the searcher)
|
||||
Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_2.locidispid, creator_conn_2.client_id, creator_conn_2.addr, fair_network_score, nil)
|
||||
Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_2.locidispid, creator_conn_2.client_id, creator_conn_2.addr, fair_network_score, nil, nil, {auserid: searcher_1.id, buserid: creator_2.id})
|
||||
|
||||
# create a good score between searcher_1 and creator_3 (but we should still see it sort last because it's an open session; no affiliation with the searcher)
|
||||
Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_3.locidispid, creator_conn_3.client_id, creator_conn_3.addr, good_network_score, nil)
|
||||
Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_3.locidispid, creator_conn_3.client_id, creator_conn_3.addr, good_network_score, nil, nil, {auserid: searcher_1.id, buserid: creator_3.id})
|
||||
|
||||
music_sessions, user_scores = sms(searcher_1, {client_id: searcher_conn_1.client_id})
|
||||
|
||||
|
|
@ -777,5 +779,20 @@ describe MusicSession do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "scheduled session rescheduled logic" do
|
||||
|
||||
it "detect change to scheduling info" do
|
||||
|
||||
music_session1.description = "Hey!"
|
||||
music_session1.save!
|
||||
music_session1.scheduling_info_changed.should be_false
|
||||
|
||||
music_session1.scheduled_start = Time.now - 1.days
|
||||
music_session1.save!
|
||||
music_session1.scheduling_info_changed.should be_true
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ describe ScoreHistory do
|
|||
score_history.from_user_id.should == score1.auserid
|
||||
score_history.from_latency_tester_id.should == score1.alatencytestid
|
||||
score_history.from_addr.should == score1.aaddr
|
||||
score_history.from_locidispid.should == score1.alocidispid
|
||||
score_history.from_isp.should == austin[:jamisp].jam_company.company
|
||||
score_history.from_country.should == austin[:geoiplocation].countrycode
|
||||
score_history.from_region.should == austin[:geoiplocation].region
|
||||
|
|
@ -49,6 +50,7 @@ describe ScoreHistory do
|
|||
score_history.to_user_id.should == score1.buserid
|
||||
score_history.to_latency_tester_id.should == score1.blatencytestid
|
||||
score_history.to_addr.should == score1.baddr
|
||||
score_history.to_locidispid.should == score1.blocidispid
|
||||
score_history.to_isp.should == dallas[:jamisp].jam_company.company
|
||||
score_history.to_country.should == dallas[:geoiplocation].countrycode
|
||||
score_history.to_region.should == dallas[:geoiplocation].region
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ describe "MusicSessionScheduler" do
|
|||
|
||||
MusicSession.all.count.should == 1
|
||||
|
||||
# end the session
|
||||
ms.session_removed_at = Time.now
|
||||
# move the session back more than 4 hours
|
||||
ms.scheduled_start = Time.now - 5.hours
|
||||
ms.save!
|
||||
|
||||
# run the scheduler again
|
||||
|
|
@ -27,7 +27,6 @@ describe "MusicSessionScheduler" do
|
|||
|
||||
MusicSession.all.count.should == 2
|
||||
|
||||
MusicSession.where(:session_removed_at => nil).count.should == 1
|
||||
MusicSession.where(:next_session_scheduled => true).count.should == 1
|
||||
end
|
||||
|
||||
|
|
@ -40,7 +39,7 @@ describe "MusicSessionScheduler" do
|
|||
@scheduler.run
|
||||
MusicSession.all.count.should == 1
|
||||
|
||||
ms.session_removed_at = Time.now
|
||||
ms.scheduled_start = Time.now - 3.hours
|
||||
ms.save!
|
||||
|
||||
@scheduler.run
|
||||
|
|
|
|||
|
|
@ -63,6 +63,70 @@ describe "RenderMailers", :slow => true do
|
|||
it { @filename="welcome_betauser"; InvitedUserMailer.welcome_betauser(admin_invited_user).deliver }
|
||||
end
|
||||
|
||||
describe "Daily Scheduled Session emails" do
|
||||
let (:scheduled_batch) { FactoryGirl.create(:email_batch_scheduled_session) }
|
||||
let(:music_session) { FactoryGirl.create(:music_session) }
|
||||
let (:drums) { FactoryGirl.create(:instrument, :description => 'drums') }
|
||||
let (:guitar) { FactoryGirl.create(:instrument, :description => 'guitar') }
|
||||
let (:bass) { FactoryGirl.create(:instrument, :description => 'bass') }
|
||||
let (:vocals) { FactoryGirl.create(:instrument, :description => 'vocal') }
|
||||
|
||||
let (:drummer) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) }
|
||||
let (:guitarist) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) }
|
||||
let (:bassist) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) }
|
||||
let (:vocalist) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) }
|
||||
|
||||
let (:session1) do
|
||||
FactoryGirl.create(:music_session,
|
||||
:creator => drummer,
|
||||
:scheduled_start => Time.now() + 2.days,
|
||||
:musician_access => true,
|
||||
:approval_required => false,
|
||||
:created_at => Time.now - 1.hour)
|
||||
end
|
||||
|
||||
let (:session2) do
|
||||
FactoryGirl.create(:music_session,
|
||||
:creator => drummer,
|
||||
:scheduled_start => Time.now() + 2.days,
|
||||
:musician_access => true,
|
||||
:approval_required => false,
|
||||
:created_at => Time.now - 1.hour)
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
BatchMailer.deliveries.clear
|
||||
scheduled_batch.reset!
|
||||
|
||||
drummer.musician_instruments << FactoryGirl.build(:musician_instrument, user: drummer, instrument: drums, proficiency_level: 2)
|
||||
drummer.musician_instruments << FactoryGirl.build(:musician_instrument, user: drummer, instrument: guitar, proficiency_level: 2)
|
||||
|
||||
guitarist.musician_instruments << FactoryGirl.build(:musician_instrument, user: guitarist, instrument: guitar, proficiency_level: 2)
|
||||
guitarist.musician_instruments << FactoryGirl.build(:musician_instrument, user: guitarist, instrument: bass, proficiency_level: 2)
|
||||
|
||||
vocalist.musician_instruments << FactoryGirl.build(:musician_instrument, user: vocalist, instrument: vocals, proficiency_level: 2)
|
||||
|
||||
FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => session1)
|
||||
FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => session1)
|
||||
FactoryGirl.create(:rsvp_slot, :instrument => bass, :music_session => session1)
|
||||
|
||||
FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => session2)
|
||||
FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => session2)
|
||||
FactoryGirl.create(:rsvp_slot, :instrument => bass, :music_session => session2)
|
||||
|
||||
JamRuby::Score.createx(1, 'a', 1, 1, 'a', 1, 10)
|
||||
JamRuby::Score.createx(1, 'a', 1, 2, 'a', 2, Score::MAX_YELLOW_LATENCY + 1)
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
BatchMailer.deliveries.length.should == 1
|
||||
mail = BatchMailer.deliveries[0]
|
||||
save_emails_to_disk(mail, @filename)
|
||||
end
|
||||
|
||||
it "daily sessions" do @filename="daily_sessions"; scheduled_batch.deliver_batch end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def save_emails_to_disk(mail, filename)
|
||||
|
|
|
|||
|
|
@ -109,6 +109,19 @@ def app_config
|
|||
def max_track_part_upload_failures
|
||||
3
|
||||
end
|
||||
|
||||
def max_good_full_score
|
||||
20
|
||||
end
|
||||
|
||||
def max_yellow_full_score
|
||||
35
|
||||
end
|
||||
|
||||
def max_red_full_score
|
||||
50
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def audiomixer_workspace_path
|
||||
|
|
@ -169,4 +182,9 @@ def fake_geo_124_zip(geoisp_csv)
|
|||
end
|
||||
|
||||
zipfile
|
||||
end
|
||||
|
||||
def friend(user1, user2)
|
||||
FactoryGirl.create(:friendship, user: user1, friend: user2)
|
||||
FactoryGirl.create(:friendship, user: user2, friend: user1)
|
||||
end
|
||||
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.AccountProfileScreen = function(app) {
|
||||
var $document = $(document);
|
||||
var logger = context.JK.logger;
|
||||
var EVENTS = context.JK.EVENTS;
|
||||
var api = context.JK.Rest();
|
||||
var userId;
|
||||
var user = {};
|
||||
|
|
@ -63,6 +65,7 @@
|
|||
$('select#user_birth_date_1i', content_root).val(parseInt(birthDateYear));
|
||||
$('select#user_birth_date_2i', content_root).val(parseInt(birthDateMonth));
|
||||
$('select#user_birth_date_3i', content_root).val(parseInt(birthDateDay));
|
||||
|
||||
}
|
||||
|
||||
// update instruments
|
||||
|
|
@ -87,6 +90,7 @@
|
|||
}
|
||||
|
||||
context.JK.dropdown($('select', content_root));
|
||||
|
||||
}
|
||||
|
||||
function isUserInstrument(instrument, userInstruments) {
|
||||
|
|
@ -317,7 +321,9 @@
|
|||
.always(function() { loadingCitiesData = false;})
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
context.JK.dropdown($('select'));
|
||||
}
|
||||
|
||||
function navToAccount() {
|
||||
|
|
@ -366,6 +372,9 @@
|
|||
},
|
||||
null,
|
||||
true);
|
||||
|
||||
$document.triggerHandler(EVENTS.USER_UPDATED, response);
|
||||
|
||||
}
|
||||
|
||||
function postUpdateProfileFailure(xhr, textStatus, errorMessage) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
context.JK.AccountProfileAvatarScreen = function(app) {
|
||||
var self = this;
|
||||
var logger = context.JK.logger;
|
||||
var EVENTS = context.JK.EVENTS;
|
||||
var rest = context.JK.Rest();
|
||||
var user = {};
|
||||
var tmpUploadPath = null;
|
||||
|
|
@ -381,9 +382,9 @@
|
|||
self.userDetail = response;
|
||||
|
||||
// notify any listeners that the avatar changed
|
||||
userDropdown.loadMe();
|
||||
// userDropdown.loadMe();
|
||||
// $('.avatar_large img').trigger('avatar_changed', [self.userDetail.photo_url]);
|
||||
|
||||
$(document).triggerHandler(EVENTS.USER_UPDATED, response);
|
||||
app.notify(
|
||||
{ title: "Avatar Changed",
|
||||
text: "You have updated your avatar successfully."
|
||||
|
|
|
|||
|
|
@ -28,11 +28,15 @@
|
|||
$('.schedule-recurrence', $dialog).html("Recurs " + response.recurring_mode + " on this day at this time");
|
||||
}
|
||||
|
||||
var hasOpenSlots = response.open_slots && response.open_slots.length > 0;
|
||||
|
||||
|
||||
if (response['is_unstructured_rsvp?']) {
|
||||
$('.rsvp-instruments', $dialog).append('<input type="checkbox" value="unstructured"/>Any Instrument<br/>');
|
||||
var checkedState = hasOpenSlots ? '' : 'checked="checked"'
|
||||
$('.rsvp-instruments', $dialog).append('<input type="checkbox" ' + checkedState + ' value="unstructured"/>Play Any Instrument You Like<br/>');
|
||||
}
|
||||
|
||||
if (response.open_slots && response.open_slots.length > 0) {
|
||||
if (hasOpenSlots) {
|
||||
$.each(response.open_slots, function(index, val) {
|
||||
var instrument = val.instrument_id;
|
||||
|
||||
|
|
|
|||
|
|
@ -201,13 +201,13 @@
|
|||
}
|
||||
|
||||
function renderNotConnected() {
|
||||
console.log("RENDER NOT CONNECTED!!!!!!!!!")
|
||||
logger.debug("text-message dialog: render not connected")
|
||||
$interactionBlocker.addClass('active');
|
||||
$disconnectedMsg.addClass('active');
|
||||
}
|
||||
|
||||
function renderConnected() {
|
||||
console.log("RENDER CONNECTED!!!!!!!!!")
|
||||
logger.debug("text-message dialog: render connected")
|
||||
$interactionBlocker.removeClass('active');
|
||||
$disconnectedMsg.removeClass('active');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,6 +203,19 @@
|
|||
return temp;
|
||||
},
|
||||
|
||||
|
||||
convertPercentToAudioTaper: function (input) {
|
||||
|
||||
// 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 >= 99) { return 20; }
|
||||
|
||||
},
|
||||
|
||||
|
||||
setFaderValue: function (faderId, faderValue) {
|
||||
var $fader = $('[fader-id="' + faderId + '"]');
|
||||
this.setHandlePosition($fader, faderValue);
|
||||
|
|
|
|||
|
|
@ -89,8 +89,8 @@
|
|||
// these are raw scores as reported by client (round trip times)
|
||||
if (score == null) return "purple";
|
||||
if (0 < score && score <= 40) return "green";
|
||||
if (40 < score && score <= 80) return "yellow";
|
||||
if (80 < score && score <= 120) return "red";
|
||||
if (40 < score && score <= 70) return "yellow";
|
||||
if (70 < score && score <= 100) return "red";
|
||||
return "blue";
|
||||
}
|
||||
|
||||
|
|
@ -98,8 +98,8 @@
|
|||
// these are raw scores as reported by client (round trip times)
|
||||
if (score == null) return "missing";
|
||||
if (0 < score && score <= 40) return "good";
|
||||
if (40 < score && score <= 80) return "moderate";
|
||||
if (80 < score && score <= 120) return "poor";
|
||||
if (40 < score && score <= 70) return "moderate";
|
||||
if (70 < score && score <= 100) return "poor";
|
||||
return "unacceptable";
|
||||
}
|
||||
|
||||
|
|
@ -126,6 +126,7 @@
|
|||
var mVals, musician, renderings='';
|
||||
var instr_logos, instr;
|
||||
var follows, followVals, aFollow;
|
||||
var myAudioLatency = musicianList.my_audio_latency;
|
||||
|
||||
for (ii=0, len=musicians.length; ii < len; ii++) {
|
||||
musician = musicians[ii];
|
||||
|
|
@ -165,7 +166,7 @@
|
|||
};
|
||||
var musician_actions = context.JK.fillTemplate(aTemplate, actionVals);
|
||||
|
||||
var joined_score = musician['joined_score']
|
||||
var full_score = musician['full_score'];
|
||||
mVals = {
|
||||
avatar_url: context.JK.resolveAvatarUrl(musician.photo_url),
|
||||
profile_url: "/client#/profile/" + musician.id,
|
||||
|
|
@ -180,22 +181,27 @@
|
|||
musician_id: musician['id'],
|
||||
musician_follow_template: follows,
|
||||
musician_action_template: musician_actions,
|
||||
musician_one_way_score: score_to_text(joined_score),
|
||||
musician_score_color: score_to_color(joined_score),
|
||||
musician_score_color_alt: score_to_color_alt(joined_score)
|
||||
musician_one_way_score: score_to_text(full_score),
|
||||
musician_score_color: score_to_color(full_score),
|
||||
musician_score_color_alt: score_to_color_alt(full_score)
|
||||
|
||||
};
|
||||
var musician_row = context.JK.fillTemplate(mTemplate, mVals);
|
||||
renderings += musician_row;
|
||||
var $rendering = $(context.JK.fillTemplate(mTemplate, mVals))
|
||||
|
||||
var $offsetParent = $results.closest('.content');
|
||||
var options = {positions: ['top', 'bottom', 'right', 'left'], offsetParent: $offsetParent};
|
||||
var scoreOptions = {positions: ['right', 'top', 'bottom', 'left'], offsetParent: $offsetParent, width:'600px'};
|
||||
context.JK.helpBubble($('.follower-count', $rendering), 'musician-follower-count', {}, options);
|
||||
context.JK.helpBubble($('.friend-count', $rendering), 'musician-friend-count', {}, options);
|
||||
context.JK.helpBubble($('.recording-count', $rendering), 'musician-recording-count', {}, options);
|
||||
context.JK.helpBubble($('.session-count', $rendering), 'musician-session-count', {}, options);
|
||||
context.JK.helpBubble($('.score-count', $rendering), 'musician-score-count',
|
||||
{full_score: full_score ? Math.round(full_score / 2) : null, my_gear_latency: myAudioLatency, their_gear_latency:musician['audio_latency'], internet_latency: musician['score']},
|
||||
scoreOptions)
|
||||
|
||||
$results.append($rendering);
|
||||
}
|
||||
|
||||
var $renderings = $(renderings);
|
||||
var $offsetParent = $results.closest('.content');
|
||||
var options = {positions: ['top', 'bottom', 'right', 'left'], offsetParent: $offsetParent}
|
||||
context.JK.helpBubble($('.follower-count', $renderings), 'musician-follower-count', {}, options);
|
||||
context.JK.helpBubble($('.friend-count', $renderings), 'musician-friend-count', {}, options);
|
||||
context.JK.helpBubble($('.recording-count', $renderings), 'musician-recording-count', {}, options);
|
||||
context.JK.helpBubble($('.session-count', $renderings), 'musician-session-count', {}, options);
|
||||
$results.append($renderings);
|
||||
|
||||
$('.search-m-friend').on('click', friendMusician);
|
||||
$('.search-m-follow').on('click', followMusician);
|
||||
|
|
|
|||
|
|
@ -273,11 +273,11 @@
|
|||
}
|
||||
|
||||
function clearResults() {
|
||||
$('table#sessions-active').empty();
|
||||
$('table#sessions-active').find("tr:gt(0)").remove();
|
||||
|
||||
currentScheduledSessionsPage = 0;
|
||||
$ssScroller.infinitescroll('resume');
|
||||
$('table#sessions-scheduled').empty();
|
||||
$('table#sessions-scheduled').find("tr:gt(0)").remove();
|
||||
$ssNoMoreEntries.hide();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,9 @@
|
|||
SHOW_SIGNUP : 'show_signup',
|
||||
SHOW_SIGNIN : 'show_signin',
|
||||
RSVP_SUBMITTED: 'rsvp_submitted',
|
||||
RSVP_CANCELED : 'rsvp_canceled'
|
||||
}
|
||||
RSVP_CANCELED : 'rsvp_canceled',
|
||||
USER_UPDATED : 'user_updated'
|
||||
};
|
||||
|
||||
context.JK.ALERT_NAMES = {
|
||||
NO_EVENT : 0,
|
||||
|
|
|
|||
|
|
@ -883,11 +883,7 @@
|
|||
dataType: "json",
|
||||
contentType: 'application/json',
|
||||
url: "/api/users/progression/certified_gear",
|
||||
processData: false,
|
||||
data: JSON.stringify({
|
||||
success: options.success,
|
||||
reason: options.reason
|
||||
})
|
||||
data: JSON.stringify(options)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1184,17 +1180,6 @@
|
|||
});
|
||||
}
|
||||
|
||||
function updateAudioLatency(options) {
|
||||
var id = getId(options);
|
||||
return $.ajax({
|
||||
type: "POST",
|
||||
url: '/api/users/' + id + '/audio_latency',
|
||||
dataType: "json",
|
||||
contentType: 'application/json',
|
||||
data: options,
|
||||
});
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
return self;
|
||||
}
|
||||
|
|
@ -1298,7 +1283,6 @@
|
|||
this.getChatMessages = getChatMessages;
|
||||
this.createDiagnostic = createDiagnostic;
|
||||
this.getLatencyTester = getLatencyTester;
|
||||
this.updateAudioLatency = updateAudioLatency;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -218,9 +218,10 @@
|
|||
hoveraction: val.session_id ? "session" : "",
|
||||
avatar_url: context.JK.resolveAvatarUrl(val.photo_url),
|
||||
text: val.formatted_msg,
|
||||
date: $.timeago(val.created_at)
|
||||
date: $.timeago(val.created_at),
|
||||
userId: val.source_user_id
|
||||
});
|
||||
|
||||
|
||||
$list.append(notificationHtml);
|
||||
|
||||
// val.description contains the notification record's description value from the DB (i.e., type)
|
||||
|
|
@ -307,10 +308,15 @@
|
|||
}
|
||||
|
||||
var $action_btn = $notification.find($btnNotificationAction);
|
||||
$action_btn.text(actionText);
|
||||
$action_btn.click(function() {
|
||||
callback(payload);
|
||||
});
|
||||
if (actionText === '') {
|
||||
$action_btn.hide();
|
||||
}
|
||||
else {
|
||||
$action_btn.text(actionText);
|
||||
$action_btn.click(function() {
|
||||
callback(payload);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
else if (type === context.JK.MessageType.MUSICIAN_RECORDING_SAVED || type === context.JK.MessageType.BAND_RECORDING_SAVED) {
|
||||
|
|
|
|||
|
|
@ -117,14 +117,14 @@
|
|||
var firstSession = function() {
|
||||
var $firstSession = $scheduledSessions.children().first().find('input[name="scheduled-session-info"]');
|
||||
$firstSession.attr('checked', 'checked');
|
||||
createSessionSettings.selectedSessionId = $firstSession.attr('id');
|
||||
createSessionSettings.selectedSessionId = $firstSession.attr('data-session-id');
|
||||
|
||||
};
|
||||
if (createSessionSettings.selectedSessionId == null) {
|
||||
firstSession();
|
||||
}
|
||||
else {
|
||||
var $selectedSession = $scheduledSessions.children().first().find('input[name="scheduled-session-info"][id="' + createSessionSettings.selectedSessionId + '"]');
|
||||
var $selectedSession = $scheduledSessions.children().first().find('input[name="scheduled-session-info"][data-session-id="' + createSessionSettings.selectedSessionId + '"]');
|
||||
if ($selectedSession.length)
|
||||
$selectedSession.attr('checked', 'checked');
|
||||
else
|
||||
|
|
@ -349,6 +349,7 @@
|
|||
|
||||
function beforeMoveStep1() {
|
||||
if (createSessionSettings.createType == 'start-scheduled') {
|
||||
createSessionSettings.selectedSessionId = $scheduledSessions.find('.iradio_minimal.checked input[name="scheduled-session-info"]').attr('data-session-id');
|
||||
var session = scheduledSessions[createSessionSettings.selectedSessionId];
|
||||
|
||||
if(session == null) {
|
||||
|
|
@ -382,7 +383,7 @@
|
|||
var startTime = new Date(session.scheduled_start);
|
||||
var diffTime = startTime.getTime() - currentTime.getTime();
|
||||
if (diffTime > ONE_HOUR) {
|
||||
var confirmDialog = new context.JK.ConfirmDialog(app, "Start Session Now",
|
||||
var confirmDialog = new context.JK.ConfirmDialog(app, "START SESSION NOW",
|
||||
"You are starting a session that is scheduled to begin more than one hour from now. Are you sure you want to do this?",
|
||||
"Future Session", moveToFinish);
|
||||
confirmDialog.initialize();
|
||||
|
|
@ -416,7 +417,7 @@
|
|||
createSessionSettings.startTime = $startTimeList.val();
|
||||
createSessionSettings.endTime = $endTimeList.val();
|
||||
createSessionSettings.notations = [];
|
||||
createSessionSettings.selectedSessionId = $scheduledSessions.find('input[name="scheduled-session-info"][checked="checked"]').attr('id');
|
||||
createSessionSettings.selectedSessionId = $scheduledSessions.find('.iradio_minimal.checked input[name="scheduled-session-info"]').attr('data-session-id');
|
||||
createSessionSettings.timezone.value = $timezoneList.val();
|
||||
createSessionSettings.timezone.label = $timezoneList.get(0).options[$timezoneList.get(0).selectedIndex].text;
|
||||
createSessionSettings.recurring_mode.label = $recurringModeList.get(0).options[$recurringModeList.get(0).selectedIndex].text;
|
||||
|
|
@ -569,22 +570,28 @@
|
|||
}
|
||||
|
||||
function startSessionClicked() {
|
||||
var $btn = $(this);
|
||||
if($btn.is('.disabled')) { return false; }
|
||||
$btn.addClass('disabled')
|
||||
|
||||
if(willOptionStartSession()) {
|
||||
gearUtils.guardAgainstInvalidConfiguration(app)
|
||||
.fail(function() {
|
||||
$btn.removeClass('disabled')
|
||||
app.notify(
|
||||
{ title: "Unable to Start New Session",
|
||||
text: "You can only start a session once you have working audio gear and a tested internet connection."
|
||||
})
|
||||
})
|
||||
.done(function(){
|
||||
startSession();
|
||||
startSession($btn);
|
||||
});
|
||||
}
|
||||
else {
|
||||
startSession();
|
||||
startSession($btn);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// did the user have to pick the RSVP slots explicitely?
|
||||
|
|
@ -592,7 +599,7 @@
|
|||
return createType == "immediately" || createType == "schedule-future" || createType == "rsvp";
|
||||
}
|
||||
|
||||
function startSession() {
|
||||
function startSession($startBtn) {
|
||||
|
||||
var data = {};
|
||||
|
||||
|
|
@ -682,36 +689,12 @@
|
|||
}
|
||||
|
||||
var joinSession = function(sessionId) {
|
||||
var tracks = context.JK.TrackHelpers.getUserTracks(context.jamClient);
|
||||
|
||||
var options = {};
|
||||
options.client_id = app.clientId;
|
||||
options.session_id = sessionId;
|
||||
options.as_musician = true;
|
||||
options.tracks = tracks;
|
||||
rest.joinSession(options)
|
||||
.done(function(response) {
|
||||
var invitationCount = data.invitations.length;
|
||||
|
||||
context.location = '/client#/session/' + sessionId;
|
||||
|
||||
context.JK.GA.trackSessionCount(data.musician_access, data.fan_access, invitationCount);
|
||||
context.JK.GA.trackSessionMusicians(context.JK.GA.SessionCreationTypes.create);
|
||||
})
|
||||
.fail(function(jqXHR) {
|
||||
var handled = false;
|
||||
if(jqXHR.status = 422) {
|
||||
var response = JSON.parse(jqXHR.responseText);
|
||||
if(response["errors"] && response["errors"]["tracks"] && response["errors"]["tracks"][0] == "Please select at least one track") {
|
||||
app.notifyAlert("No Inputs Configured", $('<span>You will need to reconfigure your audio device.</span>'));
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
if(!handled) {
|
||||
app.notifyServerError(jqXHR, "Unable to Create Session");
|
||||
}
|
||||
})
|
||||
var invitationCount = data.invitations.length;
|
||||
context.JK.GA.trackSessionCount(data.musician_access, data.fan_access, invitationCount);
|
||||
|
||||
// we redirect to the session screen, which handles the REST call to POST /participants.
|
||||
logger.debug("joining session screen: " + sessionId)
|
||||
context.location = '/client#/session/' + sessionId;
|
||||
};
|
||||
|
||||
if (createSessionSettings.createType == 'start-scheduled') {
|
||||
|
|
@ -734,9 +717,10 @@
|
|||
}
|
||||
})
|
||||
.fail(function(jqXHR){
|
||||
$startBtn.removeClass('disabled');
|
||||
logger.debug("unable to schedule a session")
|
||||
app.notifyServerError(jqXHR, "Unable to schedule a session");
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -315,6 +315,12 @@
|
|||
null,
|
||||
true);
|
||||
}
|
||||
else if(xhr.status == 422) {
|
||||
var response = JSON.parse(jqXHR.responseText);
|
||||
if(response["errors"] && response["errors"]["tracks"] && response["errors"]["tracks"][0] == "Please select at least one track") {
|
||||
app.notifyAlert("No Inputs Configured", $('<span>You will need to reconfigure your audio device.</span>'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
app.notifyServerError(xhr, 'Unable to Join Session');
|
||||
}
|
||||
|
|
@ -569,6 +575,7 @@
|
|||
$voiceChat.show();
|
||||
$voiceChat.attr('mixer-id', mixer.id);
|
||||
var $voiceChatGain = $voiceChat.find('.voicechat-gain');
|
||||
$voiceChatGain.attr('mixer-id', mixer.id);
|
||||
var $voiceChatMute = $voiceChat.find('.voicechat-mute').attr('mixer-id', mixer.id);
|
||||
var gainPercent = percentFromMixerValue(
|
||||
mixer.range_low, mixer.range_high, mixer.volume_left);
|
||||
|
|
@ -1147,8 +1154,8 @@
|
|||
// volumes on trackVolumeObject, and call SetControlState to stick.
|
||||
var sliderValue = percentToMixerValue(
|
||||
currentMixerRangeMin, currentMixerRangeMax, volumePercent);
|
||||
context.trackVolumeObject.volL = sliderValue;
|
||||
context.trackVolumeObject.volR = sliderValue;
|
||||
context.trackVolumeObject.volL = context.JK.FaderHelpers.convertPercentToAudioTaper(volumePercent);
|
||||
context.trackVolumeObject.volR = context.JK.FaderHelpers.convertPercentToAudioTaper(volumePercent);
|
||||
// Special case for L2M mix:
|
||||
if (mixerId === '__L2M__') {
|
||||
logger.debug("L2M volumePercent=" + volumePercent);
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@
|
|||
|
||||
var LATENCY = {
|
||||
GOOD : {description: "GOOD", style: "latency-green", min: 0.0, max: 20.0},
|
||||
MEDIUM : {description: "MEDIUM", style: "latency-yellow", min: 20.0, max: 40.0},
|
||||
POOR : {description: "POOR", style: "latency-red", min: 40.0, max: 10000000000.0},
|
||||
UNREACHABLE: {description: "UNREACHABLE", style: "latency-grey", min: -1, max: -1},
|
||||
MEDIUM : {description: "MEDIUM", style: "latency-yellow", min: 20.0, max: 35.0},
|
||||
POOR : {description: "POOR", style: "latency-red", min: 35.0, max: 50},
|
||||
UNACCEPTABLE: {description: "UNACCEPTABLE", style: "latency-grey", min: 50, max: 10000000},
|
||||
UNKNOWN: {description: "UNKNOWN", style: "latency-grey", min: -2, max: -2}
|
||||
};
|
||||
|
||||
|
|
@ -350,7 +350,10 @@
|
|||
}
|
||||
|
||||
function createLatency(user) {
|
||||
var latencyStyle = LATENCY.UNREACHABLE.style, latencyDescription = LATENCY.UNREACHABLE.description
|
||||
|
||||
var latencyStyle;
|
||||
var latencyDescription;
|
||||
|
||||
if (user.id === context.JK.currentUserId) {
|
||||
latencyStyle = LATENCY.GOOD.style, latencyDescription = LATENCY.GOOD.description;
|
||||
}
|
||||
|
|
@ -358,24 +361,25 @@
|
|||
else {
|
||||
var latency = user.latency;
|
||||
|
||||
if (!latency || latency === 1000) {
|
||||
// 1000 is a magical number returned by new scoring API to indicate one or more people in the session have an unknown score
|
||||
if (!latency) {
|
||||
latencyDescription = LATENCY.UNKNOWN.description;
|
||||
latencyStyle = LATENCY.UNKNOWN.style;
|
||||
}
|
||||
else if (latency <= LATENCY.GOOD.max) {
|
||||
latencyDescription = LATENCY.GOOD.description;
|
||||
latencyStyle = LATENCY.GOOD.style;
|
||||
}
|
||||
else if (latency > LATENCY.MEDIUM.min && latency <= LATENCY.MEDIUM.max) {
|
||||
latencyDescription = LATENCY.MEDIUM.description;
|
||||
latencyStyle = LATENCY.MEDIUM.style;
|
||||
}
|
||||
else if (latency > LATENCY.POOR.min && latency <= LATENCY.UNACCEPTABLE.max) {
|
||||
latencyDescription = LATENCY.POOR.description;
|
||||
latencyStyle = LATENCY.POOR.style;
|
||||
}
|
||||
else {
|
||||
if (latency <= LATENCY.GOOD.max) {
|
||||
latencyDescription = LATENCY.GOOD.description;
|
||||
latencyStyle = LATENCY.GOOD.style;
|
||||
}
|
||||
else if (latency > LATENCY.MEDIUM.min && latency <= LATENCY.MEDIUM.max) {
|
||||
latencyDescription = LATENCY.MEDIUM.description;
|
||||
latencyStyle = LATENCY.MEDIUM.style;
|
||||
}
|
||||
else {
|
||||
latencyDescription = LATENCY.POOR.description;
|
||||
latencyStyle = LATENCY.POOR.style;
|
||||
}
|
||||
latencyStyle = LATENCY.UNREACHABLE.style
|
||||
latencyDescription = LATENCY.UNREACHABLE.description
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,17 +87,22 @@
|
|||
var deferred = joinSessionRest(sessionId);
|
||||
|
||||
deferred
|
||||
.done(function(){
|
||||
.done(function(response){
|
||||
logger.debug("calling jamClient.JoinSession");
|
||||
// on temporary disconnect scenarios, a user may already be in a session when they enter this path
|
||||
// so we avoid double counting
|
||||
if(!alreadyInSession()) {
|
||||
// on temporary disconnect scenarios, a user may already be in a session when they enter this path
|
||||
// so we avoid double counting
|
||||
if(response.music_session.participant_count == 1) {
|
||||
context.JK.GA.trackSessionMusicians(context.JK.GA.SessionCreationTypes.create);
|
||||
}
|
||||
else {
|
||||
context.JK.GA.trackSessionMusicians(context.JK.GA.SessionCreationTypes.join);
|
||||
}
|
||||
}
|
||||
|
||||
recordingModel.reset();
|
||||
client.JoinSession({ sessionID: sessionId });
|
||||
refreshCurrentSession();
|
||||
refreshCurrentSession(true);
|
||||
server.registerMessageCallback(context.JK.MessageType.SESSION_JOIN, trackChanges);
|
||||
server.registerMessageCallback(context.JK.MessageType.SESSION_DEPART, trackChanges);
|
||||
server.registerMessageCallback(context.JK.MessageType.TRACKS_CHANGED, trackChanges);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.UserDropdown = function (app) {
|
||||
|
||||
var EVENTS = context.JK.EVENTS;
|
||||
var logger = context.JK.logger;
|
||||
var rest = new JK.Rest();
|
||||
var userMe = null;
|
||||
|
|
@ -53,12 +53,16 @@
|
|||
$('.shortcuts .test-network').on('click', function(e) {
|
||||
app.layout.showDialog('network-test');
|
||||
return false;
|
||||
})
|
||||
});
|
||||
|
||||
$('#header-avatar').on('avatar_changed', function (event, newAvatarUrl) {
|
||||
updateAvatar(newAvatarUrl);
|
||||
event.preventDefault();
|
||||
return false;
|
||||
});
|
||||
$(document).on(EVENTS.USER_UPDATED, function(e, data) {
|
||||
userMe = data;
|
||||
updateHeader();
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -115,5 +119,8 @@
|
|||
loadMe();
|
||||
}
|
||||
this.loadMe = loadMe;
|
||||
|
||||
}
|
||||
})(window, jQuery);
|
||||
})(window, jQuery);
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@
|
|||
cornerRadius: 0,
|
||||
cssStyles: {
|
||||
fontSize: '11px',
|
||||
color: 'white',
|
||||
color: '#cccccc',
|
||||
whiteSpace: 'normal'
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -174,7 +174,10 @@
|
|||
$('.call-to-action').html('Tell the session organizer if you can no longer join this session');
|
||||
$btnAction.html('CANCEL RSVP');
|
||||
$btnAction.click(function(e) {
|
||||
ui.launchRsvpCancelDialog(musicSessionId, rsvp.id);
|
||||
ui.launchRsvpCancelDialog(musicSessionId, rsvp.id)
|
||||
.one(EVENTS.RSVP_CANCELED, function() {
|
||||
location.reload();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -184,7 +187,10 @@
|
|||
$('.call-to-action').html("Tell the session organizer you'd like to play in this session");
|
||||
$btnAction.html('RSVP NOW!');
|
||||
$btnAction.click(function(e) {
|
||||
ui.launchRsvpSubmitDialog(musicSessionId);
|
||||
ui.launchRsvpSubmitDialog(musicSessionId)
|
||||
.one(EVENTS.RSVP_SUBMITTED, function() {
|
||||
location.reload();
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
|
|
@ -201,14 +207,6 @@
|
|||
});
|
||||
|
||||
addLatencyDetails();
|
||||
|
||||
$(document).on(EVENTS.RSVP_SUBMITTED, function() {
|
||||
location.reload();
|
||||
});
|
||||
|
||||
$(document).on(EVENTS.RSVP_CANCELED, function() {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
this.initialize = initialize;
|
||||
|
|
|
|||
|
|
@ -433,7 +433,7 @@
|
|||
|
||||
context.JK.GA.trackAudioTestData(uniqueDeviceName(), context.JK.GA.AudioTestDataReasons.pass, latencyScore);
|
||||
|
||||
rest.userCertifiedGear({success: true, client_id: app.clientId, audio_latency: getLatencyScore()});
|
||||
rest.userCertifiedGear({success: true, client_id: app.clientId, audio_latency: getLatencyScore().latency});
|
||||
}
|
||||
|
||||
function onGearTestFail(e, data) {
|
||||
|
|
|
|||
|
|
@ -269,14 +269,6 @@
|
|||
return result;
|
||||
}
|
||||
|
||||
gearUtils.updateAudioLatency = function(app) {
|
||||
var latency = jamClient.FTUEGetExpectedLatency().latency;
|
||||
return rest.updateAudioLatency({client_id: app.clientId, audio_latency: latency})
|
||||
.fail(function(jqXHR) {
|
||||
app.notifyServerError(jqXHR, "Unable to sync audio latency")
|
||||
});
|
||||
}
|
||||
|
||||
// if the user has a good user network score, immediately returns with a resolved deferred object.
|
||||
// if not, the user will have the network test dialog prompted... once it's closed, then you'll be told reject() if score is still bad, or resolve() if now good
|
||||
gearUtils.guardAgainstBadNetworkScore = function(app) {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
*= require ./searchResults
|
||||
*= require ./clientUpdate
|
||||
*= require ./musician
|
||||
*= require ./help
|
||||
*= require ./jquery-ui-overrides
|
||||
*= require web/audioWidgets
|
||||
*= require web/recordings
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
.screen {
|
||||
.bt-wrapper {
|
||||
.bt-content {
|
||||
|
||||
color:#cccccc;
|
||||
|
||||
font-size:14px;
|
||||
|
||||
p {
|
||||
font-size:14px;
|
||||
}
|
||||
ul {
|
||||
font-size:14px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-left:1em;
|
||||
margin-bottom:.5em;
|
||||
}
|
||||
|
||||
.definition {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.help-musician-score-count {
|
||||
.measurement {
|
||||
|
||||
}
|
||||
|
||||
.measurement-value {
|
||||
font-size:24px;
|
||||
}
|
||||
|
||||
.measurement-absent {
|
||||
font-style:italic;
|
||||
font-size:12px;
|
||||
display:block;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -312,5 +312,14 @@
|
|||
.more-text-available {
|
||||
}
|
||||
}
|
||||
.wrapword{
|
||||
white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 */
|
||||
white-space: -o-pre-wrap; /* Opera 7 */
|
||||
white-space: pre-wrap; /* css-3 */
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -599,9 +599,6 @@ strong {
|
|||
}
|
||||
}
|
||||
|
||||
body.jam.web.welcome .no-websocket-connection {
|
||||
display:none;
|
||||
}
|
||||
body.jam.web.register .no-websocket-connection {
|
||||
body.jam.web .no-websocket-connection {
|
||||
display:none;
|
||||
}
|
||||
|
|
@ -224,7 +224,8 @@ class ApiMusicSessionsController < ApiController
|
|||
response.status = :unprocessable_entity
|
||||
respond_with @music_session
|
||||
else
|
||||
Notification.send_scheduled_session_rescheduled(@music_session)
|
||||
Notification.send_scheduled_session_rescheduled(@music_session) if @music_session.scheduling_info_changed
|
||||
|
||||
respond_with @music_session, responder: ApiResponder, :location => api_session_history_detail_url(@music_session)
|
||||
end
|
||||
else
|
||||
|
|
@ -287,7 +288,8 @@ class ApiMusicSessionsController < ApiController
|
|||
params[:id],
|
||||
params[:client_id],
|
||||
params[:as_musician],
|
||||
params[:tracks]
|
||||
params[:tracks],
|
||||
params[:audio_latency]
|
||||
)
|
||||
|
||||
if @connection.errors.any?
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ class ApiScoringController < ApiController
|
|||
|
||||
conn = Connection.where(client_id: clientid, user_id: current_user.id).first
|
||||
if conn.nil? then render :json => {message: 'session not found'}, :status => 404; return end
|
||||
|
||||
if conn.locidispid.nil? then render :json => {message: 'no locidispid for connection'}, :status => 404; return end
|
||||
|
||||
# if !current_user.id.eql?(conn.user.id) then render :json => {message: 'session not owned by user'}, :status => 403; return end
|
||||
|
||||
result_client_ids = JamRuby::GetWork.get_work_list(conn.locidispid, conn.addr)
|
||||
|
|
|
|||
|
|
@ -534,7 +534,7 @@ class ApiUsersController < ApiController
|
|||
|
||||
if !@user.errors.any?
|
||||
# update audio gear latency information
|
||||
@user.update_audio_latency(connection, params[:audio_latency])
|
||||
@user.update_audio_latency(connection, params[:audio_latency]) if params[:audio_latency]
|
||||
end
|
||||
else
|
||||
@user.failed_qualification(params[:reason])
|
||||
|
|
|
|||
|
|
@ -197,11 +197,11 @@ class UsersController < ApplicationController
|
|||
|
||||
@slides = [
|
||||
Slide.new("JamKazam Overview", "web/carousel_musicians.jpg", "http://www.youtube.com/embed/ylYcvTY9CVo?autoplay=1"),
|
||||
Slide.new("Getting Started", "web/carousel_fans.jpg", "http://www.youtube.com/embed/VexH4834o9I?autoplay=1"),
|
||||
Slide.new("Playing in a Session", "web/carousel_bands.jpg", "http://www.youtube.com/embed/ylYcvTY9CVo?autoplay=1"),
|
||||
Slide.new("Getting Started", "web/carousel_fans.jpg", "http://www.youtube.com/embed/DBo--aj_P1w?autoplay=1"),
|
||||
Slide.new("Playing in a Session", "web/carousel_bands.jpg", "http://www.youtube.com/embed/zJ68hA8-fLA?autoplay=1"),
|
||||
Slide.new("JamKazam Overview", "web/carousel_musicians.jpg", "http://www.youtube.com/embed/ylYcvTY9CVo?autoplay=1"),
|
||||
Slide.new("Getting Started", "web/carousel_fans.jpg", "http://www.youtube.com/embed/VexH4834o9I?autoplay=1"),
|
||||
Slide.new("Playing in a Session", "web/carousel_bands.jpg", "http://www.youtube.com/embed/ylYcvTY9CVo?autoplay=1")
|
||||
Slide.new("Getting Started", "web/carousel_fans.jpg", "http://www.youtube.com/embed/DBo--aj_P1w?autoplay=1"),
|
||||
Slide.new("Playing in a Session", "web/carousel_bands.jpg", "http://www.youtube.com/embed/zJ68hA8-fLA?autoplay=1")
|
||||
]
|
||||
|
||||
@promo_buzz = PromoBuzz.active
|
||||
|
|
@ -228,9 +228,6 @@ class UsersController < ApplicationController
|
|||
|
||||
if !@user.nil? && !@user.errors.any?
|
||||
UserMailer.welcome_message(@user).deliver
|
||||
sign_in @user
|
||||
redirect_to :client
|
||||
return
|
||||
elsif !@user.nil?
|
||||
# new user with validation errors;
|
||||
logger.debug("#{@user} has errors. can not sign in until remedied. #{@user.errors.inspect}")
|
||||
|
|
@ -463,6 +460,9 @@ JS
|
|||
end
|
||||
|
||||
def load_location(remote_ip, location = nil)
|
||||
# useful if you need to repro something on 127.0.0.1
|
||||
# remote_ip = ' 23.119.29.89'
|
||||
|
||||
@location = location
|
||||
|
||||
if @location.nil?
|
||||
|
|
@ -471,7 +471,6 @@ JS
|
|||
|
||||
@location[:country] = "US" if @location[:country].nil?
|
||||
|
||||
# right now we only accept US signups for beta
|
||||
@countriesx = MaxMindManager.countries
|
||||
# populate regions based on current country
|
||||
@regions = MaxMindManager.regions(@location[:country])
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ glue :music_session do
|
|||
attributes :id, :name, :location, :photo_url
|
||||
}
|
||||
|
||||
child(:active_music_session => :music_session) do
|
||||
child(:active_music_session => :active_music_session) do
|
||||
# only show mount info if fan_access is public. Eventually we'll also need to show this in other scenarios, like if invited
|
||||
child({:mount => :mount}, :if => lambda { |music_session| music_session.fan_access}) {
|
||||
attributes :id, :name, :sourced, :listeners, :bitrate, :subtype, :url
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
object @connection
|
||||
|
||||
extends "api_music_sessions/participant_show"
|
||||
|
||||
attributes :music_session_id
|
||||
|
||||
child(:music_session => :music_session) {
|
||||
attributes :id
|
||||
|
||||
node :participant_count do |music_session|
|
||||
music_session.connections.length
|
||||
end
|
||||
}
|
||||
|
|
@ -1,11 +1,8 @@
|
|||
object @connection
|
||||
|
||||
attributes :ip_address, :client_id
|
||||
attributes :ip_address, :client_id, :user_id
|
||||
attribute :aasm_state => :connection_state
|
||||
|
||||
node(:user_id, :if => lambda { |connection| connection.user.friends?(current_user) }) do |connection|
|
||||
connection.user_id
|
||||
end
|
||||
child(:tracks => :tracks) {
|
||||
attributes :id, :instrument_id, :sound
|
||||
}
|
||||
|
|
@ -41,9 +41,13 @@ if @search.musicians_filter_search?
|
|||
node :page_count do |foo|
|
||||
@search.page_count
|
||||
end
|
||||
|
||||
node :my_audio_latency do |user|
|
||||
current_user.last_jam_audio_latency
|
||||
end
|
||||
|
||||
child(:results => :musicians) {
|
||||
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :email, :online, :musician, :photo_url, :biography, :joined_score, :regionname
|
||||
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :email, :online, :musician, :photo_url, :biography, :regionname, :score, :full_score, :audio_latency
|
||||
|
||||
node :is_friend do |musician|
|
||||
@search.is_friend?(musician)
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@
|
|||
|
||||
<div class="field right w45 birth_date">
|
||||
<label>Birth Date:</label>
|
||||
<%= date_select("user", "birth_date", :use_short_month => true, :start_year => 1900, :end_year => Time.now.year - 18, :order => [:month, :day, :year], :default => -25.years.from_now) %>
|
||||
<%= date_select("user", "birth_date", :use_short_month => true, :start_year => 1900, :end_year => Time.now.year - 18, :order => [:month, :day, :year], :default => -25.years.from_now, :html=>{:class => "account-profile-birthdate"} ) %>
|
||||
</div>
|
||||
|
||||
<br class="clearall"/>
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@
|
|||
%td
|
||||
{{data.latency}}
|
||||
.right
|
||||
%a{href: "/client#/profile/{{data.user_id}}", class: 'button-orange left', 'user-id' => "{{data.user_id}}", target: "_blank"} PROFILE
|
||||
%a{href: "/client#/profile/{{data.user_id}}", class: 'button-orange left', 'user-id' => "{{data.user_id}}"} PROFILE
|
||||
%a{href: "#", class: 'button-orange left approveRsvpRequest', 'user-id' => "{{data.user_id}}", 'request-id' => "{{data.request_id}}"} APPROVE
|
||||
%a{href: "#", class: 'button-orange left declineRsvpRequest', 'user-id' => "{{data.user_id}}", 'request-id' => "{{data.request_id}}"} DECLINE
|
||||
.clearall
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@
|
|||
Fan Access:
|
||||
.right-column
|
||||
%select#session-prop-fans-access
|
||||
%option{value: "listen-chat-band"} Fans may listen, chat with the band
|
||||
%option{value: "listen-chat-each", selected: "selected"} Fans may listen, chat with each other
|
||||
%option{value: "no-listen-chat"} Fans may not listen to session
|
||||
.clearall.right-no-left
|
||||
|
|
@ -95,7 +94,6 @@
|
|||
%option{value: "Standard"} Standard
|
||||
%option{value: "Creative Commons"} Creative Commons
|
||||
%option{value: "Offline"} Offline
|
||||
%option{value: "Jamtracks"} Jamtracks
|
||||
.clearall.left-column
|
||||
Notation Files:
|
||||
.right-column
|
||||
|
|
|
|||
|
|
@ -56,4 +56,26 @@
|
|||
|
||||
<script type="text/template" id="template-help-musician-session-count">
|
||||
The number of sessions that this user has played in.
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-help-musician-score-count">
|
||||
<div class="help-musician-score-count">
|
||||
<p>The score shown is the one-way latency (or delay) in milliseconds from you to this user. This score is calculated using the following three values that JamKazam gathers:</p>
|
||||
<ul>
|
||||
<li><span class="definition">Your Audio Gear Latency:</span> <span class="measurement my-gear-latency"><span class="measurement-value">{{data.my_gear_latency ? data.my_gear_latency + ' ms': '13 ms*'}}</span> <span class="measurement-absent">{{data.my_gear_latency ? '' : "(you have not qualified any gear, so we picked an average gear latency)"}}</span></span></li>
|
||||
<li><span class="definition">Their Audio Gear Latency:</span> <span class="measurement their-gear-latency"><span class="measurement-value">{{data.their_gear_latency ? data.their_gear_latency + ' ms': '13 ms*'}}</span> <span class="measurement-absent">{{data.their_gear_latency ? '' : "(they have not qualified any gear, so we picked an average gear latency)"}}</span></span></li>
|
||||
<li><span class="definition">Round-trip Internet Latency:</span> <span class="measurement internet-latency"><span class="measurement-value">{{data.internet_latency ? data.internet_latency + ' ms': '?'}}</span> <span class="measurement-absent">{{data.internet_latency ? '' : "(we have not scored you with this user yet)"}}</span></span></li>
|
||||
</ul>
|
||||
<p> <span class="definition">Total One-Way Latency:</span> <span class="measurement my-gear-latency"><span class="measurement-value">( {{data.my_gear_latency ? data.my_gear_latency: '13'}} + {{data.their_gear_latency ? data.their_gear_latency: '13'}} + {{data.internet_latency ? data.internet_latency: '?'}} ) / 2 = {{data.full_score ? data.full_score + ' ms' : "?"}}</span> <span class="measurement-absent">{{data.full_score ? '' : "(when we don't know internet latency, we don't try to guess your one-way latency)"}}</span></span>
|
||||
|
||||
<p>We categorize this score as good, fair, poor, unacceptable, or unknown. Those categories are defined as follows:
|
||||
<ul>
|
||||
<li><span class="definition">Good:</span> 20ms or less <img src="/assets/content/icon_green_score.png" /></li>
|
||||
<li><span class="definition">Fair:</span> 20ms to 35ms <img src="/assets/content/icon_yellow_score.png" /></li>
|
||||
<li><span class="definition">Poor:</span> 35ms to 50ms <img src="/assets/content/icon_red_score.png" /></li>
|
||||
<li><span class="definition">Unacceptable:</span> Above 50ms <img src="/assets/content/icon_blue_score.png" /></li>
|
||||
<li><span class="definition">Unknown:</span> No internet score is available between you and them. <img src="/assets/content/icon_purple_score.png" /></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</script>
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
<% end -%>
|
||||
|
||||
<!-- Session Row Template -->
|
||||
<script type="text/template" id="template-find-musician-row">
|
||||
<script type="text/template" id="template-find-musician-row">
|
||||
<div class="profile-band-list-result musician-list-result">
|
||||
<div class="f11" data-hint="container">
|
||||
<div class="left" style="width:63px;margin-top:-12px;">
|
||||
|
|
|
|||
|
|
@ -456,7 +456,7 @@
|
|||
<!-- Scheduled session template -->
|
||||
<script type="text/template" id="template-scheduled-session">
|
||||
<li>
|
||||
<input type="radio" name="scheduled-session-info" id="{{data.id}}" value="false" />
|
||||
<input type="radio" name="scheduled-session-info" data-session-id="{{data.id}}" value="false" />
|
||||
<label for="{{data.id}}" class="radio-text w85">
|
||||
{{data.scheduled_start}} : {{data.name}}
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<h2><%= title %></h2>
|
||||
<br/>
|
||||
<div class="findsession-container">
|
||||
<table class="findsession-table" cellspacing="0" cellpadding="0" border="0">
|
||||
<table id="<%= category %>" class="findsession-table" cellspacing="0" cellpadding="0" border="0">
|
||||
<!-- header -->
|
||||
<tr>
|
||||
<th align="left" width="30%">SESSION</th>
|
||||
|
|
@ -12,7 +12,4 @@
|
|||
</tr>
|
||||
<!-- session row goes here -->
|
||||
</table>
|
||||
<table id="<%= category %>" class="findsession-table" cellspacing="0" cellpadding="0" border="0">
|
||||
<!-- session row goes here -->
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -105,7 +105,7 @@
|
|||
<span>Chat is available when in session.</span>
|
||||
</div>
|
||||
<div class="chat-list-scroller">
|
||||
<div class="previous-chat-list">
|
||||
<div class="previous-chat-list wrapword">
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-sender">
|
||||
|
|
@ -208,8 +208,8 @@
|
|||
<!-- Notification panel template -->
|
||||
<script type="text/template" id="template-notification-panel">
|
||||
<li session-id="{sessionId}" notification-id="{notificationId}">
|
||||
<div class="avatar-small"><img src="{avatar_url}" /></div>
|
||||
<div session-id="{sessionId}" hoveraction="session" class="note-text">
|
||||
<div class="avatar-small" user-id="{userId}" hoveraction="musician"><img src="{avatar_url}" /></div>
|
||||
<div session-id="{sessionId}" hoveraction="hoveraction" class="note-text">
|
||||
{text}<br/>
|
||||
<em>{date}</em>
|
||||
<div class="note-delete">
|
||||
|
|
|
|||
|
|
@ -170,11 +170,11 @@
|
|||
%h2 Tutorial Videos
|
||||
%ul
|
||||
%li
|
||||
%a{href: '#'} Creating a Session
|
||||
%a{href: 'https://www.youtube.com/watch?v=EZZuGcDUoWk', rel: 'external'} Creating a Session
|
||||
%li
|
||||
%a{href: '#'} Finding a Session
|
||||
%a{href: 'https://www.youtube.com/watch?v=xWponSJo-GU', rel: 'external'} Finding a Session
|
||||
%li
|
||||
%a{href: '#'} Playing in a Session
|
||||
%a{href: 'https://www.youtube.com/watch?v=zJ68hA8-fLA', rel: 'external'} Playing in a Session
|
||||
%li
|
||||
%a{href: '#'} Connecting with Other Musicians
|
||||
%li
|
||||
|
|
@ -184,9 +184,9 @@
|
|||
%h2 Other Valuable Resource Links
|
||||
%ul
|
||||
%li
|
||||
%a{href: '#'} JamKazam Support Center
|
||||
%a{href: 'https://jamkazam.desk.com/', rel: 'external'} JamKazam Support Center
|
||||
%li
|
||||
%a{href: '#'} JamKazam Community Forum
|
||||
%a{href: 'http://forums.jamkazam.com', rel: 'external'} JamKazam Community Forum
|
||||
.wizard-buttons
|
||||
|
||||
%script{type: 'text/template', id: 'template-ftuesteps'}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
.error{:style => 'display:none'} You must select at least 1 instrument.
|
||||
.rsvp-instruments
|
||||
|
||||
.comment-instructions Enter a message to the other musicians in the session (optional):
|
||||
.comment-instructions Enter a message to the other musicians in the session (optional):
|
||||
%textarea.txtComment{rows: '2', placeholder: 'Enter a comment...'}
|
||||
.buttons
|
||||
.left
|
||||
|
|
|
|||
|
|
@ -96,7 +96,10 @@
|
|||
%br/
|
||||
.left.w65.ib.still-needed
|
||||
%strong Still Needed
|
||||
- if @open_slots.blank?
|
||||
- if @music_session.is_unstructured_rsvp
|
||||
.clearall.left.w100.h20.ib.mb10
|
||||
Open to any instrument
|
||||
- elsif @open_slots.blank?
|
||||
.clearall.left.w100.h20.ib.mb10
|
||||
All slots are taken
|
||||
- else
|
||||
|
|
@ -108,8 +111,6 @@
|
|||
= slot.instrument_id.capitalize
|
||||
= "(#{slot.proficiency_desc})"
|
||||
|
||||
|
||||
%br{:clear => "all"}/
|
||||
%br/
|
||||
.w65.ib.invited
|
||||
%strong Invited
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@
|
|||
<option value="" <%= @location[:state].blank? ? "selected" : "" %>>State/Province</option>
|
||||
<% @regions.each do |region| %>
|
||||
<% unless region.blank? %>
|
||||
<option value="<%= region[:region] %>" <%= @location[:state] == region ? "selected" : "" %>><%= region[:name] %></option>
|
||||
<option value="<%= region[:region] %>" <%= @location[:state] == region[:region] ? "selected" : "" %>><%= region[:name] %></option>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
<% provide(:title, 'Signup Confirmation') %>
|
||||
|
||||
<% if @user.nil? %>
|
||||
<h1>Signup Already Confirmed</h1>
|
||||
|
||||
<h1>Signup Already Confirmed</h1>
|
||||
<br/>
|
||||
<br/>
|
||||
<p>
|
||||
|
|
@ -13,5 +12,14 @@
|
|||
Please proceed to <%= link_to 'the home page', root_path %> and log in.
|
||||
</p>
|
||||
<% else %>
|
||||
The server had a problem. Please try to confirm your email later.
|
||||
<h1>Email Confirmed</h1>
|
||||
<br/>
|
||||
<br/>
|
||||
<p>
|
||||
You have successfully confirmed this email address.
|
||||
</p>
|
||||
<br/>
|
||||
<p>
|
||||
You can proceed to the website <%= link_to 'home page', root_path %> and sign in, or close this page in your browser.
|
||||
</p>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -249,5 +249,10 @@ if defined?(Bundler)
|
|||
# recording upload/download configs
|
||||
config.max_track_upload_failures = 10
|
||||
config.max_track_part_upload_failures = 3
|
||||
|
||||
# scoring thresholds for 'full score', which is 1/2 your gear + 1/2 their gear + and 1/2 ping time
|
||||
config.max_good_full_score = 20
|
||||
config.max_yellow_full_score = 35
|
||||
config.max_red_full_score = 50
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
@@log = Logging.logger['EmailInitializer']
|
||||
|
||||
ActionMailer::Base.raise_delivery_errors = true
|
||||
ActionMailer::Base.delivery_method = Rails.env == "test" ? :test : :smtp
|
||||
ActionMailer::Base.delivery_method = GenericState.allow_emails? ? :smtp : :test
|
||||
ActionMailer::Base.smtp_settings = {
|
||||
:address => Rails.application.config.email_smtp_address,
|
||||
:port => Rails.application.config.email_smtp_port,
|
||||
|
|
@ -8,4 +10,6 @@ ActionMailer::Base.smtp_settings = {
|
|||
:user_name => Rails.application.config.email_smtp_user_name,
|
||||
:password => Rails.application.config.email_smtp_password ,
|
||||
:enable_starttls_auto => Rails.application.config.email_smtp_starttls_auto
|
||||
}
|
||||
}
|
||||
|
||||
@@log.debug("ActionMailer.delivery_method = #{ActionMailer::Base.delivery_method}")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
Resque.redis = Rails.application.config.redis_host
|
||||
|
||||
|
||||
if !$rails_rake_task && Rails.env == 'development' && ENV['RUN_JOBS_INLINE'] == '1'
|
||||
if !$rails_rake_task && Rails.env == 'development' && (ENV['RUN_JOBS_INLINE'] == '1' || ENV['RUN_INLINE_JOBS'] == '1')
|
||||
|
||||
Thread.new do
|
||||
system('INTERVAL=1 bundle exec rake all_jobs')
|
||||
|
|
|
|||
|
|
@ -16,7 +16,20 @@ task :scheduler => :environment do
|
|||
|
||||
# The schedule doesn't need to be stored in a YAML, it just needs to
|
||||
# be a hash. YAML is usually the easiest.
|
||||
Resque.schedule = YAML.load_file(File.join(File.dirname(__FILE__), '../..', 'config/scheduler.yml'))
|
||||
config = YAML.load_file(File.join(File.dirname(__FILE__), '../..', 'config/scheduler.yml'))
|
||||
|
||||
if File.exist? File.join(File.dirname(__FILE__), '../..', 'config/scheduler_override.yml')
|
||||
puts "scheduler_override file found. loading..."
|
||||
override = YAML.load_file(File.join(File.dirname(__FILE__), '../..', 'config/scheduler_override.yml'))
|
||||
if override # override will be false if file is empty
|
||||
config.merge!(override)
|
||||
else
|
||||
puts "schedule_override is empty... skipping..."
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Resque.schedule = config
|
||||
|
||||
# If your schedule already has +queue+ set for each job, you don't
|
||||
# need to require your jobs. This can be an advantage since it's
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ describe ApiMusicSessionsController do
|
|||
ams = FactoryGirl.create(:active_music_session, creator: other)
|
||||
other_conn.join_the_session(ams.music_session, true, tracks, other, 10)
|
||||
other_conn.errors.any?.should be_false
|
||||
Score.createx(conn.locidispid, conn.client_id, conn.addr, other_conn.locidispid, other_conn.client_id, other_conn.addr, network_score, nil)
|
||||
Score.createx(conn.locidispid, conn.client_id, conn.addr, other_conn.locidispid, other_conn.client_id, other_conn.addr, network_score, nil, nil, {auserid: user.id, buserid: other.id})
|
||||
|
||||
get :ams_index, {client_id: conn.client_id}
|
||||
json = JSON.parse(response.body, :symbolize_names => true)
|
||||
|
|
@ -75,7 +75,7 @@ describe ApiMusicSessionsController do
|
|||
third_user.last_jam_audio_latency = 10 # RSVP's are an 'offline' search, meaning they use user.last_jam_audio_latency instead of connection.last_jam_audio_latency
|
||||
third_user.last_jam_locidispid = conn.locidispid
|
||||
third_user.save!
|
||||
Score.createx(conn.locidispid, conn.client_id, conn.addr, other_conn.locidispid, other_conn.client_id, other_conn.addr, network_score, nil)
|
||||
Score.createx(conn.locidispid, conn.client_id, conn.addr, other_conn.locidispid, other_conn.client_id, other_conn.addr, network_score, nil, nil, {auserid: user.id, buserid: other.id})
|
||||
|
||||
# set up a second RSVP (other than the creators, pointing to the third_user)
|
||||
rsvp_slot = FactoryGirl.create(:rsvp_slot, music_session: ams.music_session, instrument: Instrument.find('piano'))
|
||||
|
|
@ -139,14 +139,14 @@ describe ApiMusicSessionsController do
|
|||
it "scores with invitees and RSVP's" do
|
||||
# create a session with someone else in it, but no score
|
||||
sms = FactoryGirl.create(:music_session, creator: other)
|
||||
Score.createx(conn.locidispid, conn.client_id, conn.addr, other_conn.locidispid, other_conn.client_id, other_conn.addr, network_score, nil)
|
||||
Score.createx(conn.locidispid, conn.client_id, conn.addr, other_conn.locidispid, other_conn.client_id, other_conn.addr, network_score, nil, nil, {auserid: user.id, buserid: other.id})
|
||||
|
||||
invitee = FactoryGirl.create(:user, last_jam_audio_latency: 30, last_jam_locidispid: 3)
|
||||
FactoryGirl.create(:friendship, user: other, friend: invitee)
|
||||
FactoryGirl.create(:friendship, user: invitee, friend: other)
|
||||
FactoryGirl.create(:invitation, sender:other, receiver:invitee, music_session: sms)
|
||||
|
||||
Score.createx(invitee.last_jam_locidispid, 'immaterial', 1, conn.locidispid, conn.client_id, conn.addr, network_score, nil)
|
||||
Score.createx(invitee.last_jam_locidispid, 'immaterial', 1, conn.locidispid, conn.client_id, conn.addr, network_score, nil, nil, {auserid: invitee.id, buserid: user.id})
|
||||
|
||||
get :sms_index, {client_id: conn.client_id}
|
||||
json = JSON.parse(response.body, :symbolize_names => true)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ include ActionDispatch::TestProcess # added for artifact_update http://stackover
|
|||
|
||||
FactoryGirl.define do
|
||||
factory :user, :class => JamRuby::User do
|
||||
ignore do
|
||||
specific_instruments nil
|
||||
end
|
||||
|
||||
sequence(:email) { |n| "person_#{n}@example.com"}
|
||||
sequence(:first_name) { |n| "Person" }
|
||||
sequence(:last_name) { |n| "#{n}" }
|
||||
|
|
@ -33,8 +37,15 @@ FactoryGirl.define do
|
|||
end
|
||||
end
|
||||
|
||||
before(:create) do |user|
|
||||
user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user)
|
||||
before(:create) do |user, evaluator|
|
||||
if evaluator.specific_instruments
|
||||
evaluator.specific_instruments.each do |instrument|
|
||||
puts "burp: "
|
||||
user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user, instrument: instrument)
|
||||
end
|
||||
else
|
||||
user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user)
|
||||
end
|
||||
end
|
||||
|
||||
factory :user_two_instruments do
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ describe "Account", :js => true, :type => :feature, :capybara_feature => true do
|
|||
emulate_client
|
||||
sign_in_poltergeist user
|
||||
visit "/client#/account"
|
||||
|
||||
|
||||
find('div.account-mid.identity')
|
||||
end
|
||||
|
||||
|
|
@ -99,7 +99,25 @@ describe "Account", :js => true, :type => :feature, :capybara_feature => true do
|
|||
user.subscribe_email.should be_false
|
||||
user.first_name.should == "Bobby"
|
||||
user.last_name.should == "Toes"
|
||||
should have_selector('#profile #user', text: 'Bobby Toes')
|
||||
|
||||
|
||||
# Update birth date and check updated birth date
|
||||
|
||||
jk_select("Jan", '#account-edit-profile-form #user_birth_date_2i')
|
||||
jk_select("12", '#account-edit-profile-form #user_birth_date_3i')
|
||||
jk_select("1960", '#account-edit-profile-form #user_birth_date_1i')
|
||||
find("#account-edit-profile-submit").trigger(:click)
|
||||
|
||||
user.reload
|
||||
user.birth_date == "1960-01-12"
|
||||
|
||||
should have_selector('#account-edit-profile-form .birth_date li.active', text: "Jan")
|
||||
should have_selector('#account-edit-profile-form .birth_date li.active', text: "12")
|
||||
should have_selector('#account-edit-profile-form .birth_date li.active', text: "1960")
|
||||
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
describe "unsuccessfully" do
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ describe "Avatar", :js => true, :type => :feature, :capybara_feature => true do
|
|||
visit "/client#/account/profile/avatar"
|
||||
|
||||
find('#account-edit-avatar-upload')
|
||||
# within_frame 'filepicker_dialog' do
|
||||
# attach_file '#fileUploadInput', Rails.root.join('spec', 'files', 'avatar.jpg')
|
||||
# end
|
||||
end
|
||||
|
||||
it {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,31 @@ describe "Create Session UI", :js => true, :type => :feature, :capybara_feature
|
|||
end
|
||||
end
|
||||
|
||||
# VRFS-1976
|
||||
it "create two sessions; select the second one" do
|
||||
|
||||
# this is tricky because of iCheck and it's custom way of dealing with 'checked' state
|
||||
start1 = 15.minutes.ago
|
||||
session1 = FactoryGirl.create(:music_session, description: 'My Session 1', creator: user1, scheduled_start: start1)
|
||||
session2 = FactoryGirl.create(:music_session, description: 'My Session 2', creator: user1)
|
||||
|
||||
|
||||
in_client(user1) do
|
||||
# should reload the session page, so that we see new sessions
|
||||
visit '/client#/createSession'
|
||||
|
||||
# pick the second session
|
||||
page.find("#scheduled-session-list input[data-session-id='#{session2.id}'] + ins").trigger(:click)
|
||||
page.find('.btn-next').trigger(:click)
|
||||
|
||||
# check if future dialog is showing; if so, accept
|
||||
accept = first('#btn-confirm-ok', text: "START SESSION NOW")
|
||||
accept.trigger(:click) if accept
|
||||
|
||||
page.find('#session-description-disp', text: 'My Session 2')
|
||||
end
|
||||
end
|
||||
|
||||
describe "step 1" do
|
||||
it "initial status" do
|
||||
in_client(user1) do
|
||||
|
|
|
|||
|
|
@ -62,12 +62,13 @@ describe "Create Session", :js => true, :type => :feature, :capybara_feature =>
|
|||
let (:first_session) { FactoryGirl.create(:music_session, creator: user1, scheduled_start: now + 75.minutes) }
|
||||
|
||||
it "warns the user that session starts in the future, and user can start session" do
|
||||
pending "play with Database.db_timezone on this one"
|
||||
sleep 1
|
||||
find('.btn-next').trigger(:click)
|
||||
sleep 2
|
||||
expect(page).to have_selector('h1', text: 'Future Session')
|
||||
expect(page).to have_content "Are you sure"
|
||||
find('#btn-confirm-ok', text: 'Start Session Now').trigger(:click)
|
||||
find('#btn-confirm-ok', text: 'START SESSION NOW').trigger(:click)
|
||||
sleep 1
|
||||
expect(page).to have_selector('.session-step-title', text: 'Review & Confirm')
|
||||
expect(page).to have_content first_session.name
|
||||
|
|
|
|||
|
|
@ -10,6 +10,22 @@ describe "Feed", :js => true, :type => :feature, :capybara_feature => true do
|
|||
end
|
||||
|
||||
|
||||
describe "regressions" do
|
||||
|
||||
describe "mount" do
|
||||
|
||||
it "should render when has mount" do
|
||||
# regression for VRFS-1987
|
||||
ams = FactoryGirl.create(:active_music_session)
|
||||
FactoryGirl.create(:icecast_mount, music_session_id: ams.id)
|
||||
|
||||
fast_signin user, "/client#/feed"
|
||||
find('#feedScreen')
|
||||
|
||||
find(".feed-entry.music-session-history-entry[data-music-session='#{ams.id}']")
|
||||
end
|
||||
end
|
||||
end
|
||||
describe "sessions" do
|
||||
|
||||
before(:each) do
|
||||
|
|
@ -40,6 +56,7 @@ describe "Feed", :js => true, :type => :feature, :capybara_feature => true do
|
|||
# it " and render artist hover bubble"
|
||||
# end
|
||||
|
||||
|
||||
it "should render stats" do
|
||||
visit "/client#/feed"
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ describe "Profile History", :js => true, :type => :feature, :capybara_feature =>
|
|||
|
||||
before do
|
||||
MusicSession.delete_all
|
||||
ActiveMusicSession.delete_all
|
||||
Recording.delete_all
|
||||
set_login_cookie user
|
||||
stub_const("APP_CONFIG", web_config)
|
||||
|
|
|
|||
|
|
@ -207,6 +207,10 @@ describe "Session Info", :js => true, :type => :feature, :capybara_feature => tr
|
|||
|
||||
# musician_access = true, approval_required = false
|
||||
it "should allow anyone to view for 'at will' option after session starts" do
|
||||
@music_session.musician_access = true
|
||||
@music_session.approval_required = false
|
||||
@music_session.save!
|
||||
|
||||
# attempt to access with musician who was invited but didn't RSVP
|
||||
fast_signin(@session_invitee, @url)
|
||||
ensure_success({:show_cta => true, :button_text => 'RSVP NOW!'})
|
||||
|
|
@ -235,6 +239,10 @@ describe "Session Info", :js => true, :type => :feature, :capybara_feature => tr
|
|||
|
||||
# musician_access = true, approval_required = true
|
||||
it "should allow anyone to view for 'join by approval' option after session starts" do
|
||||
@music_session.musician_access = true
|
||||
@music_session.approval_required = true
|
||||
@music_session.save!
|
||||
|
||||
# attempt to access with musician who was invited but didn't RSVP
|
||||
fast_signin(@session_invitee, @url)
|
||||
ensure_success({:show_cta => true, :button_text => 'RSVP NOW!'})
|
||||
|
|
|
|||
|
|
@ -7,12 +7,21 @@ describe "Signup", :js => true, :type => :feature, :capybara_feature => true do
|
|||
before(:each) do
|
||||
@mac_client = FactoryGirl.create(:artifact_update)
|
||||
UserMailer.deliveries.clear
|
||||
MaxMindManager.create_phony_database
|
||||
end
|
||||
|
||||
describe "signup page" do
|
||||
before { visit signup_path }
|
||||
|
||||
it { should have_selector('h2', text: 'Create a JamKazam account') }
|
||||
it "should initialize successfully" do
|
||||
should have_selector('h2', text: 'Create a JamKazam account')
|
||||
|
||||
# we should see these locations in the signup form already chosen
|
||||
location = GeoIpLocations.lookup('127.0.0.1')
|
||||
find('.field.country .easydropdown .selected', text:location[:country])
|
||||
find('.field.state .easydropdown .selected', text:location[:state])
|
||||
find('.field.city .easydropdown .selected', text:location[:city])
|
||||
end
|
||||
|
||||
describe "with valid musician information" do
|
||||
before do
|
||||
|
|
@ -30,7 +39,12 @@ describe "Signup", :js => true, :type => :feature, :capybara_feature => true do
|
|||
it {
|
||||
should have_title("JamKazam | Congratulations")
|
||||
should have_content("You have successfully registered as a JamKazam musician.")
|
||||
User.find_by_email('newuser1@jamkazam.com').musician_instruments.length.should == 1
|
||||
user = User.find_by_email('newuser1@jamkazam.com')
|
||||
user.musician_instruments.length.should == 1
|
||||
location = GeoIpLocations.lookup('127.0.0.1')
|
||||
user.country.should == location[:country]
|
||||
user.state.should == location[:state]
|
||||
user.city.should == location[:city]
|
||||
# an email is sent on no-invite signup
|
||||
UserMailer.deliveries.length.should == 1
|
||||
UserMailer.deliveries[0].html_part.body.include?("To confirm this email address")== 1
|
||||
|
|
@ -45,10 +59,10 @@ describe "Signup", :js => true, :type => :feature, :capybara_feature => true do
|
|||
end
|
||||
|
||||
it {
|
||||
should have_title("JamKazam")
|
||||
should have_selector('h2', text: "musicians")
|
||||
should have_title("Signup Confirmation")
|
||||
should have_selector('h1', text: "Email Confirmed")
|
||||
UserMailer.deliveries.length.should == 1
|
||||
UserMailer.deliveries[0].html_part.body.include?("Following are links to some resources")== 1
|
||||
UserMailer.deliveries[0].html_part.body.include?("Following are links to some resources") == 1
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
|
|
@ -61,7 +61,7 @@ describe "Musician Search API", :type => :api do
|
|||
it "gets musicians for long-distance locations" do
|
||||
get_query({score_limit: Search::GOOD_SCORE})
|
||||
good_response
|
||||
(json['musicians'] || []).count.should == 5
|
||||
(json['musicians'] || []).count.should == 3 # only users that have the 10 latency are low enough
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -106,7 +106,7 @@ describe "Musician Search API", :type => :api do
|
|||
f5.save
|
||||
|
||||
expect(@user4.followers.count).to be 3
|
||||
get_query
|
||||
get_query(orderby: :followed)
|
||||
good_response
|
||||
musician = json["musicians"][0]
|
||||
expect(musician["id"]).to eq(@user4.id)
|
||||
|
|
|
|||
|
|
@ -6,13 +6,18 @@ def bputs(msg)
|
|||
end
|
||||
end
|
||||
|
||||
bputs "before simplecov"
|
||||
|
||||
require 'simplecov'
|
||||
bputs "before rubygems"
|
||||
require 'rubygems'
|
||||
bputs "before omniauth"
|
||||
#require 'spork'
|
||||
require 'omniauth'
|
||||
#uncomment the following line to use spork with the debugger
|
||||
#require 'spork/ext/ruby-debug'
|
||||
|
||||
|
||||
ENV["RAILS_ENV"] ||= 'test'
|
||||
|
||||
bputs "before activerecord load"
|
||||
|
|
@ -28,7 +33,6 @@ bputs "before db_config load"
|
|||
db_config = YAML::load(File.open('config/database.yml'))["test"]
|
||||
# initialize ActiveRecord's db connection\
|
||||
|
||||
|
||||
bputs "before recreate db"
|
||||
SpecDb::recreate_database(db_config)
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,22 @@ def web_config
|
|||
def max_track_part_upload_failures
|
||||
3
|
||||
end
|
||||
|
||||
def icecast_hardcoded_source_password
|
||||
'blueberryjam'
|
||||
end
|
||||
|
||||
def max_good_full_score
|
||||
20
|
||||
end
|
||||
|
||||
def max_yellow_full_score
|
||||
35
|
||||
end
|
||||
|
||||
def max_red_full_score
|
||||
50
|
||||
end
|
||||
end
|
||||
klass.new
|
||||
end
|
||||
|
|
|
|||
|
|
@ -543,7 +543,7 @@ def change_session_genre #randomly just change it
|
|||
find('#session-settings-dialog') # ensure the dialog is visible
|
||||
within('#session-settings-dialog') do
|
||||
wait_for_ajax
|
||||
@new_genre = get_options(here).-(["Select Genre"]).-(selected_genres).sample.to_s
|
||||
@new_genre = get_options(here).-(["Select Genre", "Unspecified"]).-(selected_genres).sample.to_s
|
||||
jk_select(@new_genre, '#session-settings-dialog select[name="genres"]')
|
||||
wait_for_ajax
|
||||
find('#session-settings-dialog-submit').trigger(:click)
|
||||
|
|
|
|||
|
|
@ -1,462 +1,509 @@
|
|||
/*!
|
||||
* iCheck v0.9.1, http://git.io/uhUPMA
|
||||
* =================================
|
||||
* Powerful jQuery plugin for checkboxes and radio buttons customization
|
||||
* iCheck v1.0.2, http://git.io/arlzeA
|
||||
* ===================================
|
||||
* Powerful jQuery and Zepto plugin for checkboxes and radio buttons customization
|
||||
*
|
||||
* (c) 2013 Damir Foy, http://damirfoy.com
|
||||
* (c) 2013 Damir Sultanov, http://fronteed.com
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
// Cached vars
|
||||
var _iCheck = 'iCheck',
|
||||
_iCheckHelper = _iCheck + '-helper',
|
||||
_checkbox = 'checkbox',
|
||||
_radio = 'radio',
|
||||
_checked = 'checked',
|
||||
_unchecked = 'un' + _checked,
|
||||
_disabled = 'disabled',
|
||||
_determinate = 'determinate',
|
||||
_indeterminate = 'in' + _determinate,
|
||||
_update = 'update',
|
||||
_type = 'type',
|
||||
_click = 'click',
|
||||
_touch = 'touchbegin.i touchend.i',
|
||||
_add = 'addClass',
|
||||
_remove = 'removeClass',
|
||||
_callback = 'trigger',
|
||||
_label = 'label',
|
||||
_cursor = 'cursor',
|
||||
_mobile = /ipad|iphone|ipod|android|blackberry|windows phone|opera mini|silk/i.test(navigator.userAgent);
|
||||
// Cached vars
|
||||
var _iCheck = 'iCheck',
|
||||
_iCheckHelper = _iCheck + '-helper',
|
||||
_checkbox = 'checkbox',
|
||||
_radio = 'radio',
|
||||
_checked = 'checked',
|
||||
_unchecked = 'un' + _checked,
|
||||
_disabled = 'disabled',
|
||||
_determinate = 'determinate',
|
||||
_indeterminate = 'in' + _determinate,
|
||||
_update = 'update',
|
||||
_type = 'type',
|
||||
_click = 'click',
|
||||
_touch = 'touchbegin.i touchend.i',
|
||||
_add = 'addClass',
|
||||
_remove = 'removeClass',
|
||||
_callback = 'trigger',
|
||||
_label = 'label',
|
||||
_cursor = 'cursor',
|
||||
_mobile = /ipad|iphone|ipod|android|blackberry|windows phone|opera mini|silk/i.test(navigator.userAgent);
|
||||
|
||||
// Plugin init
|
||||
$.fn[_iCheck] = function(options, fire) {
|
||||
// Plugin init
|
||||
$.fn[_iCheck] = function(options, fire) {
|
||||
|
||||
// Walker
|
||||
var handle = ':' + _checkbox + ', :' + _radio,
|
||||
stack = $(),
|
||||
walker = function(object) {
|
||||
object.each(function() {
|
||||
var self = $(this);
|
||||
// Walker
|
||||
var handle = 'input[type="' + _checkbox + '"], input[type="' + _radio + '"]',
|
||||
stack = $(),
|
||||
walker = function(object) {
|
||||
object.each(function() {
|
||||
var self = $(this);
|
||||
|
||||
if (self.is(handle)) {
|
||||
stack = stack.add(self);
|
||||
} else {
|
||||
stack = stack.add(self.find(handle));
|
||||
};
|
||||
});
|
||||
};
|
||||
if (self.is(handle)) {
|
||||
stack = stack.add(self);
|
||||
} else {
|
||||
stack = stack.add(self.find(handle));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Check if we should operate with some method
|
||||
if (/^(check|uncheck|toggle|indeterminate|determinate|disable|enable|update|destroy)$/i.test(options)) {
|
||||
// Check if we should operate with some method
|
||||
if (/^(check|uncheck|toggle|indeterminate|determinate|disable|enable|update|destroy)$/i.test(options)) {
|
||||
|
||||
// Normalize method's name
|
||||
options = options.toLowerCase();
|
||||
// Normalize method's name
|
||||
options = options.toLowerCase();
|
||||
|
||||
// Find checkboxes and radio buttons
|
||||
walker(this);
|
||||
// Find checkboxes and radio buttons
|
||||
walker(this);
|
||||
|
||||
return stack.each(function() {
|
||||
if (options == 'destroy') {
|
||||
tidy(this, 'ifDestroyed');
|
||||
} else {
|
||||
operate($(this), true, options);
|
||||
};
|
||||
return stack.each(function() {
|
||||
var self = $(this);
|
||||
|
||||
// Fire method's callback
|
||||
if ($.isFunction(fire)) {
|
||||
fire();
|
||||
};
|
||||
});
|
||||
|
||||
// Customization
|
||||
} else if (typeof options == 'object' || !options) {
|
||||
|
||||
// Check if any options were passed
|
||||
var settings = $.extend({
|
||||
checkedClass: _checked,
|
||||
disabledClass: _disabled,
|
||||
indeterminateClass: _indeterminate,
|
||||
labelHover: true
|
||||
}, options),
|
||||
|
||||
selector = settings.handle,
|
||||
hoverClass = settings.hoverClass || 'hover',
|
||||
focusClass = settings.focusClass || 'focus',
|
||||
activeClass = settings.activeClass || 'active',
|
||||
labelHover = !!settings.labelHover,
|
||||
labelHoverClass = settings.labelHoverClass || 'hover',
|
||||
|
||||
// Setup clickable area
|
||||
area = ('' + settings.increaseArea).replace('%', '') | 0;
|
||||
|
||||
// Selector limit
|
||||
if (selector == _checkbox || selector == _radio) {
|
||||
handle = ':' + selector;
|
||||
};
|
||||
|
||||
// Clickable area limit
|
||||
if (area < -50) {
|
||||
area = -50;
|
||||
};
|
||||
|
||||
// Walk around the selector
|
||||
walker(this);
|
||||
|
||||
return stack.each(function() {
|
||||
|
||||
// If already customized
|
||||
tidy(this);
|
||||
|
||||
var self = $(this),
|
||||
node = this,
|
||||
id = node.id,
|
||||
|
||||
// Layer styles
|
||||
offset = -area + '%',
|
||||
size = 100 + (area * 2) + '%',
|
||||
layer = {
|
||||
position: 'absolute',
|
||||
top: offset,
|
||||
left: offset,
|
||||
display: 'block',
|
||||
width: size,
|
||||
height: size,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
background: '#fff',
|
||||
border: 0,
|
||||
opacity: 0
|
||||
},
|
||||
|
||||
// Choose how to hide input
|
||||
hide = _mobile ? {
|
||||
position: 'absolute',
|
||||
visibility: 'hidden'
|
||||
} : area ? layer : {
|
||||
position: 'absolute',
|
||||
opacity: 0
|
||||
},
|
||||
|
||||
// Get proper class
|
||||
className = node[_type] == _checkbox ? settings.checkboxClass || 'i' + _checkbox : settings.radioClass || 'i' + _radio,
|
||||
|
||||
// Find assigned labels
|
||||
label = $(_label + '[for="' + id + '"]').add(self.closest(_label)),
|
||||
|
||||
// Wrap input
|
||||
parent = self.wrap('<div class="' + className + '"/>')[_callback]('ifCreated').parent().append(settings.insert),
|
||||
|
||||
// Layer addition
|
||||
helper = $('<ins class="' + _iCheckHelper + '"/>').css(layer).appendTo(parent);
|
||||
|
||||
// Finalize customization
|
||||
self.data(_iCheck, {o: settings, s: self.attr('style')}).css(hide);
|
||||
!!settings.inheritClass && parent[_add](node.className);
|
||||
!!settings.inheritID && id && parent.attr('id', _iCheck + '-' + id);
|
||||
parent.css('position') == 'static' && parent.css('position', 'relative');
|
||||
operate(self, true, _update);
|
||||
|
||||
// Label events
|
||||
if (label.length) {
|
||||
label.on(_click + '.i mouseenter.i mouseleave.i ' + _touch, function(event) {
|
||||
var type = event[_type],
|
||||
item = $(this);
|
||||
|
||||
// Do nothing if input is disabled
|
||||
if (!node[_disabled]) {
|
||||
|
||||
// Click
|
||||
if (type == _click) {
|
||||
operate(self, false, true);
|
||||
|
||||
// Hover state
|
||||
} else if (labelHover) {
|
||||
|
||||
// mouseleave|touchend
|
||||
if (/ve|nd/.test(type)) {
|
||||
parent[_remove](hoverClass);
|
||||
item[_remove](labelHoverClass);
|
||||
} else {
|
||||
parent[_add](hoverClass);
|
||||
item[_add](labelHoverClass);
|
||||
};
|
||||
};
|
||||
|
||||
if (_mobile) {
|
||||
event.stopPropagation();
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Input events
|
||||
self.on(_click + '.i focus.i blur.i keyup.i keydown.i keypress.i', function(event) {
|
||||
var type = event[_type],
|
||||
key = event.keyCode;
|
||||
|
||||
// Click
|
||||
if (type == _click) {
|
||||
return false;
|
||||
|
||||
// Keydown
|
||||
} else if (type == 'keydown' && key == 32) {
|
||||
if (!(node[_type] == _radio && node[_checked])) {
|
||||
if (node[_checked]) {
|
||||
off(self, _checked);
|
||||
} else {
|
||||
on(self, _checked);
|
||||
};
|
||||
};
|
||||
|
||||
return false;
|
||||
|
||||
// Keyup
|
||||
} else if (type == 'keyup' && node[_type] == _radio) {
|
||||
!node[_checked] && on(self, _checked);
|
||||
|
||||
// Focus/blur
|
||||
} else if (/us|ur/.test(type)) {
|
||||
parent[type == 'blur' ? _remove : _add](focusClass);
|
||||
};
|
||||
});
|
||||
|
||||
// Helper events
|
||||
helper.on(_click + ' mousedown mouseup mouseover mouseout ' + _touch, function(event) {
|
||||
var type = event[_type],
|
||||
|
||||
// mousedown|mouseup
|
||||
toggle = /wn|up/.test(type) ? activeClass : hoverClass;
|
||||
|
||||
// Do nothing if input is disabled
|
||||
if (!node[_disabled]) {
|
||||
|
||||
// Click
|
||||
if (type == _click) {
|
||||
operate(self, false, true);
|
||||
|
||||
// Active and hover states
|
||||
} else {
|
||||
|
||||
// State is on
|
||||
if (/wn|er|in/.test(type)) {
|
||||
|
||||
// mousedown|mouseover|touchbegin
|
||||
parent[_add](toggle);
|
||||
|
||||
// State is off
|
||||
} else {
|
||||
parent[_remove](toggle + ' ' + activeClass);
|
||||
};
|
||||
|
||||
// Label hover
|
||||
if (label.length && labelHover && toggle == hoverClass) {
|
||||
|
||||
// mouseout|touchend
|
||||
label[/ut|nd/.test(type) ? _remove : _add](labelHoverClass);
|
||||
};
|
||||
};
|
||||
|
||||
if (_mobile) {
|
||||
event.stopPropagation();
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
};
|
||||
});
|
||||
});
|
||||
if (options == 'destroy') {
|
||||
tidy(self, 'ifDestroyed');
|
||||
} else {
|
||||
return this;
|
||||
};
|
||||
};
|
||||
operate(self, true, options);
|
||||
}
|
||||
|
||||
// Do something with inputs
|
||||
function operate(input, direct, method) {
|
||||
var node = input[0];
|
||||
state = /er/.test(method) ? _indeterminate : /bl/.test(method) ? _disabled : _checked,
|
||||
active = method == _update ? {
|
||||
checked: node[_checked],
|
||||
disabled: node[_disabled],
|
||||
indeterminate: input.attr(_indeterminate) == 'true' || input.attr(_determinate) == 'false'
|
||||
} : node[state];
|
||||
// Fire method's callback
|
||||
if ($.isFunction(fire)) {
|
||||
fire();
|
||||
}
|
||||
});
|
||||
|
||||
// Check, disable or indeterminate
|
||||
if (/^(ch|di|in)/.test(method) && !active) {
|
||||
on(input, state);
|
||||
// Customization
|
||||
} else if (typeof options == 'object' || !options) {
|
||||
|
||||
// Uncheck, enable or determinate
|
||||
} else if (/^(un|en|de)/.test(method) && active) {
|
||||
off(input, state);
|
||||
// Check if any options were passed
|
||||
var settings = $.extend({
|
||||
checkedClass: _checked,
|
||||
disabledClass: _disabled,
|
||||
indeterminateClass: _indeterminate,
|
||||
labelHover: true
|
||||
}, options),
|
||||
|
||||
// Update
|
||||
} else if (method == _update) {
|
||||
selector = settings.handle,
|
||||
hoverClass = settings.hoverClass || 'hover',
|
||||
focusClass = settings.focusClass || 'focus',
|
||||
activeClass = settings.activeClass || 'active',
|
||||
labelHover = !!settings.labelHover,
|
||||
labelHoverClass = settings.labelHoverClass || 'hover',
|
||||
|
||||
// Handle states
|
||||
for (var state in active) {
|
||||
if (active[state]) {
|
||||
on(input, state, true);
|
||||
// Setup clickable area
|
||||
area = ('' + settings.increaseArea).replace('%', '') | 0;
|
||||
|
||||
// Selector limit
|
||||
if (selector == _checkbox || selector == _radio) {
|
||||
handle = 'input[type="' + selector + '"]';
|
||||
}
|
||||
|
||||
// Clickable area limit
|
||||
if (area < -50) {
|
||||
area = -50;
|
||||
}
|
||||
|
||||
// Walk around the selector
|
||||
walker(this);
|
||||
|
||||
return stack.each(function() {
|
||||
var self = $(this);
|
||||
|
||||
// If already customized
|
||||
tidy(self);
|
||||
|
||||
var node = this,
|
||||
id = node.id,
|
||||
|
||||
// Layer styles
|
||||
offset = -area + '%',
|
||||
size = 100 + (area * 2) + '%',
|
||||
layer = {
|
||||
position: 'absolute',
|
||||
top: offset,
|
||||
left: offset,
|
||||
display: 'block',
|
||||
width: size,
|
||||
height: size,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
background: '#fff',
|
||||
border: 0,
|
||||
opacity: 0
|
||||
},
|
||||
|
||||
// Choose how to hide input
|
||||
hide = _mobile ? {
|
||||
position: 'absolute',
|
||||
visibility: 'hidden'
|
||||
} : area ? layer : {
|
||||
position: 'absolute',
|
||||
opacity: 0
|
||||
},
|
||||
|
||||
// Get proper class
|
||||
className = node[_type] == _checkbox ? settings.checkboxClass || 'i' + _checkbox : settings.radioClass || 'i' + _radio,
|
||||
|
||||
// Find assigned labels
|
||||
label = $(_label + '[for="' + id + '"]').add(self.closest(_label)),
|
||||
|
||||
// Check ARIA option
|
||||
aria = !!settings.aria,
|
||||
|
||||
// Set ARIA placeholder
|
||||
ariaID = _iCheck + '-' + Math.random().toString(36).substr(2,6),
|
||||
|
||||
// Parent & helper
|
||||
parent = '<div class="' + className + '" ' + (aria ? 'role="' + node[_type] + '" ' : ''),
|
||||
helper;
|
||||
|
||||
// Set ARIA "labelledby"
|
||||
if (aria) {
|
||||
label.each(function() {
|
||||
parent += 'aria-labelledby="';
|
||||
|
||||
if (this.id) {
|
||||
parent += this.id;
|
||||
} else {
|
||||
this.id = ariaID;
|
||||
parent += ariaID;
|
||||
}
|
||||
|
||||
parent += '"';
|
||||
});
|
||||
}
|
||||
|
||||
// Wrap input
|
||||
parent = self.wrap(parent + '/>')[_callback]('ifCreated').parent().append(settings.insert);
|
||||
|
||||
// Layer addition
|
||||
helper = $('<ins class="' + _iCheckHelper + '"/>').css(layer).appendTo(parent);
|
||||
|
||||
// Finalize customization
|
||||
self.data(_iCheck, {o: settings, s: self.attr('style')}).css(hide);
|
||||
!!settings.inheritClass && parent[_add](node.className || '');
|
||||
!!settings.inheritID && id && parent.attr('id', _iCheck + '-' + id);
|
||||
parent.css('position') == 'static' && parent.css('position', 'relative');
|
||||
operate(self, true, _update);
|
||||
|
||||
// Label events
|
||||
if (label.length) {
|
||||
label.on(_click + '.i mouseover.i mouseout.i ' + _touch, function(event) {
|
||||
var type = event[_type],
|
||||
item = $(this);
|
||||
|
||||
// Do nothing if input is disabled
|
||||
if (!node[_disabled]) {
|
||||
|
||||
// Click
|
||||
if (type == _click) {
|
||||
if ($(event.target).is('a')) {
|
||||
return;
|
||||
}
|
||||
operate(self, false, true);
|
||||
|
||||
// Hover state
|
||||
} else if (labelHover) {
|
||||
|
||||
// mouseout|touchend
|
||||
if (/ut|nd/.test(type)) {
|
||||
parent[_remove](hoverClass);
|
||||
item[_remove](labelHoverClass);
|
||||
} else {
|
||||
off(input, state, true);
|
||||
};
|
||||
};
|
||||
parent[_add](hoverClass);
|
||||
item[_add](labelHoverClass);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (!direct || method == 'toggle') {
|
||||
if (_mobile) {
|
||||
event.stopPropagation();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Helper or label was clicked
|
||||
if (!direct) {
|
||||
input[_callback]('ifClicked');
|
||||
};
|
||||
// Input events
|
||||
self.on(_click + '.i focus.i blur.i keyup.i keydown.i keypress.i', function(event) {
|
||||
var type = event[_type],
|
||||
key = event.keyCode;
|
||||
|
||||
// Toggle checked state
|
||||
if (active) {
|
||||
if (node[_type] !== _radio) {
|
||||
off(input, state);
|
||||
};
|
||||
} else {
|
||||
on(input, state);
|
||||
};
|
||||
};
|
||||
};
|
||||
// Click
|
||||
if (type == _click) {
|
||||
return false;
|
||||
|
||||
// Add checked, disabled or indeterminate state
|
||||
function on(input, state, keep) {
|
||||
var node = input[0],
|
||||
parent = input.parent(),
|
||||
checked = state == _checked,
|
||||
indeterminate = state == _indeterminate,
|
||||
callback = indeterminate ? _determinate : checked ? _unchecked : 'enabled',
|
||||
regular = option(node, callback + capitalize(node[_type])),
|
||||
specific = option(node, state + capitalize(node[_type]));
|
||||
// Keydown
|
||||
} else if (type == 'keydown' && key == 32) {
|
||||
if (!(node[_type] == _radio && node[_checked])) {
|
||||
if (node[_checked]) {
|
||||
off(self, _checked);
|
||||
} else {
|
||||
on(self, _checked);
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent unnecessary actions
|
||||
if (node[state] !== true) {
|
||||
return false;
|
||||
|
||||
// Toggle assigned radio buttons
|
||||
if (!keep && state == _checked && node[_type] == _radio && node.name) {
|
||||
var form = input.closest('form'),
|
||||
inputs = 'input[name="' + node.name + '"]';
|
||||
// Keyup
|
||||
} else if (type == 'keyup' && node[_type] == _radio) {
|
||||
!node[_checked] && on(self, _checked);
|
||||
|
||||
inputs = form.length ? form.find(inputs) : $(inputs);
|
||||
// Focus/blur
|
||||
} else if (/us|ur/.test(type)) {
|
||||
parent[type == 'blur' ? _remove : _add](focusClass);
|
||||
}
|
||||
});
|
||||
|
||||
inputs.each(function() {
|
||||
if (this !== node && $.data(this, _iCheck)) {
|
||||
off($(this), state);
|
||||
};
|
||||
});
|
||||
};
|
||||
// Helper events
|
||||
helper.on(_click + ' mousedown mouseup mouseover mouseout ' + _touch, function(event) {
|
||||
var type = event[_type],
|
||||
|
||||
// Indeterminate state
|
||||
if (indeterminate) {
|
||||
// mousedown|mouseup
|
||||
toggle = /wn|up/.test(type) ? activeClass : hoverClass;
|
||||
|
||||
// Add indeterminate state
|
||||
node[state] = true;
|
||||
// Do nothing if input is disabled
|
||||
if (!node[_disabled]) {
|
||||
|
||||
// Remove checked state
|
||||
if (node[_checked]) {
|
||||
off(input, _checked, 'force');
|
||||
};
|
||||
// Click
|
||||
if (type == _click) {
|
||||
operate(self, false, true);
|
||||
|
||||
// Checked or disabled state
|
||||
// Active and hover states
|
||||
} else {
|
||||
|
||||
// Add checked or disabled state
|
||||
if (!keep) {
|
||||
node[state] = true;
|
||||
};
|
||||
// State is on
|
||||
if (/wn|er|in/.test(type)) {
|
||||
|
||||
// Remove indeterminate state
|
||||
if (checked && node[_indeterminate]) {
|
||||
off(input, _indeterminate, false);
|
||||
};
|
||||
};
|
||||
// mousedown|mouseover|touchbegin
|
||||
parent[_add](toggle);
|
||||
|
||||
// Trigger callbacks
|
||||
callbacks(input, checked, state, keep);
|
||||
};
|
||||
// State is off
|
||||
} else {
|
||||
parent[_remove](toggle + ' ' + activeClass);
|
||||
}
|
||||
|
||||
// Add proper cursor
|
||||
if (node[_disabled] && !!option(node, _cursor, true)) {
|
||||
parent.find('.' + _iCheckHelper).css(_cursor, 'default');
|
||||
};
|
||||
// Label hover
|
||||
if (label.length && labelHover && toggle == hoverClass) {
|
||||
|
||||
// Add state class
|
||||
parent[_add](specific || option(node, state));
|
||||
// mouseout|touchend
|
||||
label[/ut|nd/.test(type) ? _remove : _add](labelHoverClass);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove regular state class
|
||||
parent[_remove](regular || option(node, callback) || '');
|
||||
};
|
||||
if (_mobile) {
|
||||
event.stopPropagation();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
// Remove checked, disabled or indeterminate state
|
||||
function off(input, state, keep) {
|
||||
var node = input[0],
|
||||
parent = input.parent(),
|
||||
checked = state == _checked,
|
||||
indeterminate = state == _indeterminate,
|
||||
callback = indeterminate ? _determinate : checked ? _unchecked : 'enabled',
|
||||
regular = option(node, callback + capitalize(node[_type])),
|
||||
specific = option(node, state + capitalize(node[_type]));
|
||||
// Do something with inputs
|
||||
function operate(input, direct, method) {
|
||||
var node = input[0],
|
||||
state = /er/.test(method) ? _indeterminate : /bl/.test(method) ? _disabled : _checked,
|
||||
active = method == _update ? {
|
||||
checked: node[_checked],
|
||||
disabled: node[_disabled],
|
||||
indeterminate: input.attr(_indeterminate) == 'true' || input.attr(_determinate) == 'false'
|
||||
} : node[state];
|
||||
|
||||
// Prevent unnecessary actions
|
||||
if (node[state] !== false) {
|
||||
// Check, disable or indeterminate
|
||||
if (/^(ch|di|in)/.test(method) && !active) {
|
||||
on(input, state);
|
||||
|
||||
// Toggle state
|
||||
if (indeterminate || !keep || keep == 'force') {
|
||||
node[state] = false;
|
||||
};
|
||||
// Uncheck, enable or determinate
|
||||
} else if (/^(un|en|de)/.test(method) && active) {
|
||||
off(input, state);
|
||||
|
||||
// Trigger callbacks
|
||||
callbacks(input, checked, callback, keep);
|
||||
};
|
||||
// Update
|
||||
} else if (method == _update) {
|
||||
|
||||
// Add proper cursor
|
||||
if (!node[_disabled] && !!option(node, _cursor, true)) {
|
||||
parent.find('.' + _iCheckHelper).css(_cursor, 'pointer');
|
||||
};
|
||||
// Handle states
|
||||
for (var each in active) {
|
||||
if (active[each]) {
|
||||
on(input, each, true);
|
||||
} else {
|
||||
off(input, each, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove state class
|
||||
parent[_remove](specific || option(node, state) || '');
|
||||
} else if (!direct || method == 'toggle') {
|
||||
|
||||
// Add regular state class
|
||||
parent[_add](regular || option(node, callback));
|
||||
};
|
||||
// Helper or label was clicked
|
||||
if (!direct) {
|
||||
input[_callback]('ifClicked');
|
||||
}
|
||||
|
||||
// Remove all traces
|
||||
function tidy(node, callback) {
|
||||
if ($.data(node, _iCheck)) {
|
||||
var input = $(node);
|
||||
// Toggle checked state
|
||||
if (active) {
|
||||
if (node[_type] !== _radio) {
|
||||
off(input, state);
|
||||
}
|
||||
} else {
|
||||
on(input, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove everything except input
|
||||
input.parent().html(input.attr('style', $.data(node, _iCheck).s || '')[_callback](callback || ''));
|
||||
// Add checked, disabled or indeterminate state
|
||||
function on(input, state, keep) {
|
||||
var node = input[0],
|
||||
parent = input.parent(),
|
||||
checked = state == _checked,
|
||||
indeterminate = state == _indeterminate,
|
||||
disabled = state == _disabled,
|
||||
callback = indeterminate ? _determinate : checked ? _unchecked : 'enabled',
|
||||
regular = option(input, callback + capitalize(node[_type])),
|
||||
specific = option(input, state + capitalize(node[_type]));
|
||||
|
||||
// Unbind events
|
||||
input.off('.i').unwrap();
|
||||
$(_label + '[for="' + node.id + '"]').add(input.closest(_label)).off('.i');
|
||||
};
|
||||
};
|
||||
// Prevent unnecessary actions
|
||||
if (node[state] !== true) {
|
||||
|
||||
// Get some option
|
||||
function option(node, state, regular) {
|
||||
if ($.data(node, _iCheck)) {
|
||||
return $.data(node, _iCheck).o[state + (regular ? '' : 'Class')];
|
||||
};
|
||||
};
|
||||
// Toggle assigned radio buttons
|
||||
if (!keep && state == _checked && node[_type] == _radio && node.name) {
|
||||
var form = input.closest('form'),
|
||||
inputs = 'input[name="' + node.name + '"]';
|
||||
|
||||
// Capitalize some string
|
||||
function capitalize(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
};
|
||||
inputs = form.length ? form.find(inputs) : $(inputs);
|
||||
|
||||
// Executable handlers
|
||||
function callbacks(input, checked, callback, keep) {
|
||||
inputs.each(function() {
|
||||
if (this !== node && $(this).data(_iCheck)) {
|
||||
off($(this), state);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Indeterminate state
|
||||
if (indeterminate) {
|
||||
|
||||
// Add indeterminate state
|
||||
node[state] = true;
|
||||
|
||||
// Remove checked state
|
||||
if (node[_checked]) {
|
||||
off(input, _checked, 'force');
|
||||
}
|
||||
|
||||
// Checked or disabled state
|
||||
} else {
|
||||
|
||||
// Add checked or disabled state
|
||||
if (!keep) {
|
||||
if (checked) {
|
||||
input[_callback]('ifToggled');
|
||||
};
|
||||
node[state] = true;
|
||||
}
|
||||
|
||||
input[_callback]('ifChanged')[_callback]('if' + capitalize(callback));
|
||||
};
|
||||
};
|
||||
})(jQuery);
|
||||
// Remove indeterminate state
|
||||
if (checked && node[_indeterminate]) {
|
||||
off(input, _indeterminate, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger callbacks
|
||||
callbacks(input, checked, state, keep);
|
||||
}
|
||||
|
||||
// Add proper cursor
|
||||
if (node[_disabled] && !!option(input, _cursor, true)) {
|
||||
parent.find('.' + _iCheckHelper).css(_cursor, 'default');
|
||||
}
|
||||
|
||||
// Add state class
|
||||
parent[_add](specific || option(input, state) || '');
|
||||
|
||||
// Set ARIA attribute
|
||||
if (!!parent.attr('role') && !indeterminate) {
|
||||
parent.attr('aria-' + (disabled ? _disabled : _checked), 'true');
|
||||
}
|
||||
|
||||
// Remove regular state class
|
||||
parent[_remove](regular || option(input, callback) || '');
|
||||
}
|
||||
|
||||
// Remove checked, disabled or indeterminate state
|
||||
function off(input, state, keep) {
|
||||
var node = input[0],
|
||||
parent = input.parent(),
|
||||
checked = state == _checked,
|
||||
indeterminate = state == _indeterminate,
|
||||
disabled = state == _disabled,
|
||||
callback = indeterminate ? _determinate : checked ? _unchecked : 'enabled',
|
||||
regular = option(input, callback + capitalize(node[_type])),
|
||||
specific = option(input, state + capitalize(node[_type]));
|
||||
|
||||
// Prevent unnecessary actions
|
||||
if (node[state] !== false) {
|
||||
|
||||
// Toggle state
|
||||
if (indeterminate || !keep || keep == 'force') {
|
||||
node[state] = false;
|
||||
}
|
||||
|
||||
// Trigger callbacks
|
||||
callbacks(input, checked, callback, keep);
|
||||
}
|
||||
|
||||
// Add proper cursor
|
||||
if (!node[_disabled] && !!option(input, _cursor, true)) {
|
||||
parent.find('.' + _iCheckHelper).css(_cursor, 'pointer');
|
||||
}
|
||||
|
||||
// Remove state class
|
||||
parent[_remove](specific || option(input, state) || '');
|
||||
|
||||
// Set ARIA attribute
|
||||
if (!!parent.attr('role') && !indeterminate) {
|
||||
parent.attr('aria-' + (disabled ? _disabled : _checked), 'false');
|
||||
}
|
||||
|
||||
// Add regular state class
|
||||
parent[_add](regular || option(input, callback) || '');
|
||||
}
|
||||
|
||||
// Remove all traces
|
||||
function tidy(input, callback) {
|
||||
if (input.data(_iCheck)) {
|
||||
|
||||
// Remove everything except input
|
||||
input.parent().html(input.attr('style', input.data(_iCheck).s || ''));
|
||||
|
||||
// Callback
|
||||
if (callback) {
|
||||
input[_callback](callback);
|
||||
}
|
||||
|
||||
// Unbind events
|
||||
input.off('.i').unwrap();
|
||||
$(_label + '[for="' + input[0].id + '"]').add(input.closest(_label)).off('.i');
|
||||
}
|
||||
}
|
||||
|
||||
// Get some option
|
||||
function option(input, state, regular) {
|
||||
if (input.data(_iCheck)) {
|
||||
return input.data(_iCheck).o[state + (regular ? '' : 'Class')];
|
||||
}
|
||||
}
|
||||
|
||||
// Capitalize some string
|
||||
function capitalize(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
|
||||
// Executable handlers
|
||||
function callbacks(input, checked, callback, keep) {
|
||||
if (!keep) {
|
||||
if (checked) {
|
||||
input[_callback]('ifToggled');
|
||||
}
|
||||
|
||||
input[_callback]('ifChanged')[_callback]('if' + capitalize(callback));
|
||||
}
|
||||
}
|
||||
})(window.jQuery || window.Zepto);
|
||||
Loading…
Reference in New Issue