Merge commit 'dcbaf4d329fac3870b64925a8b89a40ff8ec1843'

This commit is contained in:
Seth Call 2013-10-25 07:20:38 -05:00
commit 9e7e905c87
169 changed files with 3772 additions and 1274 deletions

View File

@ -79,7 +79,7 @@ end
# gem 'capistrano'
# To use debugger
# gem 'debugger'
gem 'debugger'
group :development, :test do
gem 'capybara'

View File

@ -0,0 +1,5 @@
ActiveAdmin.register JamRuby::IspScoreBatch, :as => 'Isp Score Data' do
config.sort_order = 'created_at_desc'
end

View File

@ -1,4 +1,4 @@
ActiveAdmin.register JamRuby::ArtifactUpdate do
ActiveAdmin.register JamRuby::ArtifactUpdate, :as => 'Artifacts' do
menu :label => 'Artifacts'
config.sort_order = 'product,environment'

View File

@ -1,4 +1,4 @@
ActiveAdmin.register JamRuby::InvitedUser do
ActiveAdmin.register JamRuby::InvitedUser, :as => 'Invited Users' do
menu :label => 'Invite Users'
config.sort_order = 'created_at'

View File

@ -1,4 +1,4 @@
ActiveAdmin.register JamRuby::User do
ActiveAdmin.register JamRuby::User, :as => 'Users' do
menu :label => 'Jam User'

View File

@ -6,15 +6,24 @@ ActiveAdmin.register JamRuby::MusicSessionHistory, :as => 'Music Session History
controller do
def scoped_collection
@music_session_histories ||= end_of_association_chain
.includes([:user, :band])
.order('created_at DESC')
if params['admin'].blank? || '1' == params['admin']
@music_session_histories ||= end_of_association_chain
.includes([:user, :band])
.order('created_at DESC')
else
@music_session_histories ||= end_of_association_chain
.joins('INNER JOIN users AS uu ON uu.id = music_sessions_history.user_id')
.where(['uu.admin = ?','f'])
.includes([:user, :band])
.order('created_at DESC')
end
end
end
index :as => :block do |msh|
div :for => msh do
h3 "Session ##{msh.music_session_id}: #{msh.created_at.strftime('%b %d %Y, %H:%M')}"
h4 "(append URL with ?admin=0 to hide admin sessions)"
columns do
column do
panel 'Session Details' do

View File

@ -11,25 +11,25 @@ describe "Artifact Update" do
before { sign_in user }
describe "crud" do
before { visit admin_jam_ruby_artifact_updates_path }
it { should have_selector('h2', text: "Jam Ruby Artifact Updates") }
before { visit admin_artifacts_path }
it { should have_selector('h2', text: "Artifacts") }
describe "upload artifact" do
in_directory_with_file(ARTIFACT_FILE)
before do
visit new_admin_jam_ruby_artifact_update_path
fill_in "jam_ruby_artifact_update_version", with: "1"
fill_in "jam_ruby_artifact_update_environment", with: "public"
select('JamClient/Win32', from: "jam_ruby_artifact_update_product")
fill_in "jam_ruby_artifact_update_environment", with: "public"
attach_file("jam_ruby_artifact_update_uri", ARTIFACT_FILE)
visit new_admin_artifact_path
fill_in "artifacts_version", with: "1"
fill_in "artifacts_environment", with: "public"
select('JamClient/Win32', from: "artifacts_product")
fill_in "artifacts_environment", with: "public"
attach_file("artifacts_uri", ARTIFACT_FILE)
click_button "Create Artifact update"
end
it {
should have_selector('#main_content .panel:first-child h3', text: "Jam Ruby Artifact Update Details" ) }
should have_selector('#main_content .panel:first-child h3', text: "Artifacts Details" ) }
end
end
end

View File

@ -12,8 +12,8 @@ describe InvitedUser do
describe "enduser management" do
# let's go the management page for users
before { visit admin_jam_ruby_invited_users_path }
it { should have_selector('h2', text: "Jam Ruby Invited Users") }
before { visit admin_invited_users_path }
it { should have_selector('h2', text: "Invited Users") }
describe "create service invite" do
@ -21,15 +21,13 @@ describe InvitedUser do
# create a new user
UserMailer.deliveries.clear
visit new_admin_jam_ruby_invited_user_path
fill_in "jam_ruby_invited_user_email", with: "some_silly_guy@jamkazam.com"
#fill_in "jam_ruby_invited_user_sender_id", with: "some_silly_guy@jamkazam.com"
#fill_in "jam_ruby_invited_user_autofriend", with: "some_silly_guy@jamkazam.com"
visit new_admin_invited_user_path
fill_in "invited_users_email", with: "some_silly_guy@jamkazam.com"
click_button "Create Invited user"
end
it { should have_selector('#main_content .panel:first-child h3', text: "Jam Ruby Invited User Details" ); }
it { should have_selector('#main_content .panel:first-child h3', text: "Invited Users Details" ); }
it { UserMailer.deliveries.length.should == 1 }
end
end

View File

@ -73,3 +73,4 @@ crash_dumps.sql
crash_dumps_idx.sql
music_sessions_user_history_add_session_removed_at.sql
user_progress_tracking.sql
whats_next.sql

1
db/up/whats_next.sql Normal file
View File

@ -0,0 +1 @@
ALTER TABLE users ADD COLUMN show_whats_next boolean DEFAULT TRUE;

View File

@ -28,6 +28,8 @@ message ClientMessage {
MUSICIAN_SESSION_FRESH = 115;
MUSICIAN_SESSION_STALE = 116;
HEARTBEAT_ACK = 117;
JOIN_REQUEST_APPROVED = 118;
JOIN_REQUEST_REJECTED = 119;
TEST_SESSION_MESSAGE = 200;
@ -72,6 +74,8 @@ message ClientMessage {
optional MusicianSessionFresh musician_session_fresh = 115;
optional MusicianSessionStale musician_session_stale = 116;
optional HeartbeatAck heartbeat_ack = 117;
optional JoinRequestApproved join_request_approved = 118;
optional JoinRequestRejected join_request_rejected = 119;
// Client-Session messages (to/from)
optional TestSessionMessage test_session_message = 200;
@ -195,8 +199,32 @@ message MusicianSessionStale {
message JoinRequest {
optional string join_request_id = 1;
optional string username = 2;
optional string text = 3;
optional string session_id = 2;
optional string username = 3;
optional string photo_url = 4;
optional string msg = 5;
optional string notification_id = 6;
optional string created_at = 7;
}
message JoinRequestApproved {
optional string join_request_id = 1;
optional string session_id = 2;
optional string username = 3;
optional string photo_url = 4;
optional string msg = 5;
optional string notification_id = 6;
optional string created_at = 7;
}
message JoinRequestRejected {
optional string join_request_id = 1;
optional string session_id = 2;
optional string username = 3;
optional string photo_url = 4;
optional string msg = 5;
optional string notification_id = 6;
optional string created_at = 7;
}
// route_to: session

View File

@ -10,7 +10,8 @@ require "will_paginate/active_record"
require "action_mailer"
require "devise"
require "sendgrid"
require 'postgres-copy'
require "postgres-copy"
require "jam_ruby/lib/module_overrides"
require "jam_ruby/constants/limits"
require "jam_ruby/constants/notification_types"
require "jam_ruby/constants/validation_messages"
@ -75,9 +76,10 @@ require "jam_ruby/models/recorded_track"
require "jam_ruby/models/mix"
require "jam_ruby/models/claimed_recording"
require "jam_ruby/models/crash_dump"
require "jam_ruby/models/isp_score_batch"
include Jampb
module JamRuby
end
end

View File

@ -9,6 +9,9 @@ module NotificationTypes
# session notifications
SESSION_INVITATION = "SESSION_INVITATION"
SESSION_ENDED = "SESSION_ENDED" # used to remove session-related notification from sidebar
JOIN_REQUEST = "JOIN_REQUEST"
JOIN_REQUEST_APPROVED = "JOIN_REQUEST_APPROVED"
JOIN_REQUEST_REJECTED = "JOIN_REQUEST_REJECTED"
# musician notifications
MUSICIAN_SESSION_JOIN = "MUSICIAN_SESSION_JOIN"

View File

@ -0,0 +1,11 @@
require 'json'
class String
def is_json?
begin
!!JSON.parse(self)
rescue
false
end
end
end

View File

@ -124,11 +124,22 @@
return Jampb::ClientMessage.new(:type => ClientMessage::Type::MUSICIAN_SESSION_STALE, :route_to => CLIENT_TARGET, :musician_session_stale => stale)
end
# create a join request session message
def join_request(join_request_id, session_id, username, photo_url, msg, notification_id, created_at)
req = Jampb::JoinRequest.new(:join_request_id => join_request_id, :session_id => session_id, :username => username, :photo_url => photo_url, :msg => msg, :notification_id => notification_id, :created_at => created_at)
return Jampb::ClientMessage.new(:type => ClientMessage::Type::JOIN_REQUEST, :route_to => SESSION_TARGET_PREFIX + session_id, :join_request => req)
end
# create a user-joined session message
def join_request(session_id, join_request_id, username, text)
join_request = Jampb::JoinRequest.new(:join_request_id => join_request_id, :username => username, :text => text)
return Jampb::ClientMessage.new(:type => ClientMessage::Type::JOIN_REQUEST, :route_to => SESSION_TARGET_PREFIX + session_id, :join_request => join_request)
# create a join request approved session message
def join_request_approved(join_request_id, session_id, username, photo_url, msg, notification_id, created_at)
req_approved = Jampb::JoinRequestApproved.new(:join_request_id => join_request_id, :session_id => session_id, :username => username, :photo_url => photo_url, :msg => msg, :notification_id => notification_id, :created_at => created_at)
return Jampb::ClientMessage.new(:type => ClientMessage::Type::JOIN_REQUEST_APPROVED, :route_to => SESSION_TARGET_PREFIX + session_id, :join_request_approved => req_approved)
end
# create a join request rejected session message
def join_request_rejected(join_request_id, session_id, username, photo_url, msg, notification_id, created_at)
req_rejected = Jampb::JoinRequestRejected.new(:join_request_id => join_request_id, :session_id => session_id, :username => username, :photo_url => photo_url, :msg => msg, :notification_id => notification_id, :created_at => created_at)
return Jampb::ClientMessage.new(:type => ClientMessage::Type::JOIN_REQUEST_REJECTED, :route_to => SESSION_TARGET_PREFIX + session_id, :join_request_rejected => req_rejected)
end
# create a test message to send in session

View File

@ -0,0 +1,16 @@
module JamRuby
class IspScoreBatch < ActiveRecord::Base
self.primary_key = 'id'
self.table_name = 'isp_score_batch'
attr_accessible :json_scoring_data
validates :json_scoring_data, :presence => true
validate :json_format
def json_format
errors[:json_scoring_data] << "not in json format" unless !json_scoring_data.nil? && json_scoring_data.is_json?
end
end
end

View File

@ -11,7 +11,7 @@ module JamRuby
validates :user, :presence => true
validates :music_session, :presence => true
validates :text, presence: false, no_profanity: true, length: {maximum: 140} # arbitrary decision of 140. the database is at 2000 max on this field
validates :text, no_profanity: true, length: {maximum: 140} # arbitrary decision of 140. the database is at 2000 max on this field
validates_uniqueness_of :user_id, :scope => :music_session_id

View File

@ -24,7 +24,10 @@ module JamRuby
end
def self.save(music_session_id, user_id, client_id)
session_user_history = MusicSessionUserHistory.new()
return true if 0 < self.where(:music_session_id => music_session_id,
:user_id => user_id,
:client_id => client_id).count
session_user_history = MusicSessionUserHistory.new
session_user_history.music_session_id = music_session_id
session_user_history.user_id = user_id
session_user_history.client_id = client_id

View File

@ -108,32 +108,46 @@ module JamRuby
return ids
end
def format_msg(description, user)
def format_msg(description, user = nil)
name = ""
unless user.nil?
name = user.name
else
name = "Someone"
end
case description
when NotificationTypes::FRIEND_UPDATE
return "#{user.name} is now "
return "#{name} is now "
when NotificationTypes::FRIEND_REQUEST
return "#{user.name} has sent you a friend request."
return "#{name} has sent you a friend request."
when NotificationTypes::FRIEND_REQUEST_ACCEPTED
return "#{user.name} has accepted your friend request."
return "#{name} has accepted your friend request."
when NotificationTypes::FRIEND_SESSION_JOIN
return "#{user.name} has joined the session."
return "#{name} has joined the session."
when NotificationTypes::MUSICIAN_SESSION_JOIN
return "#{user.name} has joined the session."
return "#{name} has joined the session."
when NotificationTypes::MUSICIAN_SESSION_DEPART
return "#{user.name} has left the session."
return "#{name} has left the session."
when NotificationTypes::SESSION_INVITATION
return "#{user.name} has invited you to a session."
return "#{name} has invited you to a session."
when NotificationTypes::JOIN_REQUEST
return "#{name} has requested to join your session."
when NotificationTypes::JOIN_REQUEST_APPROVED
return "#{name} has approved your request to join the session."
when NotificationTypes::JOIN_REQUEST_REJECTED
return "We're sorry, but you cannot join the session at this time."
# when "social_media_friend_joined"
# when "join_request_approved"
# when "join_request_rejected"
# when "band_invitation"
# when "band_invitation_accepted"
# when "recording_available"
@ -174,7 +188,7 @@ module JamRuby
notification.save
# (2) create notification
notification_msg = format_msg(NotificationTypes::FRIEND_REQUEST, user)
notification_msg = format_msg(notification.description, user)
msg = @@message_factory.friend_request(friend_request_id, user_id, user.name, user.photo_url, friend_id, notification_msg, notification.id, notification.created_at.to_s)
# (3) send notification
@ -193,7 +207,7 @@ module JamRuby
notification.save
# (2) create notification
notification_msg = format_msg(NotificationTypes::FRIEND_REQUEST_ACCEPTED, friend)
notification_msg = format_msg(notification.description, friend)
msg = @@message_factory.friend_request_accepted(friend_id, friend.name, friend.photo_url, user_id, notification_msg, notification.id, notification.created_at.to_s)
# (3) send notification
@ -224,7 +238,6 @@ module JamRuby
msg = @@message_factory.musician_session_join(music_session.id, user.id, user.name, user.photo_url)
# (2) send notification
puts "CONNECTION.CLIENT_ID=#{connection.client_id}"
@@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => connection.client_id})
end
@ -269,15 +282,58 @@ module JamRuby
end
end
def send_join_request(music_session, join_request, sender, text)
def send_join_request(music_session, join_request, text)
# (1) save to database
notification = Notification.new
notification.description = NotificationTypes::JOIN_REQUEST
notification.source_user_id = join_request.user.id
notification.target_user_id = music_session.creator.id
notification.session_id = music_session.id
notification.save
# (2) create notification
msg = @@message_factory.join_request(music_session.id, join_request.id, sender.name, text)
notification_msg = format_msg(notification.description, join_request.user)
msg = @@message_factory.join_request(join_request.id, music_session.id, join_request.user.name, join_request.user.photo_url, notification_msg, notification.id, notification.created_at.to_s)
# (3) send notification
@@mq_router.server_publish_to_session(music_session, msg)
@@mq_router.publish_to_user(music_session.creator.id, msg)
end
def send_join_request_approved(music_session, join_request)
# (1) save to database
notification = Notification.new
notification.description = NotificationTypes::JOIN_REQUEST_APPROVED
notification.source_user_id = music_session.creator.id
notification.target_user_id = join_request.user.id
notification.session_id = music_session.id
notification.save
# (2) create notification
notification_msg = format_msg(notification.description, music_session.creator)
msg = @@message_factory.join_request_approved(join_request.id, music_session.id, music_session.creator.name, music_session.creator.photo_url, notification_msg, notification.id, notification.created_at.to_s)
# (3) send notification
@@mq_router.publish_to_user(join_request.user.id, msg)
end
def send_join_request_rejected(music_session, join_request)
# (1) save to database
notification = Notification.new
notification.description = NotificationTypes::JOIN_REQUEST_REJECTED
notification.source_user_id = music_session.creator.id
notification.target_user_id = join_request.user.id
notification.session_id = music_session.id
notification.save
# (2) create notification
notification_msg = format_msg(notification.description, music_session.creator)
msg = @@message_factory.join_request_rejected(join_request.id, music_session.id, music_session.creator.name, music_session.creator.photo_url, notification_msg, notification.id, notification.created_at.to_s)
# (3) send notification
@@mq_router.publish_to_user(join_request.user.id, msg)
end
end

View File

@ -124,6 +124,7 @@ module JamRuby
validates :terms_of_service, :acceptance => {:accept => true, :on => :create, :allow_nil => false }
validates :subscribe_email, :inclusion => {:in => [nil, true, false]}
validates :musician, :inclusion => {:in => [true, false]}
validates :show_whats_next, :inclusion => {:in => [nil, true, false]}
# custom validators
validate :validate_musician_instruments
@ -651,7 +652,7 @@ module JamRuby
# throws ActiveRecord::RecordNotFound if instrument is invalid
# throws an email delivery error if unable to connect out to SMTP
def self.signup(first_name, last_name, email, password, password_confirmation, terms_of_service, subscribe_email,
def self.signup(first_name, last_name, email, password, password_confirmation, terms_of_service,
location, instruments, birth_date, musician, photo_url, invited_user, signup_confirm_url)
user = User.new
@ -659,7 +660,7 @@ module JamRuby
user.first_name = first_name
user.last_name = last_name
user.email = email
user.subscribe_email = subscribe_email
user.subscribe_email = true
user.terms_of_service = terms_of_service
user.musician = musician

View File

@ -0,0 +1,19 @@
require 'spec_helper'
describe IspScoreBatch do
let (:score) {IspScoreBatch.new}
it "valid json" do
score.json_scoring_data = "{}"
score.save.should be_true
end
it "no json" do
score.save.should be_false
end
it "invalid json" do
score.json_scoring_data = "blurp a durp"
score.save.should be_false
end
end

View File

@ -88,14 +88,13 @@ end
gem 'capybara-screenshot'
gem 'cucumber-rails', :require => false #, '1.3.0', :require => false
gem 'factory_girl_rails', '4.1.0'
gem 'database_cleaner', '0.7.0'
gem 'guard-spork', '0.3.2'
gem 'spork', '0.9.0'
gem 'launchy', '2.1.0'
gem 'rack-test'
# gem 'rb-fsevent', '0.9.1', :require => false
# gem 'growl', '1.0.3'
gem 'poltergeist' , '1.4.1' # can't go to 1.4.0 until this is fixed https://github.com/jonleighton/poltergeist/issues/385
gem 'poltergeist'
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -22,6 +22,8 @@
FRIEND_UPDATE : "FRIEND_UPDATE",
SESSION_INVITATION : "SESSION_INVITATION",
JOIN_REQUEST : "JOIN_REQUEST",
JOIN_REQUEST_APPROVED : "JOIN_REQUEST_APPROVED",
JOIN_REQUEST_REJECTED : "JOIN_REQUEST_REJECTED",
FRIEND_REQUEST : "FRIEND_REQUEST",
FRIEND_REQUEST_ACCEPTED : "FRIEND_REQUEST_ACCEPTED",
TEST_SESSION_MESSAGE : "TEST_SESSION_MESSAGE",

View File

@ -91,12 +91,12 @@
function navToEditIdentity() {
resetForm()
window.location = '#/account/identity'
window.location = '/client#/account/identity'
}
function navToEditProfile() {
resetForm()
window.location = '#/account/profile'
window.location = '/client#/account/profile'
}
function navToEditSubscriptions() {
@ -109,7 +109,7 @@
function navToEditAudio() {
resetForm()
window.location = "#/account/audio"
window.location = "/client#/account/audio"
}
// handle update avatar event

View File

@ -74,7 +74,7 @@
function navToAccount() {
resetForm();
window.location = '#/account';
window.location = '/client#/account';
}
function handleUpdateEmail() {

View File

@ -38,7 +38,8 @@
last_name: userDetail.last_name,
user_instruments: userDetail.instruments,
birth_date : userDetail.birth_date,
gender: userDetail.gender
gender: userDetail.gender,
subscribe_email: userDetail.subscribe_email ? "checked=checked" : ""
});
var content_root = $('#account-profile-content-scroller')
@ -263,12 +264,12 @@
function navToAccount() {
resetForm();
window.location = '#/account';
window.location = '/client#/account';
}
function navToAvatar() {
resetForm();
window.location = '#/account/profile/avatar';
window.location = '/client#/account/profile/avatar';
}
function handleUpdateProfile() {
@ -280,11 +281,12 @@
var city = getCityElement().val();
var firstName = getFirstNameElement().val();
var lastName = getLastNameElement().val();
var gender = getGenderElement().val()
var gender = getGenderElement().val();
var subscribeEmail = getSubscribeEmail().is(':checked');
var birthDate = getBirthDate();
var instruments = getInstrumentsValue();
postUpdateProfile({
api.updateUser({
country: country,
state: region,
city: city,
@ -292,25 +294,13 @@
last_name: lastName,
gender: gender,
birth_date: birthDate,
instruments: instruments
instruments: instruments,
subscribe_email: subscribeEmail
})
.done(postUpdateProfileSuccess)
.fail(postUpdateProfileFailure)
}
function postUpdateProfile(options) {
var url = "/api/users/" + context.JK.currentUserId;
return $.ajax({
type: "POST",
dataType: "json",
contentType: 'application/json',
url: url,
data: JSON.stringify(options),
processData: false
});
}
function postUpdateProfileSuccess(response) {
app.notify(
{ title: "Profile Changed",
@ -332,6 +322,7 @@
var city = context.JK.format_errors("city", errors);
var birth_date = context.JK.format_errors("birth_date", errors);
var gender = context.JK.format_errors("birth_date", errors);
var subscribeEmail = context.JK.format_errors("subscribe_email", errors);
var instruments = context.JK.format_errors("musician_instruments", errors)
if(first_name != null) {
@ -358,6 +349,10 @@
getYearElement().closest('div.field').addClass('error').end().after(birth_date);
}
if(subscribeEmail != null) {
getSubscribeEmail().closest('div.field').addClass('error').end().after(subscribeEmail);
}
if(gender != null) {
getGenderElement().closest('div.field').addClass('error').end().after(gender);
}
@ -480,6 +475,10 @@
return $('#account-profile-content-scroller select#user_birth_date_1i');
}
function getSubscribeEmail() {
return $('#account-profile-content-scroller input[name=subscribe_email]');
}
function getInstrumentsElement() {
return $('#account-profile-content-scroller .instrument_selector');
}

View File

@ -15,6 +15,7 @@
var selection = null;
var targetCropSize = 88;
var updatingAvatar = false;
var userDropdown;
function beforeShow(data) {
userId = data.id;
@ -92,7 +93,7 @@
function deleteAvatarSuccess(response) {
renderAvatar(null, null);
JK.Header.loadMe();
userDropdown.loadMe();
rest.getUserDetail()
.done(function(userDetail) {
@ -139,7 +140,7 @@
function navToEditProfile() {
resetForm();
window.location = '#/account/profile'
window.location = '/client#/account/profile'
}
function renderAvatarSpinner() {
@ -306,8 +307,9 @@
quality: 90,
policy: filepickerPolicy.policy,
signature: filepickerPolicy.signature
}, { path: createStorePath(self.userDetail) + 'cropped.jpg', access: 'public' },
}, { path: createStorePath(self.userDetail) + 'cropped-' + new Date().getTime() + '.jpg', access: 'public' },
function(cropped) {
logger.debug("converting cropped");
rest.getFilepickerPolicy({handle: cropped.url, convert: true})
.done(function(filepickerPolicy) {
filepicker.convert(cropped, {
@ -320,6 +322,7 @@
signature: filepickerPolicy.signature
}, { path: createStorePath(self.userDetail), access: 'public' },
function(scaled) {
logger.debug("converted and scaled final image %o", scaled);
rest.updateAvatar({
original_fpfile: determineCurrentFpfile(),
cropped_fpfile: scaled,
@ -361,7 +364,7 @@
self.userDetail = response;
// notify any listeners that the avatar changed
JK.Header.loadMe();
userDropdown.loadMe();
// $('.avatar_large img').trigger('avatar_changed', [self.userDetail.photo_url]);
app.notify(
@ -414,13 +417,15 @@
return $.cookie('original_fpfile') == null ? userDetail.crop_selection : null;
}
function initialize() {
function initialize(userDropdownInstance) {
var screenBindings = {
'beforeShow': beforeShow,
'afterShow': afterShow
};
app.bindScreen('account/profile/avatar', screenBindings);
events();
userDropdown = userDropdownInstance;
}
this.initialize = initialize;

View File

@ -0,0 +1,31 @@
(function(context,$) {
"use strict";
context.JK = context.JK || {};
context.JK.AlertDialog = function(app, okButtonText, message, sessionId, callback) {
var logger = context.JK.logger;
function events() {
$('#btn-alert-ok').unbind("click");
$('#btn-alert-ok').click(function(evt) {
callback(sessionId);
});
$('#btn-alert-cancel').unbind("click");
$('#btn-alert-cancel').click(function(evt) {
app.layout.closeDialog('alert');
});
}
function initialize() {
events();
$('#btn-alert-ok').text(okButtonText);
$('#alert-message').html(message);
}
this.initialize = initialize;
};
})(window,jQuery);

View File

@ -12,10 +12,10 @@
//
//= require jquery
//= require jquery_ujs
//= require jquery.icheck
//= require jquery.color
//= require jquery.cookie
//= require jquery.Jcrop
//= require jquery.naturalsize
//= require jquery.queryparams
//= require bootstrap
//= require_directory .

View File

@ -273,17 +273,17 @@
}
function isChatInput(id) {
// copy the arrays since $.grep modifies them
// copy the arrays
var chatOtherUnassignedListCopy = chatOtherUnassignedList;
var chatOtherAssignedListCopy = chatOtherAssignedList;
// is this input in the unassigned list?
$.grep(chatOtherUnassignedListCopy, function(n,i){
chatOtherUnassignedListCopy = $.grep(chatOtherUnassignedListCopy, function(n,i){
return n.chat && n.id === id;
});
// is this input in the assigned list?
$.grep(chatOtherAssignedListCopy, function(n,i){
chatOtherAssignedListCopy = $.grep(chatOtherAssignedListCopy, function(n,i){
return n.chat && n.id === id;
});
@ -332,6 +332,7 @@
// add each unused music input if it doesn't already exist
$('#audio-inputs-unused > option').each(function() {
if ($('#voice-inputs-unused > option[value="' + this.value + '"]').length === 0) {
logger.debug("Appending " + this.value + " to the unused voice input box.");
$('#voice-inputs-unused').append('<option value="' + this.value + '">' + this.text + '</option>');
}
});
@ -343,6 +344,7 @@
// if this input is not already in the available music inputs box and the selected input is not chat input, add
// it to the unused music inputs box
if ($('#audio-inputs-unused > option[value="' + this.value + '"]').length === 0 && !isChatInput(this.value)) {
logger.debug("Appending " + this.value + " to the unused audio input box.");
$('#audio-inputs-unused').append('<option value="' + this.value + '">' + this.text + '</option>');
}
});

View File

@ -7,7 +7,7 @@
var logger = context.JK.logger;
var realtimeMessaging = context.JK.JamServer;
var friendSelectorDialog = new context.JK.FriendSelectorDialog(app, friendSelectorCallback);
var invitationDialog = new context.JK.InvitationDialog(app);
var invitationDialog = null;
var autoComplete = null;
var userNames = [];
var userIds = [];
@ -100,7 +100,7 @@
}
function resetForm() {
$('#intellectual-property').attr('checked', false);
$('#intellectual-property').iCheck('uncheck').attr('checked', false);
var $form = $('#create-session-form');
var description = sessionSettings.hasOwnProperty('description') ? sessionSettings.description : '';
@ -114,7 +114,7 @@
if (musician_access) {
var approval_required = sessionSettings.hasOwnProperty('approval_required') ? sessionSettings.approval_required : false;
$('#musician-access-option-' + approval_required).attr('checked', 'checked');
$('#musician-access-option-' + approval_required).iCheck('check').attr('checked', 'checked');
}
var fan_access = sessionSettings.hasOwnProperty('fan_access') ? sessionSettings.fan_access : true;
@ -123,7 +123,7 @@
if (fan_access) {
var fan_chat = sessionSettings.hasOwnProperty('fan_chat') ? sessionSettings.fan_chat : false;
$('#fan-chat-option-' + fan_chat).attr('checked', 'checked');
$('#fan-chat-option-' + fan_chat).iCheck('check').attr('checked', 'checked');
}
// Should easily be able to grab other items out of sessionSettings and put them into the appropriate ui elements.
}
@ -293,7 +293,7 @@
friendSelectorDialog.showDialog(selectedFriendIds);
});
$('.btn-email-invitation').click(function() {
$('div[layout-id="createSession"] .btn-email-invitation').click(function() {
invitationDialog.showEmailDialog();
});
@ -374,6 +374,15 @@
resetForm();
}
// this exists solely due to a bug in Windows QTWebkit: https://bugreports.qt-project.org/browse/QTBUG-30072
function initializeButtons() {
$('div[layout-id="createSession"] .icheckbuttons input').iCheck({
checkboxClass: 'icheckbox_minimal',
radioClass: 'iradio_minimal',
inheritClass: true
});
}
function searchFriends(query) {
if (query.length < 2) {
$('#friend-search-results').empty();
@ -403,12 +412,13 @@
});
}
function initialize() {
function initialize(invitationDialogInstance) {
friendSelectorDialog.initialize();
invitationDialog.initialize();
invitationDialog = invitationDialogInstance;
events();
loadBands();
loadSessionSettings();
initializeButtons();
var screenBindings = { 'beforeShow': beforeShow, 'afterShow': afterShow };
app.bindScreen('createSession', screenBindings);
}

View File

@ -1,7 +1,7 @@
(function(context,$) {
/**
* Javascript for managing the header (account dropdown) as well
* Javascript for managing the header as well
* as any dialogs reachable from there. Account settings dialog.
*/
@ -17,8 +17,7 @@
var instrumentIds = [];
var instrumentNames = [];
var instrumentPopularities = {}; // id -> popularity
var invitationDialog = new context.JK.InvitationDialog(app);
var rest = new JK.Rest();
function loadInstruments() {
// TODO: This won't work in the long-term. We'll need to provide
@ -98,45 +97,10 @@
context.location = '#/home';
});
$('.userinfo').on('click', function() {
$('ul.shortcuts', this).toggle();
});
$('.userinfo .invite-friends .menuheader').on('click', function(e) {
$(this).closest('li').css('height', 'auto').find('ul').toggle();
e.stopPropagation();
return false;
});
/**
$('.userinfo .sign-out a').on('click', function(e) {
e.stopPropagation();
/** rest.signout()
.done(function() {
})
}); */
$('.invite-friends .google-invite a').on('click', function(e) {
invitationDialog.showGoogleDialog();
});
$('.invite-friends .email-invite a').on('click', function(e) {
invitationDialog.showEmailDialog();
})
$('#account-identity-form').submit(handleIdentitySubmit);
$('#account-profile-form').submit(handleProfileSubmit);
// Remove added instruments when 'X' is clicked
$('#added-profile-instruments').on("click", ".instrument span", removeInvitation);
$('#header-avatar').on('avatar_changed', function(event, newAvatarUrl) {
updateAvatar(newAvatarUrl);
event.preventDefault();
return false;
})
}
function handleIdentitySubmit(evt) {
@ -194,52 +158,12 @@
return false;
}
function loadMe() {
$.ajax({
url: '/api/users/' + context.JK.currentUserId
}).done(function(r) {
userMe = r;
// TODO - Setting global variable for local user.
context.JK.userMe = r;
updateHeader();
}).fail(app.ajaxError);
}
function updateHeader() {
$('#user').html(userMe.name);
showAvatar();
}
// initially show avatar
function showAvatar() {
var photoUrl = context.JK.resolveAvatarUrl(userMe.photo_url);
$('#header-avatar').attr('src', photoUrl);
}
// handle update avatar event
function updateAvatar(avatar_url) {
var photoUrl = context.JK.resolveAvatarUrl(avatar_url);
var avatar = $(new Image());
avatar.attr('src', photoUrl + '?cache_bust=' + new Date().getTime());
avatar.attr('alt', "Avatar");
avatar.attr('id', 'header-avatar');
$('#header-avatar').replaceWith(avatar);
}
this.initialize = function() {
events();
loadInstruments();
loadMe();
invitationDialog.initialize();
//searcher = new context.JK.Searcher(app);
//searcher.initialize();
};
this.loadMe = loadMe;
};
})(window,jQuery);

View File

@ -8,17 +8,6 @@
var isFtueComplete = false;
function beforeShow(data) {
refreshDeviceState();
}
function refreshDeviceState() {
isFtueComplete = checkDeviceState();
updateTiles();
}
function checkDeviceState() {
var devices = context.jamClient.TrackGetDevices();
return Object.keys(devices).length > 0;
}
function mouseenterTile() {
@ -49,19 +38,7 @@
}
function userHasDevices() {
var $createSession = $('div[type="createSession"]');
var $findSession = $('div[type="findSession"]');
$createSession.attr('layout-link', 'createSession');
$findSession.attr('layout-link', 'findSession');
// undo any earlier disabling
$('h2', $createSession).removeClass('disabled');
$('h2', $findSession).removeClass('disabled');
$createSession.removeClass('createsession-disabled');
$createSession.addClass('createsession');
$findSession.removeClass('findsession-disabled');
$findSession.addClass('findsession');
// and enable features
$($createSession).on('mouseenter', mouseenterTile);
@ -97,36 +74,22 @@
// used to initialize things that do not have to be touched up in the same UI sessiion
function events() {
// initialize profile, feed and account tiles normally
$('.homecard.profile, .homecard.feed, .homecard.account').on('mouseenter', mouseenterTile);
$('.homecard.profile, .homecard.feed, .homecard.account').on('mouseleave', mouseleaveTile);
$('div[layout-id=ftue]').on("ftue_success", refreshDeviceState);
$('.homecard').on('mouseenter', mouseenterTile);
$('.homecard').on('mouseleave', mouseleaveTile);
if(gon.allow_force_native_client) {
$('body').on('keyup', switchClientMode);
}
}
function updateTiles() {
logger.debug("isFtueComplete=" + isFtueComplete);
if(isFtueComplete) {
userHasDevices();
}
else {
userHasNoDevices();
}
$('.profile').on('click', function() {
context.location = '#/profile/' + context.JK.currentUserId;
});
}
this.initialize = function() {
var screenBindings = { 'beforeShow': beforeShow };
app.bindScreen('home', screenBindings);
events();
$('.profile').on('click', function() {
context.location = '#/profile/' + context.JK.currentUserId;
});
};
this.beforeShow = beforeShow;

View File

@ -106,7 +106,6 @@
$('#btn-send-invitation').show();
$('#btn-next-invitation').hide();
clearTextFields();
app.layout.showDialog('inviteUsers')
}

View File

@ -12,6 +12,29 @@
var self = this;
var logger = context.JK.logger;
function createJoinRequest(joinRequest) {
logger.debug("joinRequest=" + JSON.stringify(joinRequest));
return $.ajax({
type: "POST",
dataType: "json",
url: '/api/join_requests',
contentType: 'application/json',
processData: false,
data: JSON.stringify(joinRequest)
});
}
function updateJoinRequest(joinRequestId, isApproved) {
return $.ajax({
type: "PUT",
dataType: "json",
url: '/api/join_requests/' + joinRequestId,
contentType: 'application/json',
processData: false,
data: JSON.stringify({"approved": isApproved})
});
}
function updateSession(id, newSession, onSuccess) {
logger.debug('Rest.updateSession');
return $.ajax('/api/sessions/' + id, {
@ -270,6 +293,21 @@
});
}
function updateUser(options) {
var id = getId(options);
delete options['id'];
return $.ajax({
type: "POST",
dataType: "json",
contentType: 'application/json',
url: "/api/users/" + id,
data: JSON.stringify(options),
processData: false
});
}
function initialize() {
return self;
}
@ -297,6 +335,9 @@
this.userDownloadedClient = userDownloadedClient;
this.userCertifiedGear = userCertifiedGear;
this.userSocialPromoted = userSocialPromoted;
this.createJoinRequest = createJoinRequest;
this.updateJoinRequest = updateJoinRequest;
this.updateUser = updateUser;
return this;
};

View File

@ -28,7 +28,10 @@
var heartbeatAckCheckInterval = null;
var opts = {
layoutOpts: {}
inClient: true, // specify false if you want the app object but none of the client-oriented features
layoutOpts: {
layoutFooter: true // specify false if you want footer to be left alone
}
};
/**
@ -279,14 +282,17 @@
this.opts = $.extend(opts, inOpts);
this.layout = new context.JK.Layout();
this.layout.initialize(this.opts.layoutOpts);
registerLoginAck();
registerHeartbeatAck();
registerBadStateRecovered();
registerBadStateError();
registerSocketClosed();
events();
context.JK.FaderHelpers.initialize();
context.window.onunload = this.unloadFunction;
if(opts.inClient) {
registerLoginAck();
registerHeartbeatAck();
registerBadStateRecovered();
registerBadStateError();
registerSocketClosed();
context.JK.FaderHelpers.initialize();
context.window.onunload = this.unloadFunction;
}
};

View File

@ -1,106 +1,115 @@
/**
* hoverIntent is similar to jQuery's built-in "hover" function except that
* instead of firing the onMouseOver event immediately, hoverIntent checks
* to see if the user's mouse has slowed down (beneath the sensitivity
* threshold) before firing the onMouseOver event.
*
* hoverIntent r6 // 2011.02.26 // jQuery 1.5.1+
* <http://cherne.net/brian/resources/jquery.hoverIntent.html>
*
* hoverIntent is currently available for use in all personal or commercial
* projects under both MIT and GPL licenses. This means that you can choose
* the license that best suits your project, and use it accordingly.
*
* // basic usage (just like .hover) receives onMouseOver and onMouseOut functions
* $("ul li").hoverIntent( showNav , hideNav );
*
* // advanced usage receives configuration object only
* $("ul li").hoverIntent({
* sensitivity: 7, // number = sensitivity threshold (must be 1 or higher)
* interval: 100, // number = milliseconds of polling interval
* over: showNav, // function = onMouseOver callback (required)
* timeout: 0, // number = milliseconds delay before onMouseOut function call
* out: hideNav // function = onMouseOut callback (required)
* });
*
* @param f onMouseOver function || An object with configuration options
* @param g onMouseOut function || Nothing (use configuration options object)
* @author Brian Cherne brian(at)cherne(dot)net
*/
/*!
* hoverIntent r7 // 2013.03.11 // jQuery 1.9.1+
* http://cherne.net/brian/resources/jquery.hoverIntent.html
*
* You may use hoverIntent under the terms of the MIT license. Basically that
* means you are free to use hoverIntent as long as this header is left intact.
* Copyright 2007, 2013 Brian Cherne
*/
/* hoverIntent is similar to jQuery's built-in "hover" method except that
* instead of firing the handlerIn function immediately, hoverIntent checks
* to see if the user's mouse has slowed down (beneath the sensitivity
* threshold) before firing the event. The handlerOut function is only
* called after a matching handlerIn.
*
* // basic usage ... just like .hover()
* .hoverIntent( handlerIn, handlerOut )
* .hoverIntent( handlerInOut )
*
* // basic usage ... with event delegation!
* .hoverIntent( handlerIn, handlerOut, selector )
* .hoverIntent( handlerInOut, selector )
*
* // using a basic configuration object
* .hoverIntent( config )
*
* @param handlerIn function OR configuration object
* @param handlerOut function OR selector for delegation OR undefined
* @param selector selector OR undefined
* @author Brian Cherne <brian(at)cherne(dot)net>
*/
(function($) {
$.fn.hoverIntent = function(f,g) {
// default configuration options
var cfg = {
sensitivity: 7,
interval: 100,
timeout: 0
};
// override configuration options with user supplied object
cfg = $.extend(cfg, g ? { over: f, out: g } : f );
$.fn.hoverIntent = function(handlerIn,handlerOut,selector) {
// instantiate variables
// cX, cY = current X and Y position of mouse, updated by mousemove event
// pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
var cX, cY, pX, pY;
// default configuration values
var cfg = {
interval: 100,
sensitivity: 7,
timeout: 0
};
// A private function for getting mouse position
var track = function(ev) {
cX = ev.pageX;
cY = ev.pageY;
};
if ( typeof handlerIn === "object" ) {
cfg = $.extend(cfg, handlerIn );
} else if ($.isFunction(handlerOut)) {
cfg = $.extend(cfg, { over: handlerIn, out: handlerOut, selector: selector } );
} else {
cfg = $.extend(cfg, { over: handlerIn, out: handlerIn, selector: handlerOut } );
}
// A private function for comparing current and previous mouse position
var compare = function(ev,ob) {
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
// compare mouse positions to see if they've crossed the threshold
if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) {
$(ob).unbind("mousemove",track);
// set hoverIntent state to true (so mouseOut can be called)
ob.hoverIntent_s = 1;
return cfg.over.apply(ob,[ev]);
} else {
// set previous coordinates for next time
pX = cX; pY = cY;
// use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
}
};
// instantiate variables
// cX, cY = current X and Y position of mouse, updated by mousemove event
// pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
var cX, cY, pX, pY;
// A private function for delaying the mouseOut function
var delay = function(ev,ob) {
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
ob.hoverIntent_s = 0;
return cfg.out.apply(ob,[ev]);
};
// A private function for getting mouse position
var track = function(ev) {
cX = ev.pageX;
cY = ev.pageY;
};
// A private function for handling mouse 'hovering'
var handleHover = function(e) {
// copy objects to be passed into t (required for event object to be passed in IE)
var ev = jQuery.extend({},e);
var ob = this;
// A private function for comparing current and previous mouse position
var compare = function(ev,ob) {
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
// compare mouse positions to see if they've crossed the threshold
if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) {
$(ob).off("mousemove.hoverIntent",track);
// set hoverIntent state to true (so mouseOut can be called)
ob.hoverIntent_s = 1;
return cfg.over.apply(ob,[ev]);
} else {
// set previous coordinates for next time
pX = cX; pY = cY;
// use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
}
};
// cancel hoverIntent timer if it exists
if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
// A private function for delaying the mouseOut function
var delay = function(ev,ob) {
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
ob.hoverIntent_s = 0;
return cfg.out.apply(ob,[ev]);
};
// if e.type == "mouseenter"
if (e.type == "mouseenter") {
// set "previous" X and Y position based on initial entry point
pX = ev.pageX; pY = ev.pageY;
// update "current" X and Y position based on mousemove
$(ob).bind("mousemove",track);
// start polling interval (self-calling timeout) to compare mouse coordinates over time
if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
// A private function for handling mouse 'hovering'
var handleHover = function(e) {
// copy objects to be passed into t (required for event object to be passed in IE)
var ev = jQuery.extend({},e);
var ob = this;
// else e.type == "mouseleave"
} else {
// unbind expensive mousemove event
$(ob).unbind("mousemove",track);
// if hoverIntent state is true, then call the mouseOut function after the specified delay
if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
}
};
// cancel hoverIntent timer if it exists
if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
// bind the function to the two event listeners
return this.bind('mouseenter',handleHover).bind('mouseleave',handleHover);
};
// if e.type == "mouseenter"
if (e.type == "mouseenter") {
// set "previous" X and Y position based on initial entry point
pX = ev.pageX; pY = ev.pageY;
// update "current" X and Y position based on mousemove
$(ob).on("mousemove.hoverIntent",track);
// start polling interval (self-calling timeout) to compare mouse coordinates over time
if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
// else e.type == "mouseleave"
} else {
// unbind expensive mousemove event
$(ob).off("mousemove.hoverIntent",track);
// if hoverIntent state is true, then call the mouseOut function after the specified delay
if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
}
};
// listen for mouseenter and mouseleave
return this.on({'mouseenter.hoverIntent':handleHover,'mouseleave.hoverIntent':handleHover}, cfg.selector);
};
})(jQuery);

View File

@ -1,74 +0,0 @@
(function(context,$) {
"use strict";
context.JK = context.JK || {};
var downloads = {};
function listClients() {
var rest = context.JK.Rest();
var currentOS = context.JK.detectOS();
var downloads = $('.downloads');
rest.getClientDownloads()
.done(function(data) {
downloads.removeClass('spinner-large');
var count = 0;
for ( var property in data ) count++;
if(count == 0) {
alert("Currently unable to list client software downloads.");
}
else {
$.each(data, function(key, item) {
// if the currentOS we detect from browser is found within the product of an available client
// we flag it with this boolean
var matchesUserOS = currentOS != null && key.toLowerCase().indexOf(currentOS.toLowerCase()) > -1;
var platform = key.substring('JamClient/'.length);
var options = {
emphasis: matchesUserOS ? "currentOS" : "",
uri: item.uri,
platform: platform
}
var download = $(context._.template($('#client-download-link').html(), options, { variable: 'data' }));
download.find('a').data('platform', platform).click(function() {
var clicked = $(this);
rest.userDownloadedClient().always(function() {
$('body').append('<iframe class="downloading" src="' + clicked.attr('href') + '" style="display:none"/>')
})
context.JK.GA.trackDownload($(this).data('platform'));
return false;
});
if(matchesUserOS) {
// make sure the current user OS is at the top
downloads.prepend(download);
}
else {
downloads.append(download)
}
});
}
})
.fail(function() {
downloads.removeClass('spinner-large');
alert("Currently unable to list client software downloads due to error.");
})
}
downloads.listClients = listClients;
context.JK.Downloads = downloads;
})(window, jQuery)

View File

@ -7,6 +7,4 @@
//= require ga
//= require jam_rest
//= require landing/init
//= require landing/signup
//= require landing/downloads
//= require landing/congratulations
//= require landing/signup

View File

@ -56,6 +56,8 @@
var dialogBindings = {};
var wizardShowFunctions = {};
var openDialogs = []; // FIFO stack
function setup() {
requiredStyles();
hideAll();
@ -281,7 +283,10 @@
}
function layoutFooter(screenWidth, screenHeight) {
if(!opts.layoutFooter) { return; }
var $footer = $('#footer');
$footer.show();
var nHeight = $footer.height();
var footerStyle = {
top: (screenHeight - 80) + 'px'
@ -291,17 +296,7 @@
$footer.animate({ "left" : opts.gutter, "width" : width, "top": (screenHeight - 78) + "px"}, opts.animationDuration);
}
function requiredStyles() {
var bodyStyle = {
margin: '0px',
padding: '0px',
overflow: 'hidden'
};
if (opts.allowBodyOverflow) {
delete bodyStyle.overflow;
}
$('body').css(bodyStyle);
function requiredLayoutStyles() {
var layoutStyle = {
position: 'absolute',
margin: '0px',
@ -321,6 +316,21 @@
$('[layout-grid]').children().css({
position: "absolute"
});
}
function requiredStyles() {
var bodyStyle = {
margin: '0px',
padding: '0px',
overflow: 'hidden'
};
if (opts.allowBodyOverflow) {
delete bodyStyle.overflow;
}
$('body').css(bodyStyle);
requiredLayoutStyles();
var curtainStyle = {
position: "absolute",
margin: '0px',
@ -401,7 +411,8 @@
function closeDialog(dialog) {
var $dialog = $('[layout-id="' + dialog + '"]');
dialogEvent(dialog, 'beforeHide');
$('.dialog-overlay').hide();
var $overlay = $('.dialog-overlay');
unstackDialogs($overlay);
$dialog.hide();
dialogEvent(dialog, 'afterHide');
}
@ -456,11 +467,42 @@
}
}
/**
* Responsible for keeping N dialogs in correct stacked order,
* also moves the .dialog-overlay such that it hides/obscures all dialogs except the highest one
*/
function stackDialogs($dialog, $overlay) {
openDialogs.push($dialog);
var zIndex = 1000;
for(var i in openDialogs) {
var $dialog = openDialogs[i];
$dialog.css('zIndex', zIndex);
zIndex++;
}
$overlay.css('zIndex', zIndex - 1);
}
function unstackDialogs($overlay) {
if(openDialogs.length > 0) {
openDialogs.pop();
}
var zIndex = 1000 + openDialogs.length;
$overlay.css('zIndex', zIndex - 1);
if(openDialogs.length == 0) {
$overlay.hide();
}
}
function showDialog(dialog) {
dialogEvent(dialog, 'beforeShow');
$('.dialog-overlay').show();
var $overlay = $('.dialog-overlay')
$overlay.show();
centerDialog(dialog);
$('[layout-id="' + dialog + '"]').show();
var $dialog = $('[layout-id="' + dialog + '"]');
stackDialogs($dialog, $overlay);
$dialog.show();
dialogEvent(dialog, 'afterShow');
}
@ -540,7 +582,7 @@
this.notify = function(message, descriptor) {
var $notify = $('[layout="notify"]');
if (notifyQueue.length === 0) {
firstNotification = true;
setNotificationInfo(message, descriptor);
@ -621,6 +663,20 @@
$('#cancel-button', $notify).html("CANCEL");
}
}
if (descriptor.cancel_callback !== undefined) {
$('#cancel-button', $notify).click(function() {
if (descriptor.cancel_callback_args) {
logger.debug("descriptor.cancel_callback_args=" + descriptor.cancel_callback_args);
descriptor.cancel_callback(descriptor.cancel_callback_args);
return false;
}
else {
descriptor.cancel_callback();
return false;
}
});
}
}
else {
$('#ok-button', $notify).html("OKAY");

View File

@ -171,7 +171,6 @@
}
function sessionChanged() {
logger.debug("sessionChanged()");
// TODO - in the specific case of a user changing their tracks using the configureTrack dialog,
// this event appears to fire before the underlying mixers have updated. I have no event to
@ -212,7 +211,6 @@
}
function _initDialogs() {
logger.debug("Calling _initDialogs");
configureTrackDialog.initialize();
addTrackDialog.initialize();
addNewGearDialog.initialize();
@ -437,8 +435,6 @@
});
});
logger.debug("416-myTracks.length=" + myTracks.length);
configureTrackDialog = new context.JK.ConfigureTrackDialog(app, myTracks, sessionId, sessionModel);
addTrackDialog = new context.JK.AddTrackDialog(app, myTracks, sessionId, sessionModel);
addNewGearDialog = new context.JK.AddNewGearDialog(app, ftueCallback);
@ -637,6 +633,20 @@
mixerId = mixerId + "_vur";
}
_updateVU(mixerId, vuVal);
} else if (eventName === 'connection_status') {
// Connection Quality Change
var connectionClass = 'green';
if (value < 7) {
connectionClass = 'yellow';
}
if (value < 4) {
connectionClass = 'red';
}
var $connection = $('[mixer-id="' + mixerId + '_connection"]');
$connection.removeClass('green yellow red');
$connection.addClass(connectionClass);
} else if (eventName === 'add' || eventName === 'remove') {
//logger.dbg('non-vu event: ' + eventName + ',' + mixerId + ',' + value);
@ -649,7 +659,7 @@
} else {
// Examples of other events
// Add media file track: "add", "The_Abyss_4T", 0
logger.debug('non-vu event: ' + eventName + ',' + mixerId + ',' + value);
logger.dbg('non-vu event: ' + eventName + ',' + mixerId + ',' + value);
}
}
}
@ -673,7 +683,6 @@
function deleteTrack(evt) {
var trackId = $(evt.currentTarget).attr("track-id");
sessionModel.deleteTrack(sessionId, trackId);
logger.debug("643-myTracks.length=" + myTracks.length);
}
function _toggleVisualMuteControl($control, muting) {
@ -818,6 +827,7 @@
app.bindScreen('session', screenBindings);
};
this.tracks = tracks;
this.getCurrentSession = function() {

View File

@ -5,7 +5,8 @@
context.JK = context.JK || {};
context.JK.SessionList = function(app) {
var logger = context.JK.logger;
var termsDialog;
var rest = context.JK.Rest();
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},
@ -135,14 +136,43 @@
// wire up the Join Link to the T&Cs dialog
var $parentRow = $('tr[id=' + session.id + ']', tbGroup);
$('#join-link', $parentRow).click(function(evt) {
openTerms(session.id);
});
if (session.approval_required) {
$('#join-link', $parentRow).click(function(evt) {
openAlert(session.id);
});
}
else {
$('#join-link', $parentRow).click(function(evt) {
openTerms(session.id);
});
}
}
}
function openAlert(sessionId) {
var alertDialog = new context.JK.AlertDialog(app, "YES",
"You must be approved to join this session. Would you like to send a request to join?",
sessionId, onCreateJoinRequest);
alertDialog.initialize();
app.layout.showDialog('alert');
}
function onCreateJoinRequest(sessionId) {
var joinRequest = {};
joinRequest.music_session = sessionId;
joinRequest.user = context.JK.currentUserId;
rest.createJoinRequest(joinRequest)
.done(function(response) {
}).error(app.ajaxError);
app.layout.closeDialog('alert');
}
function openTerms(sessionId) {
termsDialog = new context.JK.TermsDialog(app, sessionId, onTermsAccepted);
var termsDialog = new context.JK.TermsDialog(app, sessionId, onTermsAccepted);
termsDialog.initialize();
app.layout.showDialog('terms');
}

View File

@ -6,8 +6,8 @@
context.JK.Sidebar = function(app) {
var logger = context.JK.logger;
var friends = [];
var invitationDialog = new context.JK.InvitationDialog(app);
var rest = context.JK.Rest();
var invitationDialog = null;
function initializeFriendsPanel() {
@ -56,7 +56,7 @@
}
function updateFriendList(response) {
$('#sidebar-friend-list li:not(.invite-friend-row').remove();
$('#sidebar-friend-list li:not(.invite-friend-row)').remove();
// show online friends first (sort by first name within online/offline groups)
response.sort(function(a, b) {
@ -147,6 +147,8 @@
// wire up "x" button to delete notification
$notification.find('#img-delete-notification').click(deleteNotificationHandler);
// customize action buttons based on notification type
if (type === context.JK.MessageType.FRIEND_REQUEST) {
var $action_btn = $notification.find('#btn-notification-action');
$action_btn.text('ACCEPT');
@ -161,9 +163,26 @@
var $action_btn = $notification.find('#btn-notification-action');
$action_btn.text('JOIN');
$action_btn.click(function() {
joinSession({ "session_id": payload.session_id, "notification_id": payload.notification_id });
openTerms({ "session_id": payload.session_id, "notification_id": payload.notification_id });
});
}
else if (type === context.JK.MessageType.JOIN_REQUEST) {
var $action_btn = $notification.find('#btn-notification-action');
$action_btn.text('APPROVE');
$action_btn.click(function() {
approveJoinRequest({ "join_request_id": payload.join_request_id, "notification_id": payload.notification_id });
});
}
else if (type === context.JK.MessageType.JOIN_REQUEST_APPROVED) {
var $action_btn = $notification.find('#btn-notification-action');
$action_btn.text('JOIN');
$action_btn.click(function() {
openTerms({ "session_id": payload.session_id, "notification_id": payload.notification_id });
});
}
else if (type === context.JK.MessageType.JOIN_REQUEST_REJECTED) {
$notification.find('#div-actions').hide();
}
}
function deleteNotificationHandler(evt) {
@ -314,22 +333,6 @@
}
}
function acceptFriendRequest(args) {
rest.acceptFriendRequest({
status : 'accept',
friend_request_id : args.friend_request_id
}).done(function(response) {
deleteNotification(args.notification_id); // delete notification corresponding to this friend request
initializeFriendsPanel(); // refresh friends panel when request is accepted
}).error(app.ajaxError);
}
function joinSession(args) {
context.location = "#/session/" + args.session_id;
deleteNotification(args.notification_id);
}
// default handler for incoming notification
function handleNotification(payload, type) {
var sidebarText;
@ -406,11 +409,30 @@
$('.sidebar .invite-friend-row').hoverIntent(inviteHoverIn, inviteHoverOut);
// $('#sidebar-div').mouseleave(function(evt) {
// hideSearchResults();
// });
registerFriendUpdate();
registerFriendRequest();
registerFriendRequestAccepted();
registerMusicianSessionJoin();
registerMusicianSessionDepart();
registerFriendSessionJoin();
registerSessionInvitation();
registerJoinRequest();
registerJoinRequestApproved();
registerJoinRequestRejected();
// wire up FRIEND_UPDATE handler
// watch for Invite More Users events
$('#sidebar-div .btn-email-invitation').click(function() {
invitationDialog.showEmailDialog();
return false;
});
$('#sidebar-div .btn-gmail-invitation').click(function() {
invitationDialog.showGoogleDialog();
return false;
});
}
function registerFriendUpdate() {
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.FRIEND_UPDATE, function(header, payload) {
logger.debug("Handling FRIEND_UPDATE msg " + JSON.stringify(payload));
@ -426,9 +448,9 @@
"icon_url": context.JK.resolveAvatarUrl(payload.photo_url)
});
});
//////////////////////////////////////////////////////////////
}
// wire up FRIEND_REQUEST handler
function registerFriendRequest() {
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.FRIEND_REQUEST, function(header, payload) {
logger.debug("Handling FRIEND_REQUEST msg " + JSON.stringify(payload));
@ -447,9 +469,20 @@
}
);
});
//////////////////////////////////////////////////////////////
}
// wire up FRIEND_REQUEST_ACCEPTED handler
function acceptFriendRequest(args) {
rest.acceptFriendRequest({
status : 'accept',
friend_request_id : args.friend_request_id
}).done(function(response) {
deleteNotification(args.notification_id); // delete notification corresponding to this friend request
initializeFriendsPanel(); // refresh friends panel when request is accepted
}).error(app.ajaxError);
}
function registerFriendRequestAccepted() {
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.FRIEND_REQUEST_ACCEPTED, function(header, payload) {
logger.debug("Handling FRIEND_REQUEST_ACCEPTED msg " + JSON.stringify(payload));
@ -465,9 +498,9 @@
"icon_url": context.JK.resolveAvatarUrl(payload.photo_url)
});
});
//////////////////////////////////////////////////////////////
}
// wire up MUSICIAN_SESSION_JOIN handler
function registerMusicianSessionJoin() {
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_JOIN, function(header, payload) {
logger.debug("Handling MUSICIAN_SESSION_JOIN msg " + JSON.stringify(payload));
@ -478,9 +511,9 @@
"icon_url": context.JK.resolveAvatarUrl(payload.photo_url)
});
});
//////////////////////////////////////////////////////////////
}
// wire up MUSICIAN_SESSION_DEPART handler
function registerMusicianSessionDepart() {
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_DEPART, function(header, payload) {
logger.debug("Handling MUSICIAN_SESSION_DEPART msg " + JSON.stringify(payload));
@ -491,9 +524,9 @@
"icon_url": context.JK.resolveAvatarUrl(payload.photo_url)
});
});
//////////////////////////////////////////////////////////////
}
// wire up MUSICIAN_SESSION_DEPART handler
function registerFriendSessionJoin() {
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.FRIEND_SESSION_JOIN, function(header, payload) {
logger.debug("Handling FRIEND_SESSION_JOIN msg " + JSON.stringify(payload));
@ -504,9 +537,9 @@
"icon_url": context.JK.resolveAvatarUrl(payload.photo_url)
});
});
//////////////////////////////////////////////////////////////
}
// wire up SESSION_INVITATION handler
function registerSessionInvitation() {
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SESSION_INVITATION, function(header, payload) {
logger.debug("Handling SESSION_INVITATION msg " + JSON.stringify(payload));
@ -538,28 +571,104 @@
},
{
"ok_text": "JOIN SESSION",
"ok_callback": joinSession,
"ok_callback": openTerms,
"ok_callback_args": { "session_id": payload.session_id, "notification_id": payload.notification_id }
}
);
});
//////////////////////////////////////////////////////////////
}
// watch for Invite More Users events
$('#sidebar-div .btn-email-invitation').click(function() {
invitationDialog.showEmailDialog();
});
function openTerms(args) {
var termsDialog = new context.JK.TermsDialog(app, args, onTermsAccepted);
termsDialog.initialize();
app.layout.showDialog('terms');
}
$('#sidebar-div .btn-gmail-invitation').click(function() {
invitationDialog.showGoogleDialog();
function onTermsAccepted(args) {
deleteNotification(args.notification_id);
context.location = '#/session/' + args.session_id;
}
function registerJoinRequest() {
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.JOIN_REQUEST, function(header, payload) {
logger.debug("Handling JOIN_REQUEST msg " + JSON.stringify(payload));
handleNotification(payload, header.type);
// display notification
app.notify({
"title": "New Join Request",
"text": payload.msg,
"icon_url": context.JK.resolveAvatarUrl(payload.photo_url)
},
{
"ok_text": "APPROVE",
"ok_callback": approveJoinRequest,
"ok_callback_args": { "join_request_id": payload.join_request_id, "notification_id": payload.notification_id },
"cancel_text": "REJECT",
"cancel_callback": rejectJoinRequest,
"cancel_callback_args": { "join_request_id": payload.join_request_id, "notification_id": payload.notification_id }
}
);
});
}
this.initialize = function() {
function approveJoinRequest(args) {
rest.updateJoinRequest(args.join_request_id, true)
.done(function(response) {
deleteNotification(args.notification_id);
}).error(app.ajaxError);
}
function rejectJoinRequest(args) {
rest.updateJoinRequest(args.join_request_id, false)
.done(function(response) {
deleteNotification(args.notification_id);
}).error(app.ajaxError);
}
function registerJoinRequestApproved() {
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.JOIN_REQUEST_APPROVED, function(header, payload) {
logger.debug("Handling JOIN_REQUEST_APPROVED msg " + JSON.stringify(payload));
handleNotification(payload, header.type);
// display notification
app.notify({
"title": "Join Request Approved",
"text": payload.msg,
"icon_url": context.JK.resolveAvatarUrl(payload.photo_url)
},
{
"ok_text": "JOIN SESSION",
"ok_callback": openTerms,
"ok_callback_args": { "session_id": payload.session_id, "notification_id": payload.notification_id }
}
);
});
}
function registerJoinRequestRejected() {
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.JOIN_REQUEST_REJECTED, function(header, payload) {
logger.debug("Handling JOIN_REQUEST_REJECTED msg " + JSON.stringify(payload));
handleNotification(payload, header.type);
// display notification
app.notify({
"title": "Join Request Rejected",
"text": payload.msg,
"icon_url": context.JK.resolveAvatarUrl(payload.photo_url)
});
});
}
this.initialize = function(invitationDialogInstance) {
events();
initializeFriendsPanel();
initializeChatPanel();
initializeNotificationsPanel();
invitationDialog = invitationDialogInstance;
};
};
})(window,jQuery);

View File

@ -3,13 +3,13 @@
"use strict";
context.JK = context.JK || {};
context.JK.TermsDialog = function(app, sessionId, callback) {
context.JK.TermsDialog = function(app, args, callback) {
var logger = context.JK.logger;
function events() {
$('#btn-accept-terms').click(function(evt) {
callback(sessionId);
callback(args);
});
$('#btn-cancel-terms').click(function(evt) {

View File

@ -0,0 +1,94 @@
(function(context,$) {
/** Javascript for managing the (account dropdown) */
"use strict";
context.JK = context.JK || {};
context.JK.UserDropdown = function(app) {
var logger = context.JK.logger;
var rest = new JK.Rest();
var userMe = null;
var invitationDialog = null;
function menuHoverIn() {
$('ul.shortcuts', this).show();
}
function menuHoverOut() {
$('ul.shortcuts', this).hide();
}
function events() {
$('.userinfo').hoverIntent(menuHoverIn, menuHoverOut);
$('.userinfo .invite-friends .menuheader').on('click', function(e) {
$(this).closest('li').css('height', 'auto').find('ul').toggle();
e.stopPropagation();
return false;
});
$('.invite-friends .google-invite a').on('click', function(e) {
invitationDialog.showGoogleDialog();
});
$('.invite-friends .email-invite a').on('click', function(e) {
invitationDialog.showEmailDialog();
});
$('#header-avatar').on('avatar_changed', function(event, newAvatarUrl) {
updateAvatar(newAvatarUrl);
event.preventDefault();
return false;
})
}
function loadMe() {
$.ajax({
url: '/api/users/' + context.JK.currentUserId
}).done(function(r) {
userMe = r;
// TODO - Setting global variable for local user.
context.JK.userMe = r;
updateHeader();
handleWhatsNext(userMe);
}).fail(app.ajaxError);
}
function updateHeader() {
$('#user').html(userMe.name);
showAvatar();
}
function handleWhatsNext(userProfile) {
if(gon.isNativeClient && userProfile.show_whats_next) {
app.layout.showDialog('whatsNext');
}
}
// initially show avatar
function showAvatar() {
var photoUrl = context.JK.resolveAvatarUrl(userMe.photo_url);
$('#header-avatar').attr('src', photoUrl);
}
// handle update avatar event
function updateAvatar(avatar_url) {
var photoUrl = context.JK.resolveAvatarUrl(avatar_url);
var avatar = $(new Image());
avatar.attr('src', photoUrl + '?cache_bust=' + new Date().getTime());
avatar.attr('alt', "Avatar");
avatar.attr('id', 'header-avatar');
$('#header-avatar').replaceWith(avatar);
}
this.initialize = function(invitationDialogInstance) {
events();
invitationDialog = invitationDialogInstance;
loadMe();
}
this.loadMe = loadMe;
}
})(window,jQuery);

View File

@ -297,7 +297,12 @@
context.JK.popExternalLinks = function() {
// Allow any a link with a rel="external" attribute to launch
// the link in the default browser, using jamClient:
$('a[rel="external"]').click(function(evt) {
$('body').on('click', 'a[rel="external"]', function(evt) {
if(!context.jamClient) {
return;
}
evt.preventDefault();
var href = $(this).attr("href");
if (href) {

View File

@ -6,7 +6,7 @@
congratulations.initialize = function initialize(musician, registrationType) {
if(musician) {
context.JK.Downloads.listClients();
context.JK.Downloads.listClients(true);
}
if(registrationType) {

View File

@ -0,0 +1,138 @@
(function(context,$) {
"use strict";
context.JK = context.JK || {};
var downloads = {};
var isCongratulations;
var downloadUris = {}; // map of platform > uri
var rest = context.JK.Rest();
function selectPlatform(selectedPlatform) {
var platformName; // mac, windows, linux
var platformDisplay; // Mac, Windows, Linux
var platform = selectedPlatform; //MacOSX, Win32, Unix
var platformName1, platformName2, platform1, platform2;
var uri = downloadUris[selectedPlatform];
// prepare template varaibles
if (selectedPlatform == "Unix") {
platformName = "linux";
platformDisplay = "Linux"
platformName1 = "mac";
platformName2 = "windows";
platform1 = "MacOSX";
platform2 = "Win32"
} else if(selectedPlatform == "Win32") {
platformName = "windows";
platformDisplay = "Windows";
platformName1 = "mac";
platformName2 = "linux"
platform1 = "MacOSX";
platform2 = "Unix";
} else if(selectedPlatform == "MacOSX") {
platformName = "mac";
platformDisplay = "Mac";
platformName1 = "windows";
platformName2 = "linux";
platform1 = "Win32";
platform2 = "Unix";
}
else {
alert("unknown platform: " + selectedPlatform);
}
var options = {
platform : platform,
platformName : platformName,
platformDisplay : platformDisplay,
platformName1 : platformName1,
platformName2 : platformName2,
platform1: platform1,
platform2: platform2,
uri : uri ? uri : '#',
isCongratulations : isCongratulations
};
var blurb = $(context._.template($('#client-download-blurb-contents').html(), options, { variable: 'data' }));
// isolate active image for blurb
$('div.hidden-images img[data-purpose=' + platformName + ']', blurb).remove().appendTo($('a.current-os-download', blurb));
var selectOthers = $(context._.template($('#client-download-select-others').html(), options, { variable: 'data' }));
// isolate active images for selectOthers
$('div.hidden-images img[data-purpose=' + platformName1 + ']', selectOthers).remove().appendTo($('a[data-order=1]', selectOthers));
$('div.hidden-images img[data-purpose=' + platformName2 + ']', selectOthers).remove().appendTo($('a[data-order=2]', selectOthers));
// install click handler for change selection
$('a', selectOthers).click(function() {
var platform = $(this).attr('data-platform');
selectPlatform(platform);
return false;
});
$('a', blurb).click(function() {
var clicked = $(this);
var href = clicked.attr('href');
if(href != "#") {
rest.userDownloadedClient().always(function() {
$('body').append('<iframe class="downloading" src="' + clicked.attr('href') + '" style="display:none"/>')
});
}
else {
context.JK.GA.trackDownload(clicked.attr('data-platform'));
// if there is no download available, apologize to the user
alert("Sorry, this download is not currently available.");
return false;
}
return false;
});
// update blurb
$('body.web .downloads-blurb').empty().append(blurb);
// update the 'download other platforms' buttons
$('body.web .downloads-container').empty().append(selectOthers);
// update system requirements
$('body.web .system-requirements ul').hide();
$('body.web .system-requirements ul.' + platformName + '-requirements').show();
$('body.web .system-requirements').show();
}
function removeSpinner() {
$('body.web .spinner-large').remove();
}
function listClients(congratulations) {
isCongratulations = congratulations;
var rest = context.JK.Rest();
var currentOS = context.JK.detectOS();
var downloads = $('.downloads');
rest.getClientDownloads()
.done(function(data) {
removeSpinner();
$.each(data, function(key, item) {
var platform = key.substring('JamClient/'.length);
downloadUris[platform] = item.uri;
});
})
.fail(function() {
removeSpinner();
alert("Currently unable to list client software downloads due to error.");
})
.always(function() {
selectPlatform(currentOS == null ? 'Win32' : currentOS);
});
}
downloads.listClients = listClients;
context.JK.Downloads = downloads;
})(window, jQuery)

View File

@ -0,0 +1,18 @@
//= require jquery
//= require jquery_ujs
//= require jquery.queryparams
//= require jquery.hoverIntent
//= require AAA_Log
//= require AAC_underscore
//= require globals
//= require invitationDialog
//= require layout
//= require user_dropdown
//= require jamkazam
//= require utils
//= require ga
//= require jam_rest
//= require landing/init
//= require landing/signup
//= require web/downloads
//= require web/congratulations

View File

@ -0,0 +1,55 @@
(function(context,$) {
"use strict";
context.JK = context.JK || {};
context.JK.WhatsNextDialog = function(app) {
var logger = context.JK.logger;
var rest = context.JK.Rest();
var invitationDialog = null;
function registerEvents() {
$('#whatsnext-dialog a.facebook-invite').on('click', function(e) {
alert("This feature not yet implemented");
});
$('#whatsnext-dialog a.google-invite').on('click', function(e) {
invitationDialog.showGoogleDialog();
});
$('#whatsnext-dialog a.email-invite').on('click', function(e) {
invitationDialog.showEmailDialog();
});
}
function beforeShow() {
}
function beforeHide() {
var $dontShowWhatsNext = $('#show_whats_next');
if($dontShowWhatsNext.is(':checked')) {
rest.updateUser( {show_whats_next:false})
}
}
function initialize(invitationDialogInstance) {
var dialogBindings = {
'beforeShow' : beforeShow,
'beforeHide': beforeHide
};
app.bindDialog('whatsNext', dialogBindings);
registerEvents();
invitationDialog = invitationDialogInstance;
};
this.initialize = initialize;
}
return this;
})(window,jQuery);

View File

@ -41,12 +41,14 @@
min-width: 165px;
width: 20%;
}
.account-left h2 {
color: #FFFFFF;
font-size: 23px;
font-weight: 400;
margin-bottom: 8px;
}
.account-mid {
float: left;
line-height: 150%;
@ -54,6 +56,29 @@
width: 50%;
}
.account-profile {
padding-top:25px;
h2 {
margin-bottom:15px;
}
.location {
position:relative;
}
#account-change-avatar {
position:absolute;
top:-27px;
right:72px;
height:13px;
}
.birth_date {
margin-left:40px;
}
}
.audio .audio-profiles-short{
white-space: normal;
}

View File

@ -14,6 +14,7 @@
*= require ./content
*= require ./faders
*= require ./header
#= require ./user_dropdown
*= require ./footer
*= require ./screen_common
*= require ./notify
@ -34,4 +35,5 @@
*= require ./banner
*= require ./clientUpdate
*= require jquery.Jcrop
*= require icheck/minimal/minimal
*/

View File

@ -338,8 +338,15 @@ ul.shortcuts {
}
}
}
.tagline {
font-size:30px;
margin-top:35px;
color:#ed3718;
font-weight:300;
width:345px;
clear:left;
white-space:normal;
}

View File

@ -29,6 +29,7 @@
.radio-text {
font-size:13px;
line-height:17px;
}
.friendbox {
@ -111,4 +112,20 @@ div.friendbox input[type=text] {
padding:10px;
border-bottom:solid 1px #999;
cursor:pointer;
}
}
div[layout-id="createSession"] .icheckbuttons {
margin-top:5px;
div.iradio_minimal {
float:left;
}
label {
float:left;
margin:0 10px 0 3px;
}
}

View File

@ -9,6 +9,7 @@
padding-top: 10px;
border-top:solid 1px #444;
height:13px;
display:none;
}
#copyright {

View File

@ -299,6 +299,50 @@ div[layout-id="ftue3"] {
border: 1px solid #ed3618;
}
.ftue-overlay.tall {
top:75px;
}
.ftue-overlay.tall .ftue-inner {
padding:5px;
}
#whatsnext-dialog {
height:auto;
.ftue-inner h2 {
font-weight:normal;
color:#ed3618;
margin-bottom:6px;
font-size:1.7em;
}
.ftue-inner table td.whatsnext {
font-size:12px;
padding:10px;
width:50%;
font-weight:300;
}
.ftue-inner table td.whatsnext {
font-size:12px;
padding:10px;
width:50%;
font-weight:300;
}
.ftue-inner table a {
text-decoration:none;
}
.ftue-inner table {
border-collapse:separate;
border-spacing: 20px;
}
}
.ftue-inner {
width:750px;
padding:25px;

View File

@ -1,6 +1,5 @@
@charset "UTF-8";
@import "compass/utilities/text/replacement";
@import "client/common.css.scss";
.header {
height: 55px;
@ -15,123 +14,3 @@ div[layout="header"] h1 {
@include replace-text(image-url("header/logo.png"));
float:left;
}
#logo {
float:left;
width:247px;
}
#profile {
width:auto;
float:right;
height:54px;
}
.avatar_large {
float:left;
padding:2px;
width:54px;
height:54px;
background-color: $ColorScreenPrimary;
-webkit-border-radius:28px;
-moz-border-radius:28px;
border-radius:28px;
}
.avatar_large img {
width:54px;
height:54px;
-webkit-border-radius:26px;
-moz-border-radius:26px;
border-radius:26px;
}
#user {
margin:18px 0px 0px 10px;
font-size:20px;
font-weight:200;
color:#ccc;
float:left;
}
.arrow-down {
float:left;
cursor:pointer;
margin-left:16px;
margin-top:26px;
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid #fff;
}
.userinfo {
cursor:pointer;
}
.userinfo ul {
clear:both;
background: scale-lightness($ColorUIBackground, 10%);
display:none;
}
.userinfo li {
display:block;
margin: 2px;
padding: 2px;
background: scale-lightness($ColorUIBackground, 20%);
}
/*
div[layout="header"] h1 {
width: 252px;
height:47px;
@include replace-text(image-url("logo.png"));
}
*/
/*.header h1 {*/
/*margin:22px;*/
/*font-size:300%;*/
/*font-family: 'LatoLight', Arial, sans-serif;*/
/*}*/
/*
.userinfo {
position:absolute;
right:0px;
top:0px;
width: 266px;
z-index:5;
}
.userinfo img.avatar {
float:left;
}
.userinfo .username {
float:left;
margin-top: 18px;
cursor: pointer;
}
.userinfo h2 {
font-size:120%;
font-weight: bold;
float:left;
margin-right:4px;
}
.userinfo .profile-toggle {
display:inline-block;
}
.userinfo ul {
clear:both;
background: $color7;
display:none;
}
.userinfo li {
display:block;
margin: 2px;
padding: 2px;
background: scale-lightness($color7, 10%);
}
*/

View File

@ -131,6 +131,10 @@ input[type="button"] {
font-size:11px;
}
.orange {
color: $ColorScreenPrimary !important;
}
.curtain {
background-color: $ColorScreenBackground;
position:absolute;
@ -433,6 +437,9 @@ input[type="text"], input[type="password"]{
width:100%;
}
.f20 {
font-size: 20px !important;
}
/* TODO - we need a separate stylesheet(s) for signin/signup screens */
/* Following is a style adjustment for the sign-in table spacing */
#sign-in td { padding: 4px; }

View File

@ -18,6 +18,15 @@
}
.audio-devices {
font-family:"Raleway", arial, sans-serif;
border:none;
-webkit-box-shadow: inset 2px 2px 3px 0px #888;
box-shadow: inset 2px 2px 3px 0px #888;
color:#666;
}
.track {
width:70px;
height:290px;
@ -440,7 +449,7 @@ table.vu td {
left:28px;
}
.track-connection-green {
.track-connection {
position:absolute;
bottom:0px;
left:0px;
@ -450,50 +459,22 @@ table.vu td {
font-weight:bold;
font-size:8px;
text-align:center;
overflow:hidden;
}
.track-connection.green {
background-color:#72a43b;
overflow:hidden;
}
.track-connection-yellow {
position:absolute;
bottom:0px;
left:0px;
width: 70px;
height: 8px;
font-family:Arial, Helvetica, sans-serif;
font-weight:bold;
font-size:8px;
text-align:center;
.track-connection.yellow {
background-color:#cc9900;
overflow:hidden;
}
.track-connection-red {
position:absolute;
bottom:0px;
left:0px;
width: 70px;
height: 8px;
font-family:Arial, Helvetica, sans-serif;
font-weight:bold;
font-size:8px;
text-align:center;
.track-connection.red {
background-color:#980006;
overflow:hidden;
}
.track-connection-grey {
position:absolute;
bottom:0px;
left:0px;
width: 70px;
height: 8px;
font-family:Arial, Helvetica, sans-serif;
font-weight:bold;
font-size:8px;
text-align:center;
.track-connection.grey {
background-color:#666;
overflow:hidden;
}

View File

@ -0,0 +1,75 @@
@import "client/common.css.scss";
#logo {
float:left;
width:247px;
}
#profile {
width:auto;
float:right;
height:64px;
.signin {
position:relative;
margin-top:40px;
}
}
.avatar_large {
float:left;
padding:2px;
width:54px;
height:54px;
background-color: $ColorScreenPrimary;
-webkit-border-radius:28px;
-moz-border-radius:28px;
border-radius:28px;
}
.avatar_large img {
width:54px;
height:54px;
-webkit-border-radius:26px;
-moz-border-radius:26px;
border-radius:26px;
}
#user {
margin:18px 0px 0px 10px;
font-size:20px;
font-weight:200;
color:#ccc;
float:left;
}
.arrow-down {
float:left;
cursor:pointer;
margin-left:16px;
margin-top:26px;
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid #fff;
}
.userinfo {
cursor:pointer;
}
.userinfo ul {
clear:both;
background: scale-lightness($ColorUIBackground, 10%);
display:none;
position:relative;
}
.userinfo li {
display:block;
margin: 2px;
padding: 2px;
background: scale-lightness($ColorUIBackground, 20%);
}

View File

@ -175,76 +175,3 @@ strong {
display:block;
}
}
// all custom CSS for the register page goes here
.register-page {
.register-container {
padding:10px;
}
input.register-musician {
}
.error {
padding: 5px 12px 5px 5px;
margin-left:-5px;
margin-right:-12px;
}
input.register-fan {
margin-left:20px;
}
input[type=text], input[type=password] {
margin-top:1px;
width:100%;
}
select {
width:100%;
}
.right-side {
margin-left:25px;
}
.ftue-left {
margin-bottom:30px;
select {
width:104%;
}
div.field {
margin-top:31px;
width:43%;
float:left;
}
}
.ftue-right {
table {
border-collapse:separate;
border-spacing:6px;
}
label.instruments {
margin-bottom:2px;
}
div.field {
margin-top:15px;
}
a.tos {
text-decoration: underline;
}
input[type=submit] {
margin-top:20px;
}
}
}

View File

@ -0,0 +1,41 @@
@charset "UTF-8";
#footer-container {
position:relative;
margin:0 6%;
top:100px;
height:13px;
}
#footer {
padding-top: 10px;
border-top:solid 1px #444;
}
#copyright {
float:left;
font-size:11px;
color:#ccc;
}
#footer-links {
float:right;
font-size:11px;
color:#ccc;
}
#footer-links a {
color:#ccc;
text-decoration:none;
}
#footer-links a:hover {
color:#fff;
text-decoration:underline;
}
#version {
font-size:11px;
color:#ccc;
text-align: center;
}

View File

@ -0,0 +1,366 @@
html {
height:100%;
}
body.web {
background-repeat: repeat-x;
margin:0 !important;
padding:0 !important;
position:relative !important;
overflow: visible !important;
width:auto !important;
#web-container {
padding:3% 0;
}
div.wrapper {
white-space: nowrap;
p, ul {
color:#999;
line-height:160%;
margin-bottom:20px;
width:90%;
white-space:normal;
font-size:16px;
}
h2 {
font-weight:300;
}
.content-wrapper {
border-bottom: medium none;
padding: 0;
}
.black-bar{
position:relative;
width:100%;
min-height: 366px;
background-color:black;
padding-top:20px;
}
.black-bar-inner {
width:1100px;
margin: 0 auto;
position:relative;
// all custom CSS for the register page goes here
.register-page {
.register-container {
padding:10px;
}
input.register-musician {
}
.actions {
margin-top:20px;
a.button-grey {
line-height:15px; // WHY is this not universal
}
}
.error {
padding: 5px 12px 5px 5px;
margin-left:-5px;
margin-right:-12px;
}
input.register-fan {
margin-left:20px;
}
input[type=text], input[type=password] {
margin-top:1px;
width:100%;
}
select {
width:100%;
}
.right-side {
margin-left:25px;
}
.register-left {
select {
width:104%;
}
div.field {
margin-top:31px;
width:43%;
float:left;
}
}
.register-right {
margin-top:40px;
table {
border-collapse:separate;
border-spacing:6px;
}
label.instruments {
margin-bottom:2px;
}
div.field {
margin-top:15px;
}
a.tos {
text-decoration: underline;
}
.ftue-instrumentlist {
width:100%;
}
}
}
}
.after-black-bar {
position:relative;
background-color:#262626;
width:1100px;
margin:0 auto;
.after-black-bar-inner {
background-color:#262626;
position:absolute;
left:0;
right:0;
}
}
}
.header {
width:1100px;
margin:0 auto;
.logo-home {
width: 298px;
margin-top: 30px;
display: inline-block;
float:left;
}
}
#profile {
position:absolute;
top:-80px;
right:0;
ul {
margin-bottom:0;
}
}
ul {
list-style: none outside none;
}
h3 {
font-size:20px;
font-weight:normal;
}
.download-box {
padding:15px;
color:#fff;
text-align:center;
font-size:20px;
-webkit-border-radius: 8px;
border-radius: 8px;
background:#383838;
margin-top:100px;
}
.system-requirements {
margin-top:35px;
display:none;
ul {
display:none;
list-style:disc outside none;
padding-left:40px;
}
}
div.proceed {
margin:40px 0 250px 5px;
}
.downloads {
ul {
list-style:disc outside none;
padding-left:40px;
}
}
}
#landing-container {
padding: 3% 0;
position:relative;
text-align: center;
}
.signin-overlay {
position:relative;
top:0;
}
strong {
font-weight: 600;
}
.logo-message {
display: block;
margin: 0 auto;
width: 247px;
}
.message-wrapper {
margin: 0 auto;
width: 480px;
}
.message-wrapper .left {
display: block;
overflow: visible;
}
.message {
display: block;
float: left;
margin-left: 20px;
overflow: visible;
width: 320px;
}
.message h2 {
border-bottom: 1px solid #FFFFFF;
color: #FFFFFF;
display: block;
font-weight: 200;
margin-bottom: 10px;
font-size:21px;
}
.spinner-large {
vertical-align:middle;
text-align: center;
margin: 125px auto;
}
.client-download {
margin-bottom:20px;
}
.currentOS {
span.platform {
font-size:18px;
}
}
// all custom CSS for the sign-in page goes here
.signin-page {
.ftue-inner {
line-height:18px;
}
.ftue-left, .ftue-right {
}
fieldset[name=text-input]{
float:right;
margin-right:18px;
}
fieldset[name=signin-options] {
float:left;
margin:10px 0 0 10px;
small {
float:left;
}
}
fieldset[name=actions] {
float:right;
margin: 10px 19px 0 0;
}
.field {
right:0;
}
.email {
float:left;
margin-right:10px;
}
.password {
float:left;
}
label {
margin:27px 0 10px;
}
.already-member {
}
.keep-logged-in {
}
.forgot-password {
font-size:11px;
float:right;
margin:15px 19px 0 0;
a {
text-decoration: underline;
}
}
.login-error {
background-color: #330000;
border: 1px solid #990000;
padding:4px;
}
.login-error-msg {
display:none;
margin-top:10px;
text-align:center;
color:#F00;
font-size:11px;
}
fieldset.login-error .login-error-msg {
display:block;
}
}

View File

@ -0,0 +1,12 @@
/**
*= require client/ie
*= require client/jamkazam
*= require client/screen_common
*= require client/content
*= require client/ftue
*= require client/user_dropdown
*= require client/dialog
*= require client/invitationDialog
*= require web/main
*= require web/footer
*/

View File

@ -30,11 +30,29 @@ class ApiJoinRequestsController < ApiController
respond_with @join_request
else
# send notification
Notification.send_join_request(music_session, @join_request, sender, text)
Notification.send_join_request(music_session, @join_request, text)
respond_with @join_request, :responder => ApiResponder, :location => api_join_request_detail_url(@join_request)
end
end
def update
@join_request = JoinRequest.find(params[:id])
if params[:approved]
# generate invitation if join request is approved
invitation = Invitation.new
invitation.sender = current_user
invitation.receiver = @join_request.user
invitation.music_session = @join_request.music_session
invitation.join_request = @join_request
invitation.save
Notification.send_join_request_approved(@join_request.music_session, @join_request)
else
Notification.send_join_request_rejected(@join_request.music_session, @join_request)
end
end
def delete
@join_request = JoinRequest.show(params[:id], current_user)
@join_request.delete

View File

@ -200,7 +200,7 @@ class ApiMusicSessionsController < ApiController
bucket = s3.buckets[SampleApp::Application.config.aws_bucket]
read_url = bucket.objects[uri].url_for(:read,
:expires => Time.now + 90.days,
:expires => Time.now + 25.years,
:'response_content_type' => 'text/csv').to_s
@perfdata.update_attribute(:uri, read_url)

View File

@ -65,6 +65,8 @@ class ApiUsersController < ApiController
@user.country = params[:country] if params.has_key?(:country)
@user.musician = params[:musician] if params.has_key?(:musician)
@user.update_instruments(params[:instruments].nil? ? [] : params[:instruments]) if params.has_key?(:instruments)
@user.show_whats_next = params[:show_whats_next] if params.has_key?(:show_whats_next)
@user.subscribe_email = params[:subscribe_email] if params.has_key?(:subscribe_email)
@user.save
@ -420,13 +422,14 @@ class ApiUsersController < ApiController
end
def isp_scoring
if request.post?
data = request.body.read
User.connection.execute("INSERT INTO isp_score_batch(json_scoring_data) VALUES ('#{data}')")
data = request.body.read
score = IspScoreBatch.new
score.json_scoring_data = data
if score.save
render :text => 'scoring recorded'
return
else
render :text => "score invalid: #{score.errors.inspect}", status:422
end
render :nothing => true
end
################# AVATAR #####################

View File

@ -3,6 +3,14 @@ class ClientsController < ApplicationController
include UsersHelper
def index
# we want to enforce that /client is always the client view prefix
# this is a side effect of setting root path to '/'; soon we can remove this when we implement the new home page
if request.path == '/'
redirect_to client_url
return
end
# use gon to pass variables into javascript
gon.websocket_gateway_uri = Rails.application.config.websocket_gateway_uri
gon.check_for_client_updates = Rails.application.config.check_for_client_updates
@ -21,6 +29,9 @@ class ClientsController < ApplicationController
end
end
# let javascript have access to the server's opinion if this is a native client
gon.isNativeClient = @nativeClient
if current_user
render :layout => 'client'
else

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