* VRFS-1976 - fixed issue where UI always picks 1st scheduled session; VRFS-1962 - use 'full latency' instead of just internet latency in musician search, find session, and new musicians email
This commit is contained in:
parent
7e810809ac
commit
c3461e82a7
|
|
@ -197,4 +197,5 @@ update_sms_index.sql
|
|||
connection_allow_null_locidispid.sql
|
||||
track_user_in_scores.sql
|
||||
median_aggregate.sql
|
||||
current_scores_use_median.sql
|
||||
current_scores_use_median.sql
|
||||
current_scores_ams_index_sms_index_use_user_instrument.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;
|
||||
|
|
@ -185,6 +185,7 @@ module JamRuby
|
|||
score_join = 'left outer' # or 'inner'
|
||||
score_min = nil
|
||||
score_max = nil
|
||||
# these score_min, score_max come from here (doubled): https://jamkazam.atlassian.net/browse/VRFS-1962
|
||||
case score_limit
|
||||
when GOOD_SCORE
|
||||
score_join = 'inner'
|
||||
|
|
@ -193,14 +194,14 @@ module JamRuby
|
|||
when MODERATE_SCORE
|
||||
score_join = 'inner'
|
||||
score_min = 40
|
||||
score_max = 80
|
||||
score_max = 70
|
||||
when POOR_SCORE
|
||||
score_join = 'inner'
|
||||
score_min = 80
|
||||
score_max = 120
|
||||
score_max = 100
|
||||
when UNACCEPTABLE_SCORE
|
||||
score_join = 'inner'
|
||||
score_min = 120
|
||||
score_min = 100
|
||||
score_max = nil
|
||||
when SCORED_SCORE
|
||||
score_join = 'inner'
|
||||
|
|
@ -221,11 +222,11 @@ module JamRuby
|
|||
|
||||
rel = rel.joins('LEFT JOIN regions ON regions.countrycode = users.country AND regions.region = users.state')
|
||||
|
||||
rel = rel.where(['current_scores.score > ?', score_min]) unless score_min.nil?
|
||||
rel = rel.where(['current_scores.score <= ?', score_max]) unless score_max.nil?
|
||||
rel = rel.where(['current_scores.full_score > ?', score_min]) unless score_min.nil?
|
||||
rel = rel.where(['current_scores.full_score <= ?', score_max]) unless score_max.nil?
|
||||
|
||||
rel = rel.select('current_scores.score, regions.regionname')
|
||||
rel = rel.group('current_scores.score, regions.regionname')
|
||||
rel = rel.select('current_scores.full_score, current_scores.score, current_scores.b_audio_latency as audio_latency, regions.regionname')
|
||||
rel = rel.group('current_scores.full_score, current_scores.score, current_scores.b_audio_latency, regions.regionname')
|
||||
end
|
||||
|
||||
ordering = self.order_param(params)
|
||||
|
|
@ -249,7 +250,7 @@ module JamRuby
|
|||
end
|
||||
|
||||
unless locidispid.nil?
|
||||
rel = rel.order('current_scores.score ASC NULLS LAST')
|
||||
rel = rel.order('current_scores.full_score ASC NULLS LAST')
|
||||
end
|
||||
|
||||
rel = rel.order('users.created_at DESC')
|
||||
|
|
@ -393,15 +394,15 @@ module JamRuby
|
|||
# an offline process and thus uses the last jam location as "home base"
|
||||
|
||||
locidispid = usr.last_jam_locidispid
|
||||
score_limit = 60
|
||||
score_limit = 70
|
||||
limit = 50
|
||||
|
||||
rel = User.musicians_geocoded
|
||||
.where(['users.created_at >= ? AND users.id != ?', since_date, usr.id])
|
||||
.joins('inner join current_scores on users.last_jam_locidispid = current_scores.alocidispid')
|
||||
.where(['current_scores.blocidispid = ?', locidispid])
|
||||
.where(['current_scores.score <= ?', score_limit])
|
||||
.order('current_scores.score') # best scores first
|
||||
.where(['current_scores.full_score <= ?', score_limit])
|
||||
.order('current_scores.full_score') # best scores first
|
||||
.order('users.created_at DESC') # then most recent
|
||||
.limit(limit)
|
||||
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -89,8 +89,8 @@
|
|||
// these are raw scores as reported by client (round trip times)
|
||||
if (score == null) return "purple";
|
||||
if (0 < score && score <= 40) return "green";
|
||||
if (40 < score && score <= 80) return "yellow";
|
||||
if (80 < score && score <= 120) return "red";
|
||||
if (40 < score && score <= 70) return "yellow";
|
||||
if (70 < score && score <= 100) return "red";
|
||||
return "blue";
|
||||
}
|
||||
|
||||
|
|
@ -98,8 +98,8 @@
|
|||
// these are raw scores as reported by client (round trip times)
|
||||
if (score == null) return "missing";
|
||||
if (0 < score && score <= 40) return "good";
|
||||
if (40 < score && score <= 80) return "moderate";
|
||||
if (80 < score && score <= 120) return "poor";
|
||||
if (40 < score && score <= 70) return "moderate";
|
||||
if (70 < score && score <= 100) return "poor";
|
||||
return "unacceptable";
|
||||
}
|
||||
|
||||
|
|
@ -126,6 +126,7 @@
|
|||
var mVals, musician, renderings='';
|
||||
var instr_logos, instr;
|
||||
var follows, followVals, aFollow;
|
||||
var myAudioLatency = musicianList.my_audio_latency;
|
||||
|
||||
for (ii=0, len=musicians.length; ii < len; ii++) {
|
||||
musician = musicians[ii];
|
||||
|
|
@ -165,7 +166,7 @@
|
|||
};
|
||||
var musician_actions = context.JK.fillTemplate(aTemplate, actionVals);
|
||||
|
||||
var joined_score = musician['joined_score']
|
||||
var full_score = musician['full_score'];
|
||||
mVals = {
|
||||
avatar_url: context.JK.resolveAvatarUrl(musician.photo_url),
|
||||
profile_url: "/client#/profile/" + musician.id,
|
||||
|
|
@ -180,22 +181,27 @@
|
|||
musician_id: musician['id'],
|
||||
musician_follow_template: follows,
|
||||
musician_action_template: musician_actions,
|
||||
musician_one_way_score: score_to_text(joined_score),
|
||||
musician_score_color: score_to_color(joined_score),
|
||||
musician_score_color_alt: score_to_color_alt(joined_score)
|
||||
musician_one_way_score: score_to_text(full_score),
|
||||
musician_score_color: score_to_color(full_score),
|
||||
musician_score_color_alt: score_to_color_alt(full_score)
|
||||
|
||||
};
|
||||
var musician_row = context.JK.fillTemplate(mTemplate, mVals);
|
||||
renderings += musician_row;
|
||||
var $rendering = $(context.JK.fillTemplate(mTemplate, mVals))
|
||||
|
||||
var $offsetParent = $results.closest('.content');
|
||||
var options = {positions: ['top', 'bottom', 'right', 'left'], offsetParent: $offsetParent};
|
||||
var scoreOptions = {positions: ['right', 'top', 'bottom', 'left'], offsetParent: $offsetParent, width:'600px'};
|
||||
context.JK.helpBubble($('.follower-count', $rendering), 'musician-follower-count', {}, options);
|
||||
context.JK.helpBubble($('.friend-count', $rendering), 'musician-friend-count', {}, options);
|
||||
context.JK.helpBubble($('.recording-count', $rendering), 'musician-recording-count', {}, options);
|
||||
context.JK.helpBubble($('.session-count', $rendering), 'musician-session-count', {}, options);
|
||||
context.JK.helpBubble($('.score-count', $rendering), 'musician-score-count',
|
||||
{full_score: full_score ? Math.round(full_score / 2) : null, my_gear_latency: myAudioLatency, their_gear_latency:musician['audio_latency'], internet_latency: musician['score']},
|
||||
scoreOptions)
|
||||
|
||||
$results.append($rendering);
|
||||
}
|
||||
|
||||
var $renderings = $(renderings);
|
||||
var $offsetParent = $results.closest('.content');
|
||||
var options = {positions: ['top', 'bottom', 'right', 'left'], offsetParent: $offsetParent}
|
||||
context.JK.helpBubble($('.follower-count', $renderings), 'musician-follower-count', {}, options);
|
||||
context.JK.helpBubble($('.friend-count', $renderings), 'musician-friend-count', {}, options);
|
||||
context.JK.helpBubble($('.recording-count', $renderings), 'musician-recording-count', {}, options);
|
||||
context.JK.helpBubble($('.session-count', $renderings), 'musician-session-count', {}, options);
|
||||
$results.append($renderings);
|
||||
|
||||
$('.search-m-friend').on('click', friendMusician);
|
||||
$('.search-m-follow').on('click', followMusician);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@
|
|||
cornerRadius: 0,
|
||||
cssStyles: {
|
||||
fontSize: '11px',
|
||||
color: 'white',
|
||||
color: '#cccccc',
|
||||
whiteSpace: 'normal'
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -41,9 +41,13 @@ if @search.musicians_filter_search?
|
|||
node :page_count do |foo|
|
||||
@search.page_count
|
||||
end
|
||||
|
||||
node :my_audio_latency do |user|
|
||||
current_user.last_jam_audio_latency
|
||||
end
|
||||
|
||||
child(:results => :musicians) {
|
||||
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :email, :online, :musician, :photo_url, :biography, :joined_score, :regionname
|
||||
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :email, :online, :musician, :photo_url, :biography, :regionname, :score, :full_score, :audio_latency
|
||||
|
||||
node :is_friend do |musician|
|
||||
@search.is_friend?(musician)
|
||||
|
|
|
|||
|
|
@ -56,4 +56,26 @@
|
|||
|
||||
<script type="text/template" id="template-help-musician-session-count">
|
||||
The number of sessions that this user has played in.
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-help-musician-score-count">
|
||||
<div class="help-musician-score-count">
|
||||
<p>The score shown is the one-way latency (or delay) in milliseconds from you to this user. This score is calculated using the following three values that JamKazam gathers:</p>
|
||||
<ul>
|
||||
<li><span class="definition">Your Audio Gear Latency:</span> <span class="measurement my-gear-latency"><span class="measurement-value">{{data.my_gear_latency ? data.my_gear_latency + ' ms': '13 ms*'}}</span> <span class="measurement-absent">{{data.my_gear_latency ? '' : "(you have not qualified any gear, so we picked an average gear latency)"}}</span></span></li>
|
||||
<li><span class="definition">Their Audio Gear Latency:</span> <span class="measurement their-gear-latency"><span class="measurement-value">{{data.their_gear_latency ? data.their_gear_latency + ' ms': '13 ms*'}}</span> <span class="measurement-absent">{{data.their_gear_latency ? '' : "(they have not qualified any gear, so we picked an average gear latency)"}}</span></span></li>
|
||||
<li><span class="definition">Round-trip Internet Latency:</span> <span class="measurement internet-latency"><span class="measurement-value">{{data.internet_latency ? data.internet_latency + ' ms': '?'}}</span> <span class="measurement-absent">{{data.internet_latency ? '' : "(we have not scored you with this user yet)"}}</span></span></li>
|
||||
</ul>
|
||||
<p> <span class="definition">Total One-Way Latency:</span> <span class="measurement my-gear-latency"><span class="measurement-value">( {{data.my_gear_latency ? data.my_gear_latency: '13'}} + {{data.their_gear_latency ? data.their_gear_latency: '13'}} + {{data.internet_latency ? data.internet_latency: '?'}} ) / 2 = {{data.full_score ? data.full_score + ' ms' : "?"}}</span> <span class="measurement-absent">{{data.full_score ? '' : "(when we don't know internet latency, we don't try to guess your one-way latency)"}}</span></span>
|
||||
|
||||
<p>We categorize this score as good, fair, poor, unacceptable, or unknown. Those categories are defined as follows:
|
||||
<ul>
|
||||
<li><span class="definition">Good:</span> 20ms or less <img src="/assets/content/icon_green_score.png" /></li>
|
||||
<li><span class="definition">Fair:</span> 20ms to 35ms <img src="/assets/content/icon_yellow_score.png" /></li>
|
||||
<li><span class="definition">Poor:</span> 35ms to 50ms <img src="/assets/content/icon_red_score.png" /></li>
|
||||
<li><span class="definition">Unacceptable:</span> Above 50ms <img src="/assets/content/icon_blue_score.png" /></li>
|
||||
<li><span class="definition">Unknown:</span> No internet score is available between you and them. <img src="/assets/content/icon_purple_score.png" /></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</script>
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
<% end -%>
|
||||
|
||||
<!-- Session Row Template -->
|
||||
<script type="text/template" id="template-find-musician-row">
|
||||
<script type="text/template" id="template-find-musician-row">
|
||||
<div class="profile-band-list-result musician-list-result">
|
||||
<div class="f11" data-hint="container">
|
||||
<div class="left" style="width:63px;margin-top:-12px;">
|
||||
|
|
|
|||
|
|
@ -456,7 +456,7 @@
|
|||
<!-- Scheduled session template -->
|
||||
<script type="text/template" id="template-scheduled-session">
|
||||
<li>
|
||||
<input type="radio" name="scheduled-session-info" id="{{data.id}}" value="false" />
|
||||
<input type="radio" name="scheduled-session-info" data-session-id="{{data.id}}" value="false" />
|
||||
<label for="{{data.id}}" class="radio-text w85">
|
||||
{{data.scheduled_start}} : {{data.name}}
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,31 @@ describe "Create Session UI", :js => true, :type => :feature, :capybara_feature
|
|||
end
|
||||
end
|
||||
|
||||
# VRFS-1976
|
||||
it "create two sessions; select the second one" do
|
||||
|
||||
# this is tricky because of iCheck and it's custom way of dealing with 'checked' state
|
||||
start1 = 15.minutes.ago
|
||||
session1 = FactoryGirl.create(:music_session, description: 'My Session 1', creator: user1, scheduled_start: start1)
|
||||
session2 = FactoryGirl.create(:music_session, description: 'My Session 2', creator: user1)
|
||||
|
||||
|
||||
in_client(user1) do
|
||||
# should reload the session page, so that we see new sessions
|
||||
visit '/client#/createSession'
|
||||
|
||||
# pick the second session
|
||||
page.find("#scheduled-session-list input[data-session-id='#{session2.id}'] + ins").trigger(:click)
|
||||
page.find('.btn-next').trigger(:click)
|
||||
|
||||
# check if future dialog is showing; if so, accept
|
||||
accept = first('#btn-confirm-ok', text: "START SESSION NOW")
|
||||
accept.trigger(:click) if accept
|
||||
|
||||
page.find('#session-description-disp', text: 'My Session 2')
|
||||
end
|
||||
end
|
||||
|
||||
describe "step 1" do
|
||||
it "initial status" do
|
||||
in_client(user1) do
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ describe "Create Session", :js => true, :type => :feature, :capybara_feature =>
|
|||
sleep 2
|
||||
expect(page).to have_selector('h1', text: 'Future Session')
|
||||
expect(page).to have_content "Are you sure"
|
||||
find('#btn-confirm-ok', text: 'Start Session Now').trigger(:click)
|
||||
find('#btn-confirm-ok', text: 'START SESSION NOW').trigger(:click)
|
||||
sleep 1
|
||||
expect(page).to have_selector('.session-step-title', text: 'Review & Confirm')
|
||||
expect(page).to have_content first_session.name
|
||||
|
|
|
|||
|
|
@ -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']
|
||||
|
|
|
|||
|
|
@ -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