Merge branch 'develop' of https://bitbucket.org/jamkazam/jam-cloud into develop
This commit is contained in:
commit
915bb4ad13
|
|
@ -97,7 +97,7 @@ end
|
|||
# gem 'capistrano'
|
||||
|
||||
# To use debugger
|
||||
gem 'debugger'
|
||||
#gem 'debugger' # not working with 2.1.2p95
|
||||
|
||||
group :development, :test do
|
||||
gem 'capybara'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
ActiveAdmin.register_page "Download CSV" do
|
||||
menu :parent => 'Score'
|
||||
|
||||
page_action :create_csv, :method => :post do
|
||||
|
||||
puts params.inspect
|
||||
|
||||
start_time = params[:score_exports][:start]
|
||||
end_time = params[:score_exports][:end]
|
||||
|
||||
if start_time.blank?
|
||||
start_time = '1900-01-01'
|
||||
end
|
||||
start_time = "#{start_time}"
|
||||
if end_time.blank?
|
||||
end_time = (Time.now + 1.days).strftime('%F')
|
||||
else
|
||||
end_time = "#{end_time}"
|
||||
end
|
||||
|
||||
scores = ScoreHistory
|
||||
.select("from_city, from_regions.regionname as from_region_name, from_countries.countryname as from_country_name, from_isp,
|
||||
to_city, to_regions.regionname as to_region_name, to_countries.countryname as to_country_name, to_isp,
|
||||
min(score_histories.score) as min_latency, max(score_histories.score) as max_latency, avg(score_histories.score) as mean_latency, median(CAST(score_histories.score AS NUMERIC)) as median_latency, count(score_histories.score) as score_count")
|
||||
.joins('LEFT JOIN countries AS from_countries ON from_countries.countrycode = from_country')
|
||||
.joins('LEFT JOIN countries AS to_countries ON to_countries.countrycode = to_country')
|
||||
.joins('LEFT JOIN regions AS from_regions ON from_regions.region = from_region')
|
||||
.joins('LEFT JOIN regions AS to_regions ON to_regions.region = to_region')
|
||||
.where("score_dt BETWEEN DATE '#{start_time}' AND DATE '#{end_time}'")
|
||||
.order('from_city, from_regions.regionname, from_countries.countryname, from_isp, to_city, to_regions.regionname, to_countries.countryname, to_isp')
|
||||
.group('from_city, from_regions.regionname, from_countries.countryname, from_isp, to_city, to_regions.regionname, to_countries.countryname, to_isp')
|
||||
.limit(1_000_000)
|
||||
|
||||
|
||||
csv_string = CSV.generate do |csv|
|
||||
csv << ["From Country", "From Region", "From City", "From ISP", "To Country", "To Region", "To City", "To ISP", "Min Latency", "Max Latency", "Median Latency", "Mean Latency", 'Score Count']
|
||||
|
||||
scores.each do |score|
|
||||
puts score.inspect
|
||||
csv << [score.from_country_name, score.from_region_name, score.from_city, score.from_isp,
|
||||
score.to_country_name, score.to_region_name, score.to_city, score.to_isp,
|
||||
score[:min_latency], score[:max_latency], score[:median_latency], score[:mean_latency], score[:score_count]]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
send_data csv_string,
|
||||
:type => 'text/csv; charset=iso-8859-1; header=present',
|
||||
:disposition => "attachment; filename=score_export-#{start_time}-#{end_time}.csv"
|
||||
end
|
||||
|
||||
|
||||
content :title => "Export Score" do
|
||||
columns do
|
||||
column do
|
||||
semantic_form_for :score_exports, :url => admin_download_csv_create_csv_path, :builder => ActiveAdmin::FormBuilder do |f|
|
||||
f.inputs do
|
||||
f.input :start, :as => :datepicker
|
||||
f.input :end, :as => :datepicker
|
||||
end
|
||||
f.actions do
|
||||
f.action :submit, :label => 'Download CSV'
|
||||
end
|
||||
end
|
||||
end
|
||||
column do
|
||||
panel "Usage" do
|
||||
span "Select a start day and end day to generate a CSV with a score summary. Both fields are optional."
|
||||
end
|
||||
panel "Limitation 1" do
|
||||
div do
|
||||
span do "The system limits the number of rows exported to 1,000,000" end
|
||||
end
|
||||
end
|
||||
panel "Limitation 2" do
|
||||
div do
|
||||
span do "This report uses the score_histories table, which can lag up to 1 hour behind data. You can force a score_history sweep by going to" end
|
||||
span do link_to "Resque", "#{Gon.global.prefix}/resque/schedule" end
|
||||
span do " and then clicking 'Queue Now' for ScoreHistorySweeper. When the job count goes from 1 to 0, the score_histories table is now completely up-to-date, and you can make a 'fresh' CSV." end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#panel "Upaid Registrations" do
|
||||
# table_for Registration.unpaid.limit(10).order('created_at desc') do
|
||||
# column "Registration" do |registration|
|
||||
# link_to registration.id, admin_registration_path(registration)
|
||||
# end
|
||||
# column :user
|
||||
# column :tour
|
||||
# column "Payment" do |registration|
|
||||
# status_tag((registration.paid? ? "Received" : "Pending"), (registration.paid? ? :ok : :warning))
|
||||
# end
|
||||
# end
|
||||
#end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -2,36 +2,81 @@ 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
|
||||
|
||||
filter :score
|
||||
filter :score_dt
|
||||
#filter :from_user_id_eq, :as => :autocomplete, :url => "#{Gon.global.prefix}/admin/users/autocomplete_user_email",
|
||||
# :label => "From User", :required => false,
|
||||
# :wrapper_html => { :style => "list-style: none" }
|
||||
|
||||
#autocomplete :user, :email, :full => true, :display_value => :autocomplete_display_name
|
||||
|
||||
filter :from_user_id, as: :string
|
||||
filter :from_latency_tester_id
|
||||
filter :from_isp
|
||||
filter :from_country
|
||||
filter :from_region
|
||||
filter :from_city
|
||||
filter :from_postal
|
||||
filter :from_latitude
|
||||
filter :from_latitude
|
||||
filter :from_longitude
|
||||
filter :to_user_id, as: :string
|
||||
filter :to_latency_tester_id
|
||||
filter :to_isp
|
||||
filter :to_country
|
||||
filter :to_region
|
||||
filter :to_city
|
||||
filter :to_postal
|
||||
filter :to_latitude
|
||||
filter :to_latitude
|
||||
filter :to_longitude
|
||||
|
||||
before_filter only: :index do
|
||||
@per_page = 1_000_000 if request.format == 'text/csv'
|
||||
end
|
||||
|
||||
index do
|
||||
column :score
|
||||
column :score_dt
|
||||
column "Score", :score
|
||||
column "When", :score_dt
|
||||
column "From User", :from_user_id do |score|
|
||||
link_to score.from_user, admin_user_path(score.from_user) if score.from_user_id
|
||||
end
|
||||
column "From Latency Tester", :from_latency_tester_id do |score|
|
||||
link_to score.from_latency_tester_id, admin_latency_testers_path if score.from_latency_tester_id
|
||||
end
|
||||
column "From IP", :from_addr do |score|
|
||||
IPAddr.new(score.from_addr, Socket::AF_INET).to_s if score.from_addr
|
||||
end
|
||||
column "From ISP", :from_isp
|
||||
column "From Country", :from_country
|
||||
column "From Region", :from_region
|
||||
column "From City", :from_city
|
||||
column "From Postal", :from_postal
|
||||
column "From Lat", :from_latitude
|
||||
column "From Long", :from_longitude
|
||||
column "From Client", :from_client_id
|
||||
|
||||
column :from_client_id
|
||||
column :from_user_id
|
||||
column :from_latency_tester_id
|
||||
column :from_addr
|
||||
column :from_isp
|
||||
column :from_country
|
||||
column :from_region
|
||||
column :from_city
|
||||
column :from_postal
|
||||
column :from_latitude
|
||||
column :from_longitude
|
||||
|
||||
column :to_client_id
|
||||
column :to_user_id
|
||||
column :to_latency_tester_id
|
||||
column :to_addr
|
||||
column :to_isp
|
||||
column :to_country
|
||||
column :to_region
|
||||
column :to_city
|
||||
column :to_postal
|
||||
column :to_latitude
|
||||
column :to_longitude
|
||||
column "To User", :to_user_id do |score|
|
||||
link_to score.to_user, admin_user_path(score.to_user) if score.to_user_id
|
||||
end
|
||||
column "To Latency Tester", :to_latency_tester_id do |score|
|
||||
link_to score.to_latency_tester_id, admin_latency_testers_path if score.to_latency_tester_id
|
||||
end
|
||||
column "To IP", :to_addr do |score|
|
||||
IPAddr.new(score.to_addr, Socket::AF_INET).to_s if score.to_addr
|
||||
end
|
||||
column "To ISP", :to_isp
|
||||
column "To Country", :to_country
|
||||
column "To Region", :to_region
|
||||
column "To City", :to_city
|
||||
column "To Postal", :to_postal
|
||||
column "To Lat", :to_latitude
|
||||
column "To Long", :to_longitude
|
||||
column "To Client", :to_client_id
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
return $.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: gon.global.prefix + 'api/mix/' + mixId + '/enqueue',
|
||||
url: gon.global.prefix + '/api/mix/' + mixId + '/enqueue',
|
||||
contentType: 'application/json',
|
||||
processData: false
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
var $link = $(this);
|
||||
restAdmin.tryMixAgain({mix_id: $link.attr('data-mix-id')})
|
||||
.done(function(response) {
|
||||
$link.closest('div.mix-again').find('div.mix-again-dialog').html('<div>Mix enqueued</div><a href="' + gon.global.prefix + 'resque">Resque Web</a>').dialog();
|
||||
$link.closest('div.mix-again').find('div.mix-again-dialog').html('<div>Mix enqueued</div><a href="' + gon.global.prefix + '/resque">Resque Web</a>').dialog();
|
||||
})
|
||||
.error(function(jqXHR) {
|
||||
$link.closest('div.mix-again').find('div.mix-again-dialog').html('Mix failed: ' + jqXHR.responseText).dialog();
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Gon.global.prefix = ENV['RAILS_RELATIVE_URL_ROOT'] || '/'
|
||||
Gon.global.prefix = ENV['RAILS_RELATIVE_URL_ROOT'] || ''
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
bundle exec jam_db up --connopts=dbname:jam host:localhost user:postgres password:postgres --verbose
|
||||
|
|
@ -195,4 +195,8 @@ max_mind_releases.sql
|
|||
score_histories.sql
|
||||
update_sms_index.sql
|
||||
connection_allow_null_locidispid.sql
|
||||
track_user_in_scores.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
|
||||
|
|
@ -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,13 @@
|
|||
-- this results in a rough median; the only problem is that we don't avg if it's an even number. not a big deal truthfully, since eventually you'll have > 5
|
||||
|
||||
DROP VIEW current_scores;
|
||||
CREATE OR REPLACE VIEW current_scores AS
|
||||
|
||||
SELECT * FROM (SELECT * , row_number() OVER (PARTITION BY alocidispid, blocidispid, scorer ORDER BY score DESC) AS pcnum FROM
|
||||
(SELECT * FROM
|
||||
(SELECT percent_rank() over (PARTITION BY alocidispid, blocidispid ORDER BY score ASC) AS pc, * FROM
|
||||
(SELECT * FROM
|
||||
(SELECT *, row_number() OVER (PARTITION BY alocidispid, blocidispid ORDER BY created_at DESC) AS rownum FROM scores) tmp
|
||||
WHERE rownum < 6) AS score_ranked)
|
||||
AS tmp2 WHERE pc <= .5 ORDER BY pc DESC) pcs )
|
||||
AS final WHERE pcnum < 2;
|
||||
|
|
@ -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;
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
-- from here: https://wiki.postgresql.org/wiki/Aggregate_Median
|
||||
CREATE OR REPLACE FUNCTION _final_median(numeric[])
|
||||
RETURNS numeric
|
||||
AS
|
||||
$body$
|
||||
SELECT AVG(val)
|
||||
FROM (
|
||||
SELECT val
|
||||
FROM unnest($1) val
|
||||
ORDER BY 1
|
||||
LIMIT 2 - MOD(array_upper($1, 1), 2)
|
||||
OFFSET CEIL(array_upper($1, 1) / 2.0) - 1
|
||||
) sub;
|
||||
$body$
|
||||
LANGUAGE sql ;
|
||||
-- IMMUTABLE not accepted by pg migrate
|
||||
|
||||
CREATE AGGREGATE median(numeric) (
|
||||
SFUNC=array_append,
|
||||
STYPE=numeric[],
|
||||
FINALFUNC=_final_median,
|
||||
INITCOND='{}'
|
||||
);
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -111,6 +111,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 +410,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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ module JamRuby
|
|||
|
||||
self.table_name = 'score_histories'
|
||||
|
||||
belongs_to :from_user, class_name: 'JamRuby::User', foreign_key: 'from_user_id'
|
||||
belongs_to :to_user, class_name: 'JamRuby::User', foreign_key: 'to_user_id'
|
||||
|
||||
def self.migrate_scores
|
||||
|
||||
generic_state = GenericState.singleton
|
||||
|
|
@ -12,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,
|
||||
|
|
@ -31,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,
|
||||
|
|
|
|||
|
|
@ -98,8 +98,11 @@ module JamRuby
|
|||
M_ORDER_FOLLOWS = ['Most Followed', :followed]
|
||||
M_ORDER_PLAYS = ['Most Plays', :plays]
|
||||
M_ORDER_PLAYING = ['Playing Now', :playing]
|
||||
ORDERINGS = B_ORDERINGS = M_ORDERINGS = [M_ORDER_FOLLOWS, M_ORDER_PLAYS, M_ORDER_PLAYING]
|
||||
B_ORDERING_KEYS = M_ORDERING_KEYS = M_ORDERINGS.collect { |oo| oo[1] }
|
||||
M_ORDER_LATENCY = ['Latency To Me', :latency]
|
||||
M_ORDERINGS = [M_ORDER_LATENCY, M_ORDER_FOLLOWS, M_ORDER_PLAYS]
|
||||
ORDERINGS = B_ORDERINGS = [M_ORDER_FOLLOWS, M_ORDER_PLAYS, M_ORDER_PLAYING]
|
||||
M_ORDERING_KEYS = M_ORDERINGS.collect { |oo| oo[1] }
|
||||
B_ORDERING_KEYS = B_ORDERINGS.collect { |oo| oo[1] }
|
||||
|
||||
DISTANCE_OPTS = B_DISTANCE_OPTS = M_DISTANCE_OPTS = [['Any', 0], [1000.to_s, 1000], [500.to_s, 500], [250.to_s, 250], [100.to_s, 100], [50.to_s, 50], [25.to_s, 25]]
|
||||
|
||||
|
|
@ -150,7 +153,7 @@ module JamRuby
|
|||
# puts "================ user #{user.inspect}"
|
||||
# puts "================ conn #{conn.inspect}"
|
||||
|
||||
rel = User.musicians_geocoded
|
||||
rel = User.musicians # not musicians_geocoded on purpose; we allow 'unknowns' to surface in the search page
|
||||
rel = rel.select('users.*')
|
||||
rel = rel.group('users.id')
|
||||
|
||||
|
|
@ -164,7 +167,7 @@ module JamRuby
|
|||
|
||||
# filter on scores using selections from params
|
||||
# see M_SCORE_OPTS
|
||||
score_limit = TEST_SCORE
|
||||
score_limit = ANY_SCORE
|
||||
l = params[:score_limit]
|
||||
unless l.nil?
|
||||
score_limit = l
|
||||
|
|
@ -182,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'
|
||||
|
|
@ -190,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'
|
||||
|
|
@ -216,16 +220,20 @@ module JamRuby
|
|||
rel = rel.joins("#{score_join} join current_scores on current_scores.alocidispid = users.last_jam_locidispid")
|
||||
.where(['(current_scores.blocidispid = ? or current_scores.blocidispid is null)', locidispid])
|
||||
|
||||
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.joins('LEFT JOIN regions ON regions.countrycode = users.country AND regions.region = users.state')
|
||||
|
||||
rel = rel.select('current_scores.score')
|
||||
rel = rel.group('current_scores.score')
|
||||
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.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)
|
||||
# puts "================ ordering #{ordering}"
|
||||
case ordering
|
||||
when :latency
|
||||
# nothing to do. the sort added below 'current_scores.score ASC NULLS LAST' handles this
|
||||
when :plays # FIXME: double counting?
|
||||
# sel_str = "COUNT(records)+COUNT(sessions) AS play_count, #{sel_str}"
|
||||
rel = rel.select('COUNT(records.id)+COUNT(sessions.id) AS search_play_count')
|
||||
|
|
@ -242,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')
|
||||
|
|
@ -386,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(['created_at >= ? AND users.id != ?', since_date, usr.id])
|
||||
.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)
|
||||
|
||||
|
|
@ -418,7 +426,7 @@ module JamRuby
|
|||
rel = GeoIpLocations.where_latlng(rel, params, current_user)
|
||||
|
||||
sel_str = 'bands.*'
|
||||
case ordering = self.order_param(params)
|
||||
case ordering = self.order_param(params, B_ORDERING_KEYS)
|
||||
when :plays # FIXME: double counting?
|
||||
sel_str = "COUNT(records)+COUNT(msh) AS play_count, #{sel_str}"
|
||||
rel = rel.joins("LEFT JOIN music_sessions AS msh ON msh.band_id = bands.id")
|
||||
|
|
|
|||
|
|
@ -116,6 +116,9 @@ module JamRuby
|
|||
# diagnostics
|
||||
has_many :diagnostics, :class_name => "JamRuby::Diagnostic"
|
||||
|
||||
# score history
|
||||
has_many :from_score_histories, :class_name => "JamRuby::ScoreHistory", foreign_key: 'from_user_id'
|
||||
has_many :to_score_histories, :class_name => "JamRuby::ScoreHistory", foreign_key: 'to_user_id'
|
||||
# This causes the authenticate method to be generated (among other stuff)
|
||||
#has_secure_password
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,10 @@ describe 'Musician search' do
|
|||
@geomusicians << @user4
|
||||
@geomusicians << @user5
|
||||
|
||||
Score.delete_all
|
||||
# from these scores:
|
||||
# user1 has scores other users in user1 location, and with user2, user3, user4
|
||||
# user2 has scores with users in user3 and user4 location
|
||||
# Score.delete_all
|
||||
Score.createx(1, 'a', 1, 1, 'a', 1, 10)
|
||||
Score.createx(1, 'a', 1, 2, 'b', 2, 20)
|
||||
Score.createx(1, 'a', 1, 3, 'c', 3, 30)
|
||||
|
|
@ -43,46 +46,46 @@ describe 'Musician search' do
|
|||
|
||||
it "finds all musicians" do
|
||||
# expects all the musicians (geocoded)
|
||||
results = Search.musician_filter
|
||||
results = Search.musician_filter({score_limit: Search::TEST_SCORE})
|
||||
results.search_type.should == :musicians_filter
|
||||
results.results.count.should == @geomusicians.length
|
||||
results.results.should eq @geomusicians.reverse
|
||||
results.results.count.should == @musicians.length
|
||||
results.results.should eq @musicians.reverse
|
||||
end
|
||||
|
||||
it "finds all musicians page 1" do
|
||||
# expects all the musicians
|
||||
results = Search.musician_filter({page: 1})
|
||||
results = Search.musician_filter({page: 1, score_limit: Search::TEST_SCORE})
|
||||
results.search_type.should == :musicians_filter
|
||||
results.results.count.should == @geomusicians.length
|
||||
results.results.should eq @geomusicians.reverse
|
||||
results.results.count.should == @musicians.length
|
||||
results.results.should eq @musicians.reverse
|
||||
end
|
||||
|
||||
it "finds all musicians page 2" do
|
||||
# expects no musicians (all fit on page 1)
|
||||
results = Search.musician_filter({page: 2})
|
||||
results = Search.musician_filter({page: 2, score_limit: Search::TEST_SCORE})
|
||||
results.search_type.should == :musicians_filter
|
||||
results.results.count.should == 0
|
||||
end
|
||||
|
||||
it "finds all musicians page 1 per_page 3" do
|
||||
# expects three of the musicians
|
||||
results = Search.musician_filter({per_page: 3})
|
||||
results = Search.musician_filter({per_page: 3, score_limit: Search::TEST_SCORE})
|
||||
results.search_type.should == :musicians_filter
|
||||
results.results.count.should == 3
|
||||
results.results.should eq @geomusicians.reverse.slice(0, 3)
|
||||
results.results.should eq @musicians.reverse.slice(0, 3)
|
||||
end
|
||||
|
||||
it "finds all musicians page 2 per_page 3" do
|
||||
# expects two of the musicians
|
||||
results = Search.musician_filter({page: 2, per_page: 3})
|
||||
results = Search.musician_filter({page: 2, per_page: 3, score_limit: Search::TEST_SCORE})
|
||||
results.search_type.should == :musicians_filter
|
||||
results.results.count.should == 2
|
||||
results.results.should eq @geomusicians.reverse.slice(3, 3)
|
||||
results.results.count.should == 3
|
||||
results.results.should eq @musicians.reverse.slice(3, 3)
|
||||
end
|
||||
|
||||
it "finds all musicians page 3 per_page 3" do
|
||||
# expects two of the musicians
|
||||
results = Search.musician_filter({page: 3, per_page: 3})
|
||||
results = Search.musician_filter({page: 3, per_page: 3, score_limit: Search::TEST_SCORE})
|
||||
results.search_type.should == :musicians_filter
|
||||
results.results.count.should == 0
|
||||
end
|
||||
|
|
@ -146,7 +149,7 @@ describe 'Musician search' do
|
|||
f3.save
|
||||
|
||||
# @user2.followers.concat([@user3, @user4, @user2])
|
||||
results = Search.musician_filter({ :per_page => @musicians.size }, @user3)
|
||||
results = Search.musician_filter({ :per_page => @musicians.size, score_limit: Search::TEST_SCORE, orderby: 'followed'}, @user3)
|
||||
expect(results.results[0].id).to eq(@user2.id)
|
||||
|
||||
# check the follower count for given entry
|
||||
|
|
@ -157,7 +160,7 @@ describe 'Musician search' do
|
|||
|
||||
it 'paginates properly' do
|
||||
# make sure pagination works right
|
||||
params = { :per_page => 2, :page => 1 }
|
||||
params = { :per_page => 2, :page => 1 , score_limit: Search::TEST_SCORE}
|
||||
results = Search.musician_filter(params)
|
||||
expect(results.results.count).to be 2
|
||||
end
|
||||
|
|
@ -218,7 +221,7 @@ describe 'Musician search' do
|
|||
# create friendship record
|
||||
Friendship.save(@user1.id, @user2.id)
|
||||
# search on user2
|
||||
results = Search.musician_filter({}, @user2)
|
||||
results = Search.musician_filter({score_limit: Search::TEST_SCORE}, @user2)
|
||||
friend = results.results.detect { |mm| mm.id == @user1.id }
|
||||
expect(friend).to_not be_nil
|
||||
expect(results.friend_count(friend)).to be 1
|
||||
|
|
@ -239,7 +242,7 @@ describe 'Musician search' do
|
|||
expect(recording.claimed_recordings.length).to be 1
|
||||
expect(@user1.recordings.detect { |rr| rr == recording }).to_not be_nil
|
||||
|
||||
results = Search.musician_filter({},@user1)
|
||||
results = Search.musician_filter({score_limit: Search::TEST_SCORE},@user1)
|
||||
# puts "====================== results #{results.inspect}"
|
||||
uu = results.results.detect { |mm| mm.id == @user1.id }
|
||||
expect(uu).to_not be_nil
|
||||
|
|
@ -257,7 +260,7 @@ describe 'Musician search' do
|
|||
|
||||
make_recording(@user1)
|
||||
# order results by num recordings
|
||||
results = Search.musician_filter({ :orderby => 'plays' }, @user2)
|
||||
results = Search.musician_filter({ orderby: 'plays', score_limit: Search::TEST_SCORE}, @user2)
|
||||
# puts "========= results #{results.inspect}"
|
||||
expect(results.results.length).to eq(2)
|
||||
expect(results.results[0].id).to eq(@user1.id)
|
||||
|
|
@ -266,23 +269,24 @@ describe 'Musician search' do
|
|||
# add more data and make sure order still correct
|
||||
make_recording(@user3)
|
||||
make_recording(@user3)
|
||||
results = Search.musician_filter({ :orderby => 'plays' }, @user2)
|
||||
results = Search.musician_filter({ :orderby => 'plays', score_limit: Search::TEST_SCORE }, @user2)
|
||||
expect(results.results.length).to eq(2)
|
||||
expect(results.results[0].id).to eq(@user3.id)
|
||||
expect(results.results[1].id).to eq(@user1.id)
|
||||
end
|
||||
|
||||
it "by now playing" do
|
||||
pending "these tests worked, so leaving them in, but we don't currently have 'Now Playing' in the find musicians screen"
|
||||
# should get 1 result with 1 active session
|
||||
make_session(@user1)
|
||||
results = Search.musician_filter({ :orderby => 'playing' }, @user2)
|
||||
results = Search.musician_filter({ :orderby => 'playing', score_limit: Search::TEST_SCORE}, @user2)
|
||||
expect(results.results.count).to be 1
|
||||
expect(results.results.first.id).to eq(@user1.id)
|
||||
|
||||
# should get 2 results with 2 active sessions
|
||||
# sort order should be created_at DESC
|
||||
make_session(@user3)
|
||||
results = Search.musician_filter({ :orderby => 'playing' }, @user2)
|
||||
results = Search.musician_filter({ :orderby => 'playing', score_limit: Search::TEST_SCORE}, @user2)
|
||||
expect(results.results.count).to be 2
|
||||
expect(results.results[0].id).to eq(@user3.id)
|
||||
expect(results.results[1].id).to eq(@user1.id)
|
||||
|
|
@ -299,7 +303,7 @@ describe 'Musician search' do
|
|||
@user1.reload
|
||||
ii = @user1.instruments.detect { |inst| inst.id == 'tuba' }
|
||||
expect(ii).to_not be_nil
|
||||
results = Search.musician_filter({ :instrument => ii.id })
|
||||
results = Search.musician_filter({ :instrument => ii.id, score_limit: Search::TEST_SCORE })
|
||||
results.results.each do |rr|
|
||||
expect(rr.instruments.detect { |inst| inst.id=='tuba' }.id).to eq(ii.id)
|
||||
end
|
||||
|
|
@ -313,13 +317,13 @@ describe 'Musician search' do
|
|||
# short distance
|
||||
results = Search.musician_filter({ :per_page => num,
|
||||
:distance => 10,
|
||||
:city => 'Apex' }, @user1)
|
||||
:city => 'Apex', score_limit: Search::TEST_SCORE }, @user1)
|
||||
expect(results.results.count).to be num
|
||||
# long distance
|
||||
results = Search.musician_filter({ :per_page => num,
|
||||
:distance => 1000,
|
||||
:city => 'Miami',
|
||||
:state => 'FL' }, @user1)
|
||||
:state => 'FL', score_limit: Search::TEST_SCORE }, @user1)
|
||||
expect(results.results.count).to be num
|
||||
end
|
||||
|
||||
|
|
@ -327,14 +331,14 @@ describe 'Musician search' do
|
|||
pending 'distance search changes'
|
||||
expect(@user1.lat).to_not be_nil
|
||||
# uses the location of @user1
|
||||
results = Search.musician_filter({ :distance => 10, :per_page => User.musicians.count }, @user1)
|
||||
results = Search.musician_filter({ :distance => 10, :per_page => User.musicians.count, score_limit: Search::TEST_SCORE }, @user1)
|
||||
expect(results.results.count).to be User.musicians.count
|
||||
end
|
||||
|
||||
it "finds no musicians within a given distance of location" do
|
||||
pending 'distance search changes'
|
||||
expect(@user1.lat).to_not be_nil
|
||||
results = Search.musician_filter({ :distance => 10, :city => 'San Francisco' }, @user1)
|
||||
results = Search.musician_filter({ :distance => 10, :city => 'San Francisco', score_limit: Search::TEST_SCORE }, @user1)
|
||||
expect(results.results.count).to be 0
|
||||
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
|
||||
|
|
|
|||
|
|
@ -10,141 +10,305 @@ describe Score do
|
|||
let(:latency_tester2) { FactoryGirl.create(:latency_tester) }
|
||||
let(:score_with_latency_tester) { s1, s2 = Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo', { alatencytestid: latency_tester1.id, blatencytestid: latency_tester2.id}); s1 }
|
||||
|
||||
before do
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 30, nil)
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 40, Time.new.utc-3600)
|
||||
end
|
||||
|
||||
it "count" do
|
||||
Score.count.should == 6
|
||||
end
|
||||
|
||||
it 'a to b' do
|
||||
s = Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first
|
||||
s.should_not be_nil
|
||||
s.alocidispid.should == 1234
|
||||
s.anodeid.should eql('anodeid')
|
||||
s.aaddr.should == 0x01020304
|
||||
s.blocidispid.should == 2345
|
||||
s.bnodeid.should eql('bnodeid')
|
||||
s.baddr.should == 0x02030405
|
||||
s.score.should == 20
|
||||
s.scorer.should == 0
|
||||
s.score_dt.should_not be_nil
|
||||
s.scoring_data.should eq('foo')
|
||||
end
|
||||
|
||||
it 'b to a' do
|
||||
s = Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first
|
||||
s.should_not be_nil
|
||||
s.alocidispid.should == 2345
|
||||
s.anodeid.should eql('bnodeid')
|
||||
s.aaddr.should == 0x02030405
|
||||
s.blocidispid.should == 1234
|
||||
s.bnodeid.should eql('anodeid')
|
||||
s.baddr.should == 0x01020304
|
||||
s.score.should == 20
|
||||
s.scorer.should == 1
|
||||
s.score_dt.should_not be_nil
|
||||
s.scoring_data.should be_nil
|
||||
end
|
||||
|
||||
it 'a to c' do
|
||||
s = Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first
|
||||
s.should_not be_nil
|
||||
s.alocidispid.should == 1234
|
||||
s.anodeid.should eql('anodeid')
|
||||
s.aaddr.should == 0x01020304
|
||||
s.blocidispid.should == 3456
|
||||
s.bnodeid.should eql('cnodeid')
|
||||
s.baddr.should == 0x03040506
|
||||
s.score.should == 30
|
||||
s.scorer.should == 0
|
||||
s.score_dt.should_not be_nil
|
||||
s.scoring_data.should be_nil
|
||||
end
|
||||
|
||||
it 'c to a' do
|
||||
s = Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first
|
||||
s.should_not be_nil
|
||||
s.alocidispid.should == 3456
|
||||
s.anodeid.should eql('cnodeid')
|
||||
s.aaddr.should == 0x03040506
|
||||
s.blocidispid.should == 1234
|
||||
s.bnodeid.should eql('anodeid')
|
||||
s.baddr.should == 0x01020304
|
||||
s.score.should == 30
|
||||
s.scorer.should == 1
|
||||
s.score_dt.should_not be_nil
|
||||
s.scoring_data.should be_nil
|
||||
end
|
||||
|
||||
it 'delete a to c' do
|
||||
Score.deletex(1234, 3456)
|
||||
Score.count.should == 2
|
||||
Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first.should be_nil
|
||||
Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first.should be_nil
|
||||
Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first.should_not be_nil
|
||||
Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first.should_not be_nil
|
||||
end
|
||||
|
||||
it 'findx' do
|
||||
Score.findx(1234, 1234).should == -1
|
||||
Score.findx(1234, 2345).should == 20
|
||||
Score.findx(1234, 3456).should == 30
|
||||
|
||||
Score.findx(2345, 1234).should == 20
|
||||
Score.findx(2345, 2345).should == -1
|
||||
Score.findx(2345, 3456).should == -1
|
||||
|
||||
Score.findx(3456, 1234).should == 30
|
||||
Score.findx(3456, 2345).should == -1
|
||||
Score.findx(3456, 3456).should == -1
|
||||
end
|
||||
|
||||
it "test shortcut for making scores from connections" do
|
||||
user1 = FactoryGirl.create(:user)
|
||||
conn1 = FactoryGirl.create(:connection, user: user1, addr: 0x01020304, locidispid: 5)
|
||||
user2 = FactoryGirl.create(:user)
|
||||
conn2 = FactoryGirl.create(:connection, user: user2, addr: 0x11121314, locidispid: 6)
|
||||
user3 = FactoryGirl.create(:user)
|
||||
conn3 = FactoryGirl.create(:connection, user: user3, addr: 0x21222324, locidispid: 7)
|
||||
|
||||
Score.findx(5, 6).should == -1
|
||||
Score.findx(6, 5).should == -1
|
||||
Score.findx(5, 7).should == -1
|
||||
Score.findx(7, 5).should == -1
|
||||
Score.findx(6, 7).should == -1
|
||||
Score.findx(7, 6).should == -1
|
||||
|
||||
Score.score_conns(conn1, conn2, 12)
|
||||
Score.score_conns(conn1, conn3, 13)
|
||||
Score.score_conns(conn2, conn3, 23)
|
||||
|
||||
Score.findx(5, 6).should == 12
|
||||
Score.findx(6, 5).should == 12
|
||||
Score.findx(5, 7).should == 13
|
||||
Score.findx(7, 5).should == 13
|
||||
Score.findx(6, 7).should == 23
|
||||
Score.findx(7, 6).should == 23
|
||||
end
|
||||
|
||||
describe "createx" do
|
||||
it "creates with user info" do
|
||||
score_with_user.touch
|
||||
score_with_user.auserid.should == user1.id
|
||||
score_with_user.buserid.should == user2.id
|
||||
score_with_user.alatencytestid.should be_nil
|
||||
score_with_user.blatencytestid.should be_nil
|
||||
describe "with default scores" do
|
||||
before do
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 30, nil)
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 40, Time.new.utc-3600)
|
||||
end
|
||||
|
||||
it "creates with latency-tester info" do
|
||||
score_with_latency_tester.touch
|
||||
score_with_latency_tester.auserid.should be_nil
|
||||
score_with_latency_tester.buserid.should be_nil
|
||||
score_with_latency_tester.alatencytestid.should == latency_tester1.id
|
||||
score_with_latency_tester.blatencytestid.should == latency_tester2.id
|
||||
|
||||
it "count" do
|
||||
Score.count.should == 6
|
||||
end
|
||||
|
||||
it 'a to b' do
|
||||
s = Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first
|
||||
s.should_not be_nil
|
||||
s.alocidispid.should == 1234
|
||||
s.anodeid.should eql('anodeid')
|
||||
s.aaddr.should == 0x01020304
|
||||
s.blocidispid.should == 2345
|
||||
s.bnodeid.should eql('bnodeid')
|
||||
s.baddr.should == 0x02030405
|
||||
s.score.should == 20
|
||||
s.scorer.should == 0
|
||||
s.score_dt.should_not be_nil
|
||||
s.scoring_data.should eq('foo')
|
||||
end
|
||||
|
||||
it 'b to a' do
|
||||
s = Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first
|
||||
s.should_not be_nil
|
||||
s.alocidispid.should == 2345
|
||||
s.anodeid.should eql('bnodeid')
|
||||
s.aaddr.should == 0x02030405
|
||||
s.blocidispid.should == 1234
|
||||
s.bnodeid.should eql('anodeid')
|
||||
s.baddr.should == 0x01020304
|
||||
s.score.should == 20
|
||||
s.scorer.should == 1
|
||||
s.score_dt.should_not be_nil
|
||||
s.scoring_data.should be_nil
|
||||
end
|
||||
|
||||
it 'a to c' do
|
||||
s = Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first
|
||||
s.should_not be_nil
|
||||
s.alocidispid.should == 1234
|
||||
s.anodeid.should eql('anodeid')
|
||||
s.aaddr.should == 0x01020304
|
||||
s.blocidispid.should == 3456
|
||||
s.bnodeid.should eql('cnodeid')
|
||||
s.baddr.should == 0x03040506
|
||||
s.score.should == 30
|
||||
s.scorer.should == 0
|
||||
s.score_dt.should_not be_nil
|
||||
s.scoring_data.should be_nil
|
||||
end
|
||||
|
||||
it 'c to a' do
|
||||
s = Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first
|
||||
s.should_not be_nil
|
||||
s.alocidispid.should == 3456
|
||||
s.anodeid.should eql('cnodeid')
|
||||
s.aaddr.should == 0x03040506
|
||||
s.blocidispid.should == 1234
|
||||
s.bnodeid.should eql('anodeid')
|
||||
s.baddr.should == 0x01020304
|
||||
s.score.should == 30
|
||||
s.scorer.should == 1
|
||||
s.score_dt.should_not be_nil
|
||||
s.scoring_data.should be_nil
|
||||
end
|
||||
|
||||
it 'delete a to c' do
|
||||
Score.deletex(1234, 3456)
|
||||
Score.count.should == 2
|
||||
Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first.should be_nil
|
||||
Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first.should be_nil
|
||||
Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first.should_not be_nil
|
||||
Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first.should_not be_nil
|
||||
end
|
||||
|
||||
it 'findx' do
|
||||
Score.findx(1234, 1234).should == -1
|
||||
Score.findx(1234, 2345).should == 20
|
||||
Score.findx(1234, 3456).should == 30
|
||||
|
||||
Score.findx(2345, 1234).should == 20
|
||||
Score.findx(2345, 2345).should == -1
|
||||
Score.findx(2345, 3456).should == -1
|
||||
|
||||
Score.findx(3456, 1234).should == 30
|
||||
Score.findx(3456, 2345).should == -1
|
||||
Score.findx(3456, 3456).should == -1
|
||||
end
|
||||
|
||||
it "test shortcut for making scores from connections" do
|
||||
user1 = FactoryGirl.create(:user)
|
||||
conn1 = FactoryGirl.create(:connection, user: user1, addr: 0x01020304, locidispid: 5)
|
||||
user2 = FactoryGirl.create(:user)
|
||||
conn2 = FactoryGirl.create(:connection, user: user2, addr: 0x11121314, locidispid: 6)
|
||||
user3 = FactoryGirl.create(:user)
|
||||
conn3 = FactoryGirl.create(:connection, user: user3, addr: 0x21222324, locidispid: 7)
|
||||
|
||||
Score.findx(5, 6).should == -1
|
||||
Score.findx(6, 5).should == -1
|
||||
Score.findx(5, 7).should == -1
|
||||
Score.findx(7, 5).should == -1
|
||||
Score.findx(6, 7).should == -1
|
||||
Score.findx(7, 6).should == -1
|
||||
|
||||
Score.score_conns(conn1, conn2, 12)
|
||||
Score.score_conns(conn1, conn3, 13)
|
||||
Score.score_conns(conn2, conn3, 23)
|
||||
|
||||
Score.findx(5, 6).should == 12
|
||||
Score.findx(6, 5).should == 12
|
||||
Score.findx(5, 7).should == 13
|
||||
Score.findx(7, 5).should == 13
|
||||
Score.findx(6, 7).should == 23
|
||||
Score.findx(7, 6).should == 23
|
||||
end
|
||||
|
||||
describe "createx" do
|
||||
it "creates with user info" do
|
||||
score_with_user.touch
|
||||
score_with_user.auserid.should == user1.id
|
||||
score_with_user.buserid.should == user2.id
|
||||
score_with_user.alatencytestid.should be_nil
|
||||
score_with_user.blatencytestid.should be_nil
|
||||
end
|
||||
|
||||
it "creates with latency-tester info" do
|
||||
score_with_latency_tester.touch
|
||||
score_with_latency_tester.auserid.should be_nil
|
||||
score_with_latency_tester.buserid.should be_nil
|
||||
score_with_latency_tester.alatencytestid.should == latency_tester1.id
|
||||
score_with_latency_tester.blatencytestid.should == latency_tester2.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# current_scores is a view that tries to take the median of up to the last 5 entries
|
||||
describe "current_scores" do
|
||||
it "works with empty data set" do
|
||||
result = Score.connection.execute('SELECT * FROM current_scores')
|
||||
result.check
|
||||
result.ntuples.should == 0
|
||||
end
|
||||
|
||||
it "works with one score" do
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
|
||||
result = Score.connection.execute('SELECT * FROM current_scores')
|
||||
result.check
|
||||
result.ntuples.should == 2
|
||||
result[0]['alocidispid'].to_i.should == 1234
|
||||
result[0]['scorer'].to_i.should == 0
|
||||
result[1]['alocidispid'].to_i.should == 2345
|
||||
result[1]['scorer'].to_i.should == 1
|
||||
end
|
||||
|
||||
it "works with two scores in same location" do
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo') # median
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 25, nil, 'foo')
|
||||
|
||||
result = Score.connection.execute('SELECT * FROM current_scores')
|
||||
result.check
|
||||
result.ntuples.should == 2
|
||||
result[0]['score'].to_i.should == 20
|
||||
result[1]['score'].to_i.should == 20
|
||||
end
|
||||
|
||||
it "works with three scores in same location" do
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 25, nil, 'foo') # median
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo')
|
||||
|
||||
result = Score.connection.execute('SELECT * FROM current_scores')
|
||||
result.check
|
||||
result.ntuples.should == 2
|
||||
result[0]['score'].to_i.should == 25
|
||||
result[1]['score'].to_i.should == 25
|
||||
end
|
||||
|
||||
it "works with six scores in same location" do
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo') # we'll make sure this is old, so it won't be in the set
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 25, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 31, nil, 'foo')# median
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 32, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 33, nil, 'foo')
|
||||
|
||||
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{1.days.ago}' WHERE score = 20").cmdtuples.should == 2
|
||||
|
||||
result = Score.connection.execute('SELECT * FROM current_scores')
|
||||
result.check
|
||||
result.ntuples.should == 2
|
||||
result[0]['score'].to_i.should == 31
|
||||
result[1]['score'].to_i.should == 31
|
||||
|
||||
# now push back score with 33 to the very back, which will shift the median up to 30
|
||||
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{2.days.ago}' WHERE score = 33").check
|
||||
|
||||
result = Score.connection.execute('SELECT * FROM current_scores')
|
||||
result.check
|
||||
result.ntuples.should == 2
|
||||
result[0]['score'].to_i.should == 30
|
||||
result[1]['score'].to_i.should == 30
|
||||
end
|
||||
|
||||
it "works with one score each in different locations" do
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo') # median
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 25, nil, 'foo')
|
||||
|
||||
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
|
||||
result.check
|
||||
result.ntuples.should == 4
|
||||
|
||||
result[0]['score'].to_i.should == 20
|
||||
result[1]['score'].to_i.should == 20
|
||||
result[2]['score'].to_i.should == 25
|
||||
result[3]['score'].to_i.should == 25
|
||||
end
|
||||
|
||||
it "works with multiple scores in different locations" do
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo') # median
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 25, nil, 'foo') # median
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 35, nil, 'foo')
|
||||
|
||||
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
|
||||
result.check
|
||||
result.ntuples.should == 4
|
||||
|
||||
result[0]['score'].to_i.should == 20
|
||||
result[1]['score'].to_i.should == 20
|
||||
result[2]['score'].to_i.should == 25
|
||||
result[3]['score'].to_i.should == 25
|
||||
end
|
||||
|
||||
it "works with multiple scores in different locations" do
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 25, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo') # median
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 35, nil, 'foo') # median
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 40, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 45, nil, 'foo')
|
||||
|
||||
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
|
||||
result.check
|
||||
result.ntuples.should == 4
|
||||
|
||||
result[0]['score'].to_i.should == 30
|
||||
result[1]['score'].to_i.should == 30
|
||||
result[2]['score'].to_i.should == 35
|
||||
result[3]['score'].to_i.should == 35
|
||||
|
||||
|
||||
end
|
||||
|
||||
it "works with over 6 scores in different locations" do
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 25, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 35, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 40, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 45, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 45, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 50, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 55, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 60, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 65, nil, 'foo')
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 70, nil, 'foo')
|
||||
|
||||
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{1.days.ago}' WHERE score = 20 OR score = 25").cmdtuples.should == 4
|
||||
|
||||
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
|
||||
result.check
|
||||
result.ntuples.should == 4
|
||||
|
||||
result[0]['score'].to_i.should == 45
|
||||
result[1]['score'].to_i.should == 45
|
||||
result[2]['score'].to_i.should == 50
|
||||
result[3]['score'].to_i.should == 50
|
||||
|
||||
|
||||
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{2.days.ago}' WHERE score = 65 OR score = 70").cmdtuples.should == 4
|
||||
|
||||
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
|
||||
result.check
|
||||
result.ntuples.should == 4
|
||||
|
||||
result[0]['score'].to_i.should == 40
|
||||
result[1]['score'].to_i.should == 40
|
||||
result[2]['score'].to_i.should == 45
|
||||
result[3]['score'].to_i.should == 45
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||
|
||||
#require 'resque/tasks'
|
||||
#require 'resque/scheduler/tasks'
|
||||
require 'resque/tasks'
|
||||
require 'resque/scheduler/tasks'
|
||||
require File.expand_path('../config/application', __FILE__)
|
||||
SampleApp::Application.load_tasks
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
var $templateOpenSlots = null;
|
||||
var instrument_logo_map = context.JK.getInstrumentIconMap24();
|
||||
var invitationDialog = null;
|
||||
var inviteMusiciansUtil = null;
|
||||
var friendInput=null;
|
||||
|
||||
|
||||
var LATENCY = {
|
||||
|
|
@ -42,7 +44,11 @@
|
|||
function inviteMusicians(e) {
|
||||
e.preventDefault();
|
||||
|
||||
invitationDialog.showEmailDialog();
|
||||
friendInput = inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians',
|
||||
sessionId);
|
||||
inviteMusiciansUtil.loadFriends();
|
||||
$(friendInput).show();
|
||||
// invitationDialog.showEmailDialog();
|
||||
}
|
||||
|
||||
function cancelRsvpRequest(e) {
|
||||
|
|
@ -120,6 +126,7 @@
|
|||
$sessionPageBtn.on('click', openSessionPage);
|
||||
$screen.find(".approveRsvpRequest").on('click', approveRsvpRequest);
|
||||
$screen.find(".declineRsvpRequest").on('click', declineRsvpRequest);
|
||||
$(friendInput).focus(function() { $(this).val(''); })
|
||||
|
||||
$screen.find(".cancelSessionRsvp").on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
|
@ -393,6 +400,10 @@
|
|||
$sessionDetail = $screen.find("#account-session-detail-div");
|
||||
$shareUrl = $screen.find('.share-url');
|
||||
invitationDialog = invitationDlg;
|
||||
|
||||
inviteMusiciansUtil = new JK.InviteMusiciansUtil(JK.app);
|
||||
inviteMusiciansUtil.initialize(JK.FriendSelectorDialogInstance);
|
||||
|
||||
$templateOpenSlots = $('#template-open-slots');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
var did_show_musician_page = false;
|
||||
var page_num=1, page_count=0;
|
||||
var textMessageDialog = null;
|
||||
var $results = null;
|
||||
|
||||
function loadMusicians(queryString) {
|
||||
// squelch nulls and undefines
|
||||
|
|
@ -88,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";
|
||||
}
|
||||
|
||||
|
|
@ -97,11 +98,26 @@
|
|||
// 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";
|
||||
}
|
||||
|
||||
function formatLocation(musician) {
|
||||
if(musician.city && musician.state) {
|
||||
return musician.city + ', ' + musician.regionname
|
||||
}
|
||||
else if(musician.city) {
|
||||
return musician.city
|
||||
}
|
||||
else if(musician.state) {
|
||||
return musician.regionname
|
||||
}
|
||||
else {
|
||||
return 'Location Unavailable'
|
||||
}
|
||||
}
|
||||
|
||||
function renderMusicians() {
|
||||
var ii, len;
|
||||
var mTemplate = $('#template-find-musician-row').html();
|
||||
|
|
@ -110,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];
|
||||
|
|
@ -149,12 +166,12 @@
|
|||
};
|
||||
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,
|
||||
musician_name: musician.name,
|
||||
musician_location: musician.city + ', ' + musician.state,
|
||||
musician_location: formatLocation(musician),
|
||||
instruments: instr_logos,
|
||||
biography: musician['biography'],
|
||||
follow_count: musician['follow_count'],
|
||||
|
|
@ -164,14 +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);
|
||||
}
|
||||
$('#musician-filter-results').append(renderings);
|
||||
|
||||
|
||||
$('.search-m-friend').on('click', friendMusician);
|
||||
$('.search-m-follow').on('click', followMusician);
|
||||
|
|
@ -279,6 +309,8 @@
|
|||
};
|
||||
app.bindScreen('musicians', screenBindings);
|
||||
|
||||
$results = $('#musician-filter-results');
|
||||
|
||||
events();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@
|
|||
addInvitation(dd.name, dd.id);
|
||||
});
|
||||
}).fail(app.ajaxError);
|
||||
|
||||
return friendInput;
|
||||
}
|
||||
|
||||
this.clearSelections = function() {
|
||||
|
|
@ -133,7 +135,7 @@
|
|||
|
||||
} else {
|
||||
$(friendInput).select();
|
||||
context.alert('Invitation already exists for this musician.');
|
||||
// context.alert('Invitation already exists for this musician.');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -309,10 +309,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;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
var playbackControls = null;
|
||||
var promptLeave = false;
|
||||
var rateSessionDialog = null;
|
||||
var friendInput=null;
|
||||
|
||||
var rest = context.JK.Rest();
|
||||
|
||||
|
|
@ -133,6 +134,7 @@
|
|||
.done(function(){
|
||||
initializeSession();
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function notifyWithUserInfo(title , text, clientId) {
|
||||
|
|
@ -1354,7 +1356,10 @@
|
|||
}
|
||||
|
||||
function inviteMusicians() {
|
||||
inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians', sessionId);
|
||||
friendInput = inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians',
|
||||
sessionId);
|
||||
inviteMusiciansUtil.loadFriends();
|
||||
$(friendInput).show();
|
||||
}
|
||||
|
||||
function events() {
|
||||
|
|
@ -1365,6 +1370,7 @@
|
|||
$('#recording-start-stop').on('click', startStopRecording);
|
||||
$('#open-a-recording').on('click', openRecording);
|
||||
$('#session-invite-musicians').on('click', inviteMusicians);
|
||||
$('#session-invite-musicians2').on('click', inviteMusicians);
|
||||
$('#track-settings').click(function() {
|
||||
configureTrackDialog.refresh();
|
||||
configureTrackDialog.showVoiceChatPanel(true);
|
||||
|
|
@ -1376,6 +1382,7 @@
|
|||
.on('pause', onPause)
|
||||
.on('play', onPlay)
|
||||
.on('change-position', onChangePlayPosition);
|
||||
$(friendInput).focus(function() { $(this).val(''); })
|
||||
}
|
||||
|
||||
this.initialize = function(localRecordingsDialogInstance, recordingFinishedDialogInstance, friendSelectorDialog) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,12 +106,14 @@
|
|||
if (!data) {
|
||||
data = {}
|
||||
}
|
||||
if(!options) {
|
||||
options = {}
|
||||
}
|
||||
|
||||
|
||||
var helpText = context._.template($('#template-help-' + templateName).html(), data, { variable: 'data' });
|
||||
|
||||
var holder = $('<div class="hover-bubble help-bubble"></div>');
|
||||
holder.append(helpText);
|
||||
|
||||
context.JK.hoverBubble($element, helpText, options);
|
||||
}
|
||||
|
||||
|
|
@ -183,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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
#musicians-screen {
|
||||
|
||||
.filter-element.desc.instrument-selector {
|
||||
margin-left:2px;
|
||||
float:left;
|
||||
|
||||
& + .dropdown-wrapper .dropdown-container {
|
||||
width:150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#musician-filter-results {
|
||||
margin: 0 10px 0px 10px;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
.right
|
||||
%a.cancel-rsvp.button-orange{href: "#"} CANCEL RSVP
|
||||
%a.session-detail-page.button-orange{href: "#", rel:'external'} SESSION PAGE
|
||||
%a.invite-others.button-orange{href: "#"} INVITE OTHERS
|
||||
%a.invite-others.button-orange{'layout-link' => 'select-invites','href' => "#"} INVITE OTHERS
|
||||
.clearall
|
||||
|
||||
#account-session-detail-div
|
||||
|
|
@ -160,4 +160,4 @@
|
|||
|
||||
%script{type: 'text/template', id: 'template-account-session-latency'}
|
||||
.latency{class: "{{data.latency_style}}"}
|
||||
{{data.latency_text}}
|
||||
{{data.latency_text}}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -40,4 +40,42 @@
|
|||
|
||||
<script type="text/template" id="template-help-minimum-output-channels">
|
||||
To be a valid output audio device, it must have at least 2 output ports.
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-help-musician-follower-count">
|
||||
The number of followers that this user has.
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-help-musician-friend-count">
|
||||
The number of friends that this user has.
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-help-musician-recording-count">
|
||||
The number of recordings that this user has made.
|
||||
</script>
|
||||
|
||||
<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>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<!-- Musician Screen -->
|
||||
<%= content_tag(:div, :layout => 'screen', 'layout-id' => 'musicians', :class => "screen secondary") do -%>
|
||||
<%= content_tag(:div, :layout => 'screen', 'layout-id' => 'musicians', :class => "screen secondary", id: 'musicians-screen') do -%>
|
||||
<%= content_tag(:div, :class => :content) do -%>
|
||||
<%= content_tag(:div, :class => 'content-head') do -%>
|
||||
<%= content_tag(:div, image_tag("content/icon_musicians.png", {:height => 19, :width => 19}), :class => 'content-icon') %>
|
||||
|
|
@ -56,13 +56,13 @@
|
|||
</div>
|
||||
<div class="clearleft"></div>
|
||||
</div>
|
||||
<div class="button-row" data-hint="button-row">
|
||||
<div class="button-row " data-hint="button-row">
|
||||
<div class="lcol stats left">
|
||||
{friend_count} <img src="../assets/content/icon_friend.png" alt="friends" width="14" height="12" align="absmiddle" style="margin-right:4px;"/>
|
||||
{follow_count} <img src="../assets/content/icon_followers.png" alt="follows" width="22" height="12" align="absmiddle" style="margin-right:4px;"/>
|
||||
{recording_count} <img src="../assets/content/icon_recordings.png" alt="recordings" width="12" height="13" align="absmiddle" style="margin-right:4px;"/>
|
||||
{session_count} <img src="../assets/content/icon_session_tiny.png" alt="sessions" width="12" height="12" align="absmiddle" style="margin-right:4px;"/>
|
||||
{musician_one_way_score} <img src="../assets/content/icon_{musician_score_color}_score.png" alt="{musician_score_color_alt} score" width="12" height="12" align="absmiddle" style="margin-right:4px;"/>
|
||||
<span class="friend-count">{friend_count} <img src="../assets/content/icon_friend.png" alt="friends" width="14" height="12" align="absmiddle" style="margin-right:4px;"/></span>
|
||||
<span class="follower-count">{follow_count} <img src="../assets/content/icon_followers.png" alt="follows" width="22" height="12" align="absmiddle" style="margin-right:4px;"/></span>
|
||||
<span class="recording-count">{recording_count} <img src="../assets/content/icon_recordings.png" alt="recordings" width="12" height="13" align="absmiddle" style="margin-right:4px;"/></span>
|
||||
<span class="session-count">{session_count} <img src="../assets/content/icon_session_tiny.png" alt="sessions" width="12" height="12" align="absmiddle" style="margin-right:4px;"/></span>
|
||||
<span class="score-count">{musician_one_way_score} <img src="../assets/content/icon_{musician_score_color}_score.png" alt="{musician_score_color_alt} score" width="12" height="12" align="absmiddle" style="margin-right:4px;"/></span>
|
||||
</div>
|
||||
<div class="result-list-button-wrapper" data-musician-id={musician_id}>
|
||||
{musician_action_template}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@
|
|||
<div id="session-livetracks-container">
|
||||
<p class="when-empty">
|
||||
No Live Tracks:<br/>
|
||||
<a>Invite Other Musicians</a> to<br/>
|
||||
<a layout-link="select-invites", href="#" id="session-invite-musicians2">Invite Other Musicians</a> to<br/>
|
||||
Add Live Tracks
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -16,15 +16,15 @@
|
|||
<!-- @end sort filter -->
|
||||
<% else %>
|
||||
<!-- @begin order by filter -->
|
||||
<%= content_tag(:div, 'Filter By:', :class => 'filter-element desc') %>
|
||||
<%= select_tag("#{filter_label}_order_by", options_for_select(Search::ORDERINGS), {:class => "#{filter_label}-order-by easydropdown"} ) %>
|
||||
<%= content_tag(:div, 'Order By:', :class => 'filter-element desc') %>
|
||||
<%= select_tag("#{filter_label}_order_by", options_for_select(Search::M_ORDERINGS), {:class => "#{filter_label}-order-by easydropdown"} ) %>
|
||||
<!-- @end order by filter -->
|
||||
<% end %>
|
||||
<% end -%>
|
||||
<%= content_tag(:div, :class => 'filter-element wrapper') do -%>
|
||||
<% if :musician == filter_label %>
|
||||
<!-- @begin instrument filter -->
|
||||
<%= content_tag(:div, 'Instrument:', :class => 'filter-element desc') %>
|
||||
<%= content_tag(:div, 'Instrument:', :class => 'filter-element desc instrument-selector') %>
|
||||
<%= select_tag("#{filter_label}_instrument",
|
||||
options_for_select([['Any', '']].concat(JamRuby::Instrument.all.collect { |ii| [ii.description, ii.id] })), {:class=> "easydropdown"}) %>
|
||||
<!-- @end instrument filter -->
|
||||
|
|
|
|||
|
|
@ -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 %>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
# 'target' is the output directory
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Resque tasks
|
||||
require 'resque/tasks'
|
||||
require 'resque_scheduler/tasks'
|
||||
require 'resque/scheduler/tasks'
|
||||
require 'resque'
|
||||
require 'resque_scheduler'
|
||||
require 'resque-scheduler'
|
||||
|
||||
task :scheduler => :environment do
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "Create Session Flow", :js => true, :type => :feature, :capybara_feature => true do
|
||||
describe "Create Session UI", :js => true, :type => :feature, :capybara_feature => true do
|
||||
let(:user1) { FactoryGirl.create(:user) }
|
||||
let(:user2) { FactoryGirl.create(:user) }
|
||||
|
||||
|
|
@ -13,8 +13,32 @@ describe "Create Session Flow", :js => true, :type => :feature, :capybara_featur
|
|||
page.driver.resize(1500, 800) # makes sure all the elements are visible
|
||||
emulate_client
|
||||
sign_in_poltergeist user1
|
||||
wait_until_curtain_gone
|
||||
visit "/client#/createSession"
|
||||
page.find('.createsession').trigger(:click)
|
||||
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
|
||||
|
||||
|
|
@ -211,48 +235,4 @@ describe "Create Session Flow", :js => true, :type => :feature, :capybara_featur
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "create session flow backend" do
|
||||
describe "schedule a session" do
|
||||
it "schedule a session" do
|
||||
schedule_session({creator: user1})
|
||||
end
|
||||
|
||||
it "start a session after scheduling" do
|
||||
MusicSession.delete_all
|
||||
|
||||
schedule_session({creator: user1})
|
||||
|
||||
in_client(user1) do
|
||||
visit "/client#/createSession"
|
||||
|
||||
find('li[create-type="start-scheduled"] ins').trigger(:click)
|
||||
|
||||
find('.btn-next').trigger(:click)
|
||||
find('.btn-next').trigger(:click)
|
||||
|
||||
expect(page).to have_selector('h2', text: 'my tracks')
|
||||
find('#session-screen .session-mytracks .session-track')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "start quick session" do
|
||||
page.driver.resize(1500, 800) # makes sure all the elements are visible
|
||||
emulate_client
|
||||
sign_in_poltergeist user1
|
||||
wait_until_curtain_gone
|
||||
visit "/client#/createSession"
|
||||
expect(page).to have_selector('h1', text: 'create session')
|
||||
|
||||
find('li[create-type="quick-start"] ins').trigger(:click)
|
||||
find('div[info-id="quick-start"]')
|
||||
|
||||
find('.btn-next').trigger(:click)
|
||||
find('.btn-next', text: 'START SESSION').trigger(:click)
|
||||
|
||||
expect(page).to have_selector('h2', text: 'my tracks')
|
||||
find('#session-screen .session-mytracks .session-track')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "Create Session", :js => true, :type => :feature, :capybara_feature => true do
|
||||
let(:user1) { FactoryGirl.create(:user) }
|
||||
let(:user2) { FactoryGirl.create(:user) }
|
||||
|
||||
context "functionally test all ways to Create Session" do
|
||||
|
||||
context "I have already scheduled a session..." do
|
||||
let (:now) { Time.now - 5.hours }
|
||||
let (:first_session) { FactoryGirl.create(:music_session, creator: user1, name: "First one", scheduled_start: now + 5.minutes) }
|
||||
let (:second_session) { FactoryGirl.create(:music_session, creator: user1, name: "Second one", scheduled_start: now + 2.hours) }
|
||||
let (:third_session) { FactoryGirl.create(:music_session, creator: user1, name: "Third one", scheduled_start: now + 17.days) }
|
||||
let (:not_my_session) { FactoryGirl.create(:music_session, creator: user2, name: "Who cares", scheduled_start: now + 30.minutes) }
|
||||
|
||||
before do
|
||||
#instantiate these test sessions in non-sequential order
|
||||
third_session.touch; first_session.touch; not_my_session.touch; second_session.touch;
|
||||
#[first_session, second_session, third_session, not_my_session].each { |s| puts "#{s.name}: #{s.id}" }
|
||||
|
||||
emulate_client
|
||||
page.driver.resize(1500, 800) #purely aesthetic
|
||||
sign_in_poltergeist user1
|
||||
find('.createsession').trigger(:click)
|
||||
wait_for_ajax
|
||||
expect(page).to have_selector 'li[create-type="start-scheduled"] ins'
|
||||
end
|
||||
|
||||
it "sessions are shown in schedule order on the Create Session screen" do
|
||||
sleep 2 #arg
|
||||
radio_buttons = page.all('ul#scheduled-session-list li')
|
||||
first, second, third = *radio_buttons[0..2]
|
||||
expect(first.text).to include first_session.name
|
||||
expect(second.text).to include second_session.name
|
||||
expect(third.text).to include third_session.name
|
||||
expect(first).to have_selector 'input[checked=checked]'
|
||||
expect(page).to_not have_text not_my_session.name
|
||||
end
|
||||
|
||||
it "future sessions can be edited from the Create Session screen" do
|
||||
#pending "possible bug, does not occur when testing manually"
|
||||
page.find('a#edit_scheduled_sessions').trigger(:click)
|
||||
#expect(page).to have_selector "div[data-id='#{first_session.id}']" #see pending note
|
||||
expect(page).to have_selector "div[data-id='#{second_session.id}']"
|
||||
expect(page).to have_selector "div[data-id='#{third_session.id}']"
|
||||
expect(page).to_not have_selector "div[data-id='#{not_my_session.id}']"
|
||||
end
|
||||
|
||||
context "...start it now" do
|
||||
it "starts the first one" do
|
||||
sleep 1
|
||||
find('.btn-next').trigger(:click)
|
||||
sleep 1
|
||||
expect(page).to have_selector('.session-step-title', text: 'Review & Confirm')
|
||||
expect(page).to have_content first_session.name
|
||||
find('.btn-next').trigger(:click)
|
||||
expect(page).to have_selector('h2', text: 'my tracks')
|
||||
find('#session-screen .session-mytracks .session-track')
|
||||
end
|
||||
|
||||
context "attempt to start a session more than an hour from now" do
|
||||
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)
|
||||
sleep 1
|
||||
expect(page).to have_selector('.session-step-title', text: 'Review & Confirm')
|
||||
expect(page).to have_content first_session.name
|
||||
find('.btn-next').trigger(:click)
|
||||
expect(page).to have_selector('h2', text: 'my tracks')
|
||||
find('#session-screen .session-mytracks .session-track')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
shared_examples_for :a_future_session do
|
||||
specify "creator can see the session on Create Session page" do
|
||||
in_client(creator) do
|
||||
page.find('.createsession').trigger(:click)
|
||||
expect(page).to have_selector('h1', text: 'create session')
|
||||
sessions = page.first('ul#scheduled-session-list li')
|
||||
expect(sessions.text).to include session_name
|
||||
end
|
||||
end
|
||||
|
||||
specify "creator can see the session on Find Session page" do
|
||||
in_client(creator) do
|
||||
visit "/client#/findSession"
|
||||
wait_until_curtain_gone
|
||||
expect(page).to have_selector('#session-name-disp', text: "#{session_name} (#{session_genre})")
|
||||
#expect(page).to have_selector('#session-name-disp', text: @session_genre)
|
||||
end
|
||||
end
|
||||
|
||||
specify "another user can see the session on Find Session page" do
|
||||
in_client(someone_else) do
|
||||
emulate_client
|
||||
page.driver.resize(1500, 800)
|
||||
sign_in_poltergeist someone_else
|
||||
visit "/client#/findSession"
|
||||
wait_until_curtain_gone
|
||||
expect(find('table#sessions-scheduled')).to have_content session_name
|
||||
expect(find('table#sessions-scheduled')).to have_content session_genre
|
||||
end
|
||||
end
|
||||
|
||||
specify "another user can RSVP to the session" do
|
||||
in_client(someone_else) do
|
||||
emulate_client
|
||||
page.driver.resize(1500, 800)
|
||||
sign_in_poltergeist someone_else
|
||||
visit "/client#/findSession"
|
||||
wait_until_curtain_gone
|
||||
|
||||
within('table#sessions-scheduled') do
|
||||
find('a.rsvp-link').trigger(:click)
|
||||
end
|
||||
|
||||
within('div.dialog-inner') do
|
||||
find('div.session-name').should have_content session_name
|
||||
find('div.slot-instructions').should have_content "Check the box(es) next to the track(s) you want to play"
|
||||
# fill_in '.txtComment', with: "Looking forward to the session"
|
||||
#first('div.rsvp-instruments input').trigger(:click)
|
||||
sleep 1
|
||||
find('#btnSubmitRsvp').trigger(:click)
|
||||
sleep 2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
specify "creator can start the session" do
|
||||
in_client(creator) do
|
||||
page.find('.createsession').trigger(:click)
|
||||
expect(page).to have_selector('h1', text: 'create session')
|
||||
expect(page).to have_content session_name
|
||||
find('li[create-type="start-scheduled"] ins').trigger(:click)
|
||||
find('.btn-next').trigger(:click)
|
||||
find('.btn-next').trigger(:click)
|
||||
expect(page).to have_selector('h2', text: 'my tracks')
|
||||
find('#session-screen .session-mytracks .session-track')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "I want to schedule a session for a specific future time" do
|
||||
before do
|
||||
MusicSession.delete_all
|
||||
@creator, @session_name, @session_genre = schedule_session(creator: user1)
|
||||
end
|
||||
|
||||
it_should_behave_like :a_future_session do
|
||||
let(:creator) { @creator }
|
||||
let(:session_name) { @session_name }
|
||||
let(:session_genre) { @session_genre }
|
||||
let(:someone_else) { FactoryGirl.create(:user) }
|
||||
end
|
||||
end
|
||||
|
||||
context "I want to choose the time after others RSVP to my session" do
|
||||
before do
|
||||
MusicSession.delete_all
|
||||
@creator, @session_name, @session_genre = schedule_session(creator: user1, rsvp: true)
|
||||
end
|
||||
|
||||
it_should_behave_like :a_future_session do
|
||||
let(:creator) { @creator }
|
||||
let(:session_name) { @session_name }
|
||||
let(:session_genre) { @session_genre }
|
||||
let(:someone_else) { FactoryGirl.create(:user) }
|
||||
end
|
||||
end
|
||||
|
||||
context "I want to start a new session right now for others to join" do
|
||||
before do
|
||||
MusicSession.delete_all
|
||||
@creator, @session_name, @session_genre = schedule_session(creator: user1, immediate: true)
|
||||
end
|
||||
|
||||
specify "creator is in the session" do
|
||||
in_client @creator do
|
||||
expect(page).to have_selector('h2', text: 'my tracks')
|
||||
find('#session-screen .session-mytracks .session-track')
|
||||
end
|
||||
end
|
||||
|
||||
specify "another user can see this active session on Find Session page" do
|
||||
in_client(user2) do
|
||||
emulate_client
|
||||
page.driver.resize(1500, 800)
|
||||
sign_in_poltergeist user2
|
||||
visit "/client#/findSession"
|
||||
wait_until_curtain_gone
|
||||
expect(find('table#sessions-active')).to have_content @session_name
|
||||
expect(find('table#sessions-active')).to have_content @session_genre
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "I want to quick start a test session just for me" do
|
||||
before do
|
||||
MusicSession.delete_all
|
||||
@creator, @session_name, @session_genre = schedule_session(creator: user1, quickstart: true)
|
||||
end
|
||||
|
||||
specify "creator is in the session" do
|
||||
in_client @creator do
|
||||
expect(page).to have_selector('h2', text: 'my tracks')
|
||||
find('#session-screen .session-mytracks .session-track')
|
||||
end
|
||||
end
|
||||
|
||||
specify "another user does NOT see the session on Find Session page" do
|
||||
in_client(user2) do
|
||||
emulate_client
|
||||
page.driver.resize(1500, 800)
|
||||
sign_in_poltergeist user2
|
||||
visit "/client#/findSession"
|
||||
wait_until_curtain_gone
|
||||
expect(find('table#sessions-scheduled')).to_not have_content @session_name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,8 +106,9 @@ describe "Musician Search API", :type => :api do
|
|||
f5.save
|
||||
|
||||
expect(@user4.followers.count).to be 3
|
||||
get_query
|
||||
get_query(orderby: :followed)
|
||||
good_response
|
||||
puts last_response.body
|
||||
musician = json["musicians"][0]
|
||||
expect(musician["id"]).to eq(@user4.id)
|
||||
followings = musician['followings']
|
||||
|
|
|
|||
|
|
@ -335,6 +335,9 @@ def schedule_session(options = {})
|
|||
fan_chat = options[:fan_chat].nil? ? false : options[:fan_chat]
|
||||
musician_access_value = 'Musicians may join by approval'
|
||||
fan_permission_value = 'Fans may listen, chat with each other'
|
||||
rsvp = options[:rsvp]
|
||||
immediate = options[:immediate]
|
||||
quickstart = options[:quickstart]
|
||||
|
||||
if musician_access && !approval_required
|
||||
musician_access_value = 'Musicians may join at will'
|
||||
|
|
@ -356,26 +359,40 @@ def schedule_session(options = {})
|
|||
expect(page).to have_selector('h1', text: 'create session')
|
||||
|
||||
within('#create-session-form') do
|
||||
|
||||
find('li[create-type="schedule-future"] ins').trigger(:click)
|
||||
find('.btn-next').trigger(:click)
|
||||
|
||||
jk_select(genre, '#create-session-form select[name="genres"]')
|
||||
fill_in('session-name', :with => unique_session_name)
|
||||
fill_in('session-description', :with => unique_session_desc)
|
||||
find('.btn-next').trigger(:click)
|
||||
if rsvp
|
||||
find('li[create-type="rsvp"] ins').trigger(:click)
|
||||
elsif immediate
|
||||
find('li[create-type="immediately"] ins').trigger(:click)
|
||||
elsif quickstart
|
||||
find('li[create-type="quick-start"] ins').trigger(:click)
|
||||
else
|
||||
find('li[create-type="schedule-future"] ins').trigger(:click)
|
||||
end
|
||||
|
||||
find('.btn-next').trigger(:click)
|
||||
|
||||
find('div#divSessionPolicy ins').trigger(:click)
|
||||
jk_select(musician_access_value, '#session-musician-access')
|
||||
jk_select(fan_permission_value, '#session-fans-access')
|
||||
find('.btn-next').trigger(:click)
|
||||
unless quickstart
|
||||
jk_select(genre, '#create-session-form select[name="genres"]')
|
||||
fill_in('session-name', :with => unique_session_name)
|
||||
fill_in('session-description', :with => unique_session_desc)
|
||||
find('.btn-next').trigger(:click)
|
||||
|
||||
find('.btn-next', text: 'PUBLISH SESSION').trigger(:click)
|
||||
find('.btn-next').trigger(:click)
|
||||
|
||||
find('div#divSessionPolicy ins').trigger(:click)
|
||||
jk_select(musician_access_value, '#session-musician-access')
|
||||
jk_select(fan_permission_value, '#session-fans-access')
|
||||
find('.btn-next').trigger(:click)
|
||||
end
|
||||
|
||||
unless quickstart || immediate
|
||||
find('.btn-next', text: 'PUBLISH SESSION').trigger(:click)
|
||||
else
|
||||
find('.btn-next', text: 'START SESSION').trigger(:click)
|
||||
end
|
||||
end
|
||||
|
||||
find('h2', text: 'create session')
|
||||
# find('h2', text: 'create session') unless quickstart || immediate
|
||||
|
||||
sleep 1 # to get rid of this, we need to verify that the URL is /client#/home.. otherwise intermittent fails
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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