* fix padding issue
This commit is contained in:
parent
491bc8cda5
commit
86ea959972
|
|
@ -326,4 +326,5 @@ profile_teacher.sql
|
|||
populate_languages.sql
|
||||
populate_subjects.sql
|
||||
reviews.sql
|
||||
download_tracker_fingerprints.sql
|
||||
download_tracker_fingerprints.sql
|
||||
connection_active.sql
|
||||
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE connections ADD COLUMN user_active BOOLEAN DEFAULT TRUE;
|
||||
|
|
@ -21,6 +21,7 @@ message ClientMessage {
|
|||
UNSUBSCRIBE = 137;
|
||||
SUBSCRIPTION_MESSAGE = 138;
|
||||
SUBSCRIBE_BULK = 139;
|
||||
USER_STATUS = 141;
|
||||
|
||||
// friend notifications
|
||||
FRIEND_UPDATE = 140;
|
||||
|
|
@ -67,6 +68,7 @@ message ClientMessage {
|
|||
// text message
|
||||
TEXT_MESSAGE = 236;
|
||||
CHAT_MESSAGE = 237;
|
||||
SEND_CHAT_MESSAGE = 238;
|
||||
|
||||
MUSICIAN_SESSION_FRESH = 240;
|
||||
MUSICIAN_SESSION_STALE = 245;
|
||||
|
|
@ -131,6 +133,7 @@ message ClientMessage {
|
|||
optional Unsubscribe unsubscribe = 137;
|
||||
optional SubscriptionMessage subscription_message = 138;
|
||||
optional SubscribeBulk subscribe_bulk = 139;
|
||||
optional UserStatus user_status = 141;
|
||||
|
||||
// friend notifications
|
||||
optional FriendUpdate friend_update = 140; // from server to all friends of user
|
||||
|
|
@ -178,6 +181,7 @@ message ClientMessage {
|
|||
// text message
|
||||
optional TextMessage text_message = 236;
|
||||
optional ChatMessage chat_message = 237;
|
||||
optional SendChatMessage send_chat_message = 238;
|
||||
|
||||
optional MusicianSessionFresh musician_session_fresh = 240;
|
||||
optional MusicianSessionStale musician_session_stale = 245;
|
||||
|
|
@ -569,6 +573,12 @@ message ChatMessage {
|
|||
optional string msg = 3;
|
||||
optional string msg_id = 4;
|
||||
optional string created_at = 5;
|
||||
optional string channel = 6;
|
||||
}
|
||||
|
||||
message SendChatMessage {
|
||||
optional string msg = 1;
|
||||
optional string channel = 2;
|
||||
}
|
||||
|
||||
// route_to: client:
|
||||
|
|
@ -657,6 +667,11 @@ message SubscribeBulk {
|
|||
//repeated Subscription subscriptions = 1; # the ruby protocol buffer library chokes on this. so we have to do the above
|
||||
}
|
||||
|
||||
message UserStatus {
|
||||
optional bool active = 1; // same as heartbeat 'active'... does the user appear present
|
||||
optional string status = 2;
|
||||
}
|
||||
|
||||
// route_to: session
|
||||
// a test message used by ruby-client currently. just gives way to send out to rest of session
|
||||
message TestSessionMessage {
|
||||
|
|
@ -693,6 +708,7 @@ message TestClientMessage {
|
|||
message Heartbeat {
|
||||
optional string notification_seen = 1;
|
||||
optional string notification_seen_at = 2;
|
||||
optional bool active = 3; // is the user active?
|
||||
}
|
||||
|
||||
// target: client
|
||||
|
|
|
|||
|
|
@ -1650,11 +1650,11 @@ module JamRuby
|
|||
output = cmd("soxi -r \"#{track.wav_file}\"", "get_sample_rate")
|
||||
sample_rate = output.to_i
|
||||
|
||||
create_silence(tmp_dir, "padded_silence#{track.id}", amount, sample_rate, channels)
|
||||
padding_file = create_silence(tmp_dir, "padded_silence#{track.id}", amount, sample_rate, channels)
|
||||
|
||||
output_file = File.join(tmp_dir, "with_padding_#{track.id}.wav")
|
||||
|
||||
cmd("sox \"#{track.wav_file}\" \"#{output_file}\"", "same_lengthening")
|
||||
cmd("sox \"#{track.wav_file}\" \"#{padding_file}\" \"#{output_file}\"", "same_lengthening")
|
||||
|
||||
track.wav_file = output_file
|
||||
end
|
||||
|
|
|
|||
|
|
@ -897,19 +897,26 @@ module JamRuby
|
|||
)
|
||||
end
|
||||
|
||||
# creates the session chat message
|
||||
def chat_message(session_id, sender_name, sender_id, msg, msg_id, created_at)
|
||||
# creates the chat message
|
||||
def chat_message(session_id, sender_name, sender_id, msg, msg_id, created_at, channel)
|
||||
chat_message = Jampb::ChatMessage.new(
|
||||
:sender_id => sender_id,
|
||||
:sender_name => sender_name,
|
||||
:msg => msg,
|
||||
:msg_id => msg_id,
|
||||
:created_at => created_at
|
||||
:created_at => created_at,
|
||||
:channel => channel
|
||||
)
|
||||
|
||||
if session_id
|
||||
route_to = SESSION_TARGET_PREFIX + session_id
|
||||
else
|
||||
route_to = ALL_ACTIVE_CLIENTS
|
||||
end
|
||||
|
||||
Jampb::ClientMessage.new(
|
||||
:type => ClientMessage::Type::CHAT_MESSAGE,
|
||||
:route_to => SESSION_TARGET_PREFIX + session_id,
|
||||
:route_to => route_to,
|
||||
:chat_message => chat_message
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -50,7 +50,8 @@ module JamRuby
|
|||
user.id,
|
||||
chat_msg.message,
|
||||
chat_msg.id,
|
||||
chat_msg.created_at.utc.iso8601
|
||||
chat_msg.created_at.utc.iso8601,
|
||||
'session'
|
||||
)
|
||||
|
||||
@@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id})
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
UNSUBSCRIBE : "UNSUBSCRIBE",
|
||||
SUBSCRIPTION_MESSAGE : "SUBSCRIPTION_MESSAGE",
|
||||
SUBSCRIBE_BULK : "SUBSCRIBE_BULK",
|
||||
USER_STATUS : "USER_STATUS",
|
||||
|
||||
// friend notifications
|
||||
FRIEND_UPDATE : "FRIEND_UPDATE",
|
||||
|
|
@ -66,6 +67,7 @@
|
|||
// text message
|
||||
TEXT_MESSAGE : "TEXT_MESSAGE",
|
||||
CHAT_MESSAGE : "CHAT_MESSAGE",
|
||||
SEND_CHAT_MESSAGE : "SEND_CHAT_MESSAGE",
|
||||
|
||||
// broadcast notifications
|
||||
SOURCE_UP_REQUESTED : "SOURCE_UP_REQUESTED",
|
||||
|
|
@ -120,13 +122,30 @@
|
|||
};
|
||||
|
||||
// Heartbeat message
|
||||
factory.heartbeat = function(lastNotificationSeen, lastNotificationSeenAt) {
|
||||
factory.heartbeat = function(lastNotificationSeen, lastNotificationSeenAt, active) {
|
||||
var data = {};
|
||||
data.notification_seen = lastNotificationSeen;
|
||||
data.notification_seen_at = lastNotificationSeenAt;
|
||||
data.active = active;
|
||||
return client_container(msg.HEARTBEAT, route_to.SERVER, data);
|
||||
};
|
||||
|
||||
// User Status update message
|
||||
factory.userStatus = function(active, status) {
|
||||
var data = {};
|
||||
data.active = active;
|
||||
data.status = status;
|
||||
return client_container(msg.USER_STATUS, route_to.SERVER, data);
|
||||
};
|
||||
|
||||
factory.chatMessage = function(channel, msg) {
|
||||
var data = {}
|
||||
data.channel = {}
|
||||
data.msg = {}
|
||||
|
||||
return client_container(msg.SEND_CHAT_MESSAGE, route_to.SERVER, data)
|
||||
}
|
||||
|
||||
// create a login message using user/pass
|
||||
factory.login_with_user_pass = function(username, password) {
|
||||
var login = { username : username , password : password };
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
var notificationLastSeen = undefined;
|
||||
var clientClosedConnection = false;
|
||||
var initialConnectAttempt = true;
|
||||
var active = true;
|
||||
|
||||
// reconnection logic
|
||||
var connectDeferred = null;
|
||||
|
|
@ -46,6 +47,7 @@
|
|||
var reconnectingWaitPeriodStart = null;
|
||||
var reconnectDueTime = null;
|
||||
var connectTimeout = null;
|
||||
var activityTimeout;
|
||||
|
||||
// elements
|
||||
var $inSituBanner = null;
|
||||
|
|
@ -80,7 +82,6 @@
|
|||
function initiateReconnect(activeElementVotes, in_error) {
|
||||
var initialConnect = !!activeElementVotes;
|
||||
|
||||
console.log("activeElementVotes", activeElementVotes)
|
||||
freezeInteraction = activeElementVotes && ((activeElementVotes.dialog && activeElementVotes.dialog.freezeInteraction === true) || (activeElementVotes.screen && activeElementVotes.screen.freezeInteraction === true));
|
||||
|
||||
if (in_error) {
|
||||
|
|
@ -174,7 +175,8 @@
|
|||
|
||||
function _heartbeat() {
|
||||
if (app.heartbeatActive) {
|
||||
var message = context.JK.MessageFactory.heartbeat(notificationLastSeen, notificationLastSeenAt);
|
||||
console.log("heartbeat active?: " + active)
|
||||
var message = context.JK.MessageFactory.heartbeat(notificationLastSeen, notificationLastSeenAt, active);
|
||||
notificationLastSeenAt = undefined;
|
||||
notificationLastSeen = undefined;
|
||||
// for debugging purposes, see if the last time we've sent a heartbeat is way off (500ms) of the target interval
|
||||
|
|
@ -290,6 +292,35 @@
|
|||
}, 0)
|
||||
}
|
||||
|
||||
function markAway() {
|
||||
logger.debug("sleep again!")
|
||||
active = false;
|
||||
}
|
||||
|
||||
function activityCheck() {
|
||||
var timeoutTime = 300000; // 5 * 1000 * 60 , 5 minutes
|
||||
active = true;
|
||||
activityTimeout = setTimeout(markAway, timeoutTime);
|
||||
$(document).ready(function() {
|
||||
$('body').bind('mousedown keydown touchstart focus', function(event) {
|
||||
if (activityTimeout) {
|
||||
clearTimeout(activityTimeout);
|
||||
activityTimeout = null;
|
||||
}
|
||||
|
||||
if (!active) {
|
||||
if(server && server.connected) {
|
||||
logger.debug("awake again!")
|
||||
var userStatus = msg_factory.userStatus(true, null);
|
||||
server.send(userStatus);
|
||||
}
|
||||
}
|
||||
active = true;
|
||||
activityTimeout = setTimeout(markAway, timeoutTime);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function heartbeatAck(header, payload) {
|
||||
lastHeartbeatAckTime = new Date();
|
||||
}
|
||||
|
|
@ -771,6 +802,19 @@
|
|||
//console.timeEnd('sendP2PMessage');
|
||||
};
|
||||
|
||||
server.sendChatMessage = function(message) {
|
||||
|
||||
if (server.connected) {
|
||||
|
||||
var chatMsg = msg_factory.chatMessage(channel, message)
|
||||
server.send(chatMsg)
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
server.updateNotificationSeen = function (notificationId, notificationCreatedAt) {
|
||||
var time = new Date(notificationCreatedAt);
|
||||
|
||||
|
|
@ -816,6 +860,7 @@
|
|||
registerHeartbeatAck();
|
||||
registerServerRejection();
|
||||
registerSocketClosed();
|
||||
activityCheck();
|
||||
|
||||
$inSituBanner = $('.server-connection');
|
||||
$inSituBannerHolder = $('.no-websocket-connection');
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
//= require jquery.visible
|
||||
//= require jquery.jstarbox
|
||||
//= require fingerprint2.min
|
||||
//= require ResizeSensor
|
||||
//= require classnames
|
||||
//= require reflux
|
||||
//= require howler.core.js
|
||||
|
|
|
|||
|
|
@ -15,9 +15,10 @@
|
|||
var feedHelper = new context.JK.Feed(app);
|
||||
var $scroller = $recordings;
|
||||
var $content = $recordings;
|
||||
var $recordsHolder = $screen.find('.recordings-content');
|
||||
var $noMoreFeeds = $screen.find('.end-of-list');
|
||||
var $empty = $();
|
||||
feedHelper.initialize($screen, $scroller, $content, $noMoreFeeds, $empty, $empty, $empty, $empty, {sort: 'date', time_range: 'all', type: 'recording', show_checkbox: true, hide_avatar: true});
|
||||
feedHelper.initialize($screen, $scroller, $recordsHolder, $noMoreFeeds, $empty, $empty, $empty, $empty, {sort: 'date', time_range: 'all', type: 'recording', show_checkbox: true, hide_avatar: true});
|
||||
|
||||
function beforeShow(data) {
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
trackNewUser();
|
||||
|
||||
trackScreenChanges();
|
||||
|
||||
})
|
||||
|
||||
$(document).on('JAMKAZAM_READY', function() {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
var userId = null;
|
||||
var bandId = null;
|
||||
var currentFeedPage = 0;
|
||||
var feedBatchSize = 10;
|
||||
var feedBatchSize = 4;
|
||||
var $screen = null;
|
||||
var $scroller = null;
|
||||
var $content = null;
|
||||
|
|
@ -454,7 +454,6 @@
|
|||
mix_class: feed['has_mix?'] ? 'has-mix' : 'no-mix',
|
||||
}
|
||||
|
||||
console.log("OPTIONS", options)
|
||||
var $feedItem = $(context._.template($('#template-feed-recording').html(), options, {variable: 'data'}));
|
||||
var $controls = $feedItem.find('.recording-controls');
|
||||
|
||||
|
|
|
|||
|
|
@ -739,6 +739,13 @@
|
|||
stackDialogs($dialog, $overlay);
|
||||
addScreenContextToDialog($dialog)
|
||||
$dialog.show();
|
||||
|
||||
// maintain center (un-attach previous sensor if applicable, then re-add always)
|
||||
window.ResizeSensor.detach($dialog.get(0))
|
||||
new window.ResizeSensor($dialog, function(){
|
||||
centerDialog(dialog);
|
||||
});
|
||||
|
||||
dialogEvent(dialog, 'afterShow', options);
|
||||
$.btOffAll(); // add any prod bubbles if you open a dailog
|
||||
return $dialog;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
//= require ./react-components/stores/SubjectStore
|
||||
//= require ./react-components/stores/ProfileStore
|
||||
//= require ./react-components/stores/PlatformStore
|
||||
//= require ./react-components/stores/ChatStore
|
||||
//= require ./react-components/stores/BrowserMediaStore
|
||||
//= require ./react-components/stores/RecordingStore
|
||||
//= require ./react-components/stores/VideoStore
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
context = window
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
|
||||
@ChatWindow = React.createClass({
|
||||
mixins: [Reflux.listenTo(@AppStore, "onAppInit"), Reflux.listenTo(@UserStore, "onUserChanged"), Reflux.listenTo(@ChatStore, "onChatChanged")]
|
||||
|
||||
getInitialState: () ->
|
||||
{channels:['global', 'session']}
|
||||
|
||||
render: () ->
|
||||
`<div className="ChatWindow">
|
||||
<div className="tabs">
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
|
|
@ -21,7 +21,6 @@ logger = context.JK.logger
|
|||
render: () ->
|
||||
object_options = []
|
||||
|
||||
logger.debug("Rendering EditableList", this.props, this.props.listItems)
|
||||
|
||||
if this.props.listItems? && this.props.listItems.length > 0
|
||||
for object,i in this.props.listItems
|
||||
|
|
|
|||
|
|
@ -11,8 +11,14 @@ logger = context.JK.logger
|
|||
@root.off("submit", ".teacher-experience-teaching-form").on("submit", ".teacher-experience-teaching-form", @addExperience)
|
||||
|
||||
formatListItem: (obj) ->
|
||||
|
||||
if obj.end_year?
|
||||
endYear = obj.end_year
|
||||
else
|
||||
endYear = 'Present'
|
||||
|
||||
t = "#{obj.name}/#{obj.organization} (#{obj.start_year}"
|
||||
t += "-#{obj.end_year}" if this.props.showEndDate
|
||||
t += "-#{endYear}" if this.props.showEndDate
|
||||
t += ")"
|
||||
|
||||
getInitialProps: () ->
|
||||
|
|
@ -24,7 +30,6 @@ logger = context.JK.logger
|
|||
|
||||
addExperience: (e) ->
|
||||
e.preventDefault()
|
||||
logger.debug("addExperience", this.props.listItems, this.props)
|
||||
$form = e.target
|
||||
|
||||
start_year = $("[name='start_year']", $form).val()
|
||||
|
|
|
|||
|
|
@ -152,21 +152,23 @@ proficiencyDescriptionMap = {
|
|||
</div>
|
||||
</div>`
|
||||
|
||||
getYoutubeId:(url) ->
|
||||
regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/
|
||||
match = url.match(regExp)
|
||||
|
||||
if match && match[2].length >= 9
|
||||
return match[2]
|
||||
else
|
||||
return 'unknown'
|
||||
|
||||
|
||||
sampleVideo: (user, teacher) ->
|
||||
if teacher.introductory_video?
|
||||
if teacher.introductory_video? && teacher.introductory_video.length > 0
|
||||
videoUrl = teacher.introductory_video
|
||||
|
||||
if videoUrl.indexOf(window.location.protocol) != 0
|
||||
console.log("replacing video")
|
||||
if window.location.protocol == 'http:'
|
||||
console.log("replacing https: " + videoUrl)
|
||||
videoUrl = videoUrl.replace('https://', 'http://')
|
||||
console.log("replaced : " + videoUrl)
|
||||
else
|
||||
videoUrl = videoUrl.replace('http://', 'https://')
|
||||
|
||||
videoUrl = videoUrl.replace("watch?v=", "v/")
|
||||
|
||||
# http://stackoverflow.com/a/21607897
|
||||
embedId = @getYoutubeId(videoUrl)
|
||||
embedUrl = "//www.youtube.com/embed/#{embedId}"
|
||||
|
||||
return `<div className="section introductory-video">
|
||||
<h3>Intro Video</h3>
|
||||
|
|
@ -174,7 +176,7 @@ proficiencyDescriptionMap = {
|
|||
<div className="section-content">
|
||||
<div className="video-wrapper">
|
||||
<div className="video-container">
|
||||
<iframe src={videoUrl} frameborder="0" allowfullscreen="allowfullscreen"/>
|
||||
<iframe src={embedUrl} frameborder="0" allowfullscreen="allowfullscreen"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -402,7 +404,7 @@ proficiencyDescriptionMap = {
|
|||
if teachingExperience.end_year && teachingExperience.end_year > 0
|
||||
years = "#{teachingExperience.start_year} - #{teachingExperience.end_year}"
|
||||
else
|
||||
years = "#{teachingExperience.start_year} - Present"
|
||||
years = "#{teachingExperience.start_year} - Present"
|
||||
|
||||
teachingExperiences.push(`<div key={i} className="experience">
|
||||
<div className="years">{years}</div>
|
||||
|
|
@ -505,7 +507,7 @@ proficiencyDescriptionMap = {
|
|||
if summary.review_count == 1
|
||||
reviewCount = '1 review'
|
||||
else
|
||||
reviewCount = sumarry.review_count + ' reviews'
|
||||
reviewCount = summary.review_count + ' reviews'
|
||||
reviews = []
|
||||
|
||||
for review in teacher.recent_reviews
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ logger = context.JK.logger
|
|||
options = []
|
||||
|
||||
now = new Date().getFullYear()
|
||||
for yr in [1901..now]
|
||||
for yr in [now..1916]
|
||||
options.push `<option value={yr}>{yr}</option>`
|
||||
|
||||
`<select className="YearSelect react-component" name={this.props.name} required placeholder="Select" defaultValue="2010">
|
||||
`<select className="YearSelect react-component" name={this.props.name} required placeholder="Select" defaultValue={now}>
|
||||
{options}
|
||||
</select>`
|
||||
})
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
context = window
|
||||
|
||||
@ChatActions = Reflux.createActions({
|
||||
msgReceived: {}
|
||||
sendMsg: {}
|
||||
})
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
@ChatStore = Reflux.createStore(
|
||||
{
|
||||
listenables: @ChatActions
|
||||
|
||||
msgs: {}
|
||||
|
||||
init: ->
|
||||
# Register with the app store to get @app
|
||||
this.listenTo(context.AppStore, this.onAppInit)
|
||||
|
||||
onAppInit: (@app) ->
|
||||
|
||||
onMsgReceived: (msg) ->
|
||||
logger.debug("ChatStore.msgReceived", msg)
|
||||
|
||||
channelMsgs = @msgs[msg.channel]
|
||||
|
||||
if !channelMsgs?
|
||||
channelMsgs = []
|
||||
@msgs[msg.channel] = channelMsgs
|
||||
|
||||
channelMsgs.push(msg)
|
||||
@changed()
|
||||
|
||||
onSendMsg: (msg) ->
|
||||
logger.debug("ChatStore.sendMsg", msg)
|
||||
|
||||
window.JK.JamServer.sendChatMessage(msg)
|
||||
|
||||
changed: () ->
|
||||
@trigger(msgs)
|
||||
}
|
||||
)
|
||||
|
|
@ -126,9 +126,13 @@
|
|||
|
||||
.actions {
|
||||
position:absolute;
|
||||
top:0;
|
||||
top:2px;
|
||||
right:0;
|
||||
}
|
||||
|
||||
.display-value {
|
||||
margin:2px 10px 2px 2px;
|
||||
}
|
||||
}
|
||||
|
||||
h3.margined {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
}
|
||||
|
||||
.recordings {
|
||||
min-height:30px;
|
||||
min-height:50px;
|
||||
max-height:450px;
|
||||
margin-bottom:20px;
|
||||
overflow-y:auto;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@
|
|||
.dialog-inner
|
||||
.instructions
|
||||
= 'Select one or more recordings and click ADD to add JamKazam recordings to your performance samples.'
|
||||
.recordings
|
||||
.end-of-list No more feed entries
|
||||
#recording-selector-dialog-feed-scroller.recordings
|
||||
.recordings-content#recording-selector-dialog-feed-entry-list
|
||||
#recording-selector-dialog-end-of-feeds-list.end-of-list= 'No more recordings'
|
||||
#recording-selector-dialog-loading-feeds.infinite-scroll-loader{:style => 'padding:5px'}= 'Loading ...'
|
||||
.right.action-buttons
|
||||
%a.button-grey.btn-cancel-dialog{'layout-action' => 'cancel'} CANCEL
|
||||
%a.button-orange.btn-select-recordings SAVE
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
/**
|
||||
* Copyright (c) 2013 Marc J. Schmidt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
/**
|
||||
* Copyright Marc J. Schmidt. See the LICENSE file at the top-level
|
||||
* directory of this distribution and at
|
||||
* https://github.com/marcj/css-element-queries/blob/master/LICENSE.
|
||||
*/
|
||||
;
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* Class for dimension change detection.
|
||||
*
|
||||
* @param {Element|Element[]|Elements|jQuery} element
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
this.ResizeSensor = function(element, callback) {
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function EventQueue() {
|
||||
this.q = [];
|
||||
this.add = function(ev) {
|
||||
this.q.push(ev);
|
||||
};
|
||||
|
||||
var i, j;
|
||||
this.call = function() {
|
||||
for (i = 0, j = this.q.length; i < j; i++) {
|
||||
this.q[i].call();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} element
|
||||
* @param {String} prop
|
||||
* @returns {String|Number}
|
||||
*/
|
||||
function getComputedStyle(element, prop) {
|
||||
if (element.currentStyle) {
|
||||
return element.currentStyle[prop];
|
||||
} else if (window.getComputedStyle) {
|
||||
return window.getComputedStyle(element, null).getPropertyValue(prop);
|
||||
} else {
|
||||
return element.style[prop];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
* @param {Function} resized
|
||||
*/
|
||||
function attachResizeEvent(element, resized) {
|
||||
if (!element.resizedAttached) {
|
||||
element.resizedAttached = new EventQueue();
|
||||
element.resizedAttached.add(resized);
|
||||
} else if (element.resizedAttached) {
|
||||
element.resizedAttached.add(resized);
|
||||
return;
|
||||
}
|
||||
|
||||
element.resizeSensor = document.createElement('div');
|
||||
element.resizeSensor.className = 'resize-sensor';
|
||||
var style = 'position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;';
|
||||
var styleChild = 'position: absolute; left: 0; top: 0;';
|
||||
|
||||
element.resizeSensor.style.cssText = style;
|
||||
element.resizeSensor.innerHTML =
|
||||
'<div class="resize-sensor-expand" style="' + style + '">' +
|
||||
'<div style="' + styleChild + '"></div>' +
|
||||
'</div>' +
|
||||
'<div class="resize-sensor-shrink" style="' + style + '">' +
|
||||
'<div style="' + styleChild + ' width: 200%; height: 200%"></div>' +
|
||||
'</div>';
|
||||
element.appendChild(element.resizeSensor);
|
||||
|
||||
if (!{fixed: 1, absolute: 1}[getComputedStyle(element, 'position')]) {
|
||||
element.style.position = 'relative';
|
||||
}
|
||||
|
||||
var expand = element.resizeSensor.childNodes[0];
|
||||
var expandChild = expand.childNodes[0];
|
||||
var shrink = element.resizeSensor.childNodes[1];
|
||||
var shrinkChild = shrink.childNodes[0];
|
||||
|
||||
var lastWidth, lastHeight;
|
||||
|
||||
var reset = function() {
|
||||
expandChild.style.width = expand.offsetWidth + 10 + 'px';
|
||||
expandChild.style.height = expand.offsetHeight + 10 + 'px';
|
||||
expand.scrollLeft = expand.scrollWidth;
|
||||
expand.scrollTop = expand.scrollHeight;
|
||||
shrink.scrollLeft = shrink.scrollWidth;
|
||||
shrink.scrollTop = shrink.scrollHeight;
|
||||
lastWidth = element.offsetWidth;
|
||||
lastHeight = element.offsetHeight;
|
||||
};
|
||||
|
||||
reset();
|
||||
|
||||
var changed = function() {
|
||||
if (element.resizedAttached) {
|
||||
element.resizedAttached.call();
|
||||
}
|
||||
};
|
||||
|
||||
var addEvent = function(el, name, cb) {
|
||||
if (el.attachEvent) {
|
||||
el.attachEvent('on' + name, cb);
|
||||
} else {
|
||||
el.addEventListener(name, cb);
|
||||
}
|
||||
};
|
||||
|
||||
var onScroll = function() {
|
||||
if (element.offsetWidth != lastWidth || element.offsetHeight != lastHeight) {
|
||||
changed();
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
addEvent(expand, 'scroll', onScroll);
|
||||
addEvent(shrink, 'scroll', onScroll);
|
||||
}
|
||||
|
||||
var elementType = Object.prototype.toString.call(element);
|
||||
var isCollectionTyped = ('[object Array]' === elementType
|
||||
|| ('[object NodeList]' === elementType)
|
||||
|| ('[object HTMLCollection]' === elementType)
|
||||
|| ('undefined' !== typeof jQuery && element instanceof jQuery) //jquery
|
||||
|| ('undefined' !== typeof Elements && element instanceof Elements) //mootools
|
||||
);
|
||||
|
||||
if (isCollectionTyped) {
|
||||
var i = 0, j = element.length;
|
||||
for (; i < j; i++) {
|
||||
attachResizeEvent(element[i], callback);
|
||||
}
|
||||
} else {
|
||||
attachResizeEvent(element, callback);
|
||||
}
|
||||
|
||||
this.detach = function() {
|
||||
if (isCollectionTyped) {
|
||||
var i = 0, j = element.length;
|
||||
for (; i < j; i++) {
|
||||
ResizeSensor.detach(element[i]);
|
||||
}
|
||||
} else {
|
||||
ResizeSensor.detach(element);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
this.ResizeSensor.detach = function(element) {
|
||||
if (element.resizeSensor) {
|
||||
element.removeChild(element.resizeSensor);
|
||||
delete element.resizeSensor;
|
||||
delete element.resizedAttached;
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
module JamWebsockets
|
||||
class ClientContext
|
||||
|
||||
attr_accessor :user, :client, :msg_count, :session, :client_type, :sent_bad_state_previously
|
||||
attr_accessor :user, :client, :msg_count, :session, :client_type, :sent_bad_state_previously, :active
|
||||
|
||||
def initialize(user, client, client_type)
|
||||
@user = user
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
@msg_count = 0
|
||||
@session = nil
|
||||
@sent_bad_state_previously = false
|
||||
@active = true
|
||||
client.context = self
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ module JamWebsockets
|
|||
@profile_it_sums = {}
|
||||
@heartbeat_tracker = {}
|
||||
@temp_ban = {}
|
||||
@chat_enabled = {}
|
||||
|
||||
@login_success_count = 0
|
||||
@login_fail_count = 0
|
||||
|
|
@ -80,7 +81,7 @@ module JamWebsockets
|
|||
|
||||
end
|
||||
|
||||
def start(connect_time_stale_client, connect_time_expire_client, connect_time_stale_browser, connect_time_expire_browser, options={:host => "localhost", :port => 5672, :max_connections_per_user => 10, :gateway => 'default', :allow_dynamic_registration => true}, &block)
|
||||
def start(connect_time_stale_client, connect_time_expire_client, connect_time_stale_browser, connect_time_expire_browser, options={:host => "localhost", :port => 5672, :max_connections_per_user => 10, :gateway => 'default', :allow_dynamic_registration => true, chat_enabled => true}, &block)
|
||||
|
||||
@log.info "startup"
|
||||
|
||||
|
|
@ -93,6 +94,7 @@ module JamWebsockets
|
|||
@max_connections_per_user = options[:max_connections_per_user]
|
||||
@gateway_name = options[:gateway]
|
||||
@allow_dynamic_registration = options[:allow_dynamic_registration]
|
||||
@chat_enabled = options[:chat_enabled]
|
||||
|
||||
# determine the maximum amount of heartbeats we should get per user
|
||||
@maximum_minutely_heartbeat_rate_client = ((@heartbeat_interval_client / 60.0) * 2).ceil + 3
|
||||
|
|
@ -237,6 +239,23 @@ module JamWebsockets
|
|||
end
|
||||
end
|
||||
end
|
||||
elsif client_id == MessageFactory::ALL_ACTIVE_CLIENTS
|
||||
if @chat_enabled
|
||||
msg = Jampb::ClientMessage.parse(msg)
|
||||
@log.debug "client-directed message received from #{msg.from} to all chat clients"
|
||||
@client_lookup.each do |client_id, client_context|
|
||||
if client_context.active
|
||||
client = client_context.client
|
||||
|
||||
if client
|
||||
EM.schedule do
|
||||
#@log.debug "sending client-directed down websocket to #{client_id}"
|
||||
send_to_client(client, msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
client_context = @client_lookup[client_id]
|
||||
|
||||
|
|
@ -568,6 +587,8 @@ module JamWebsockets
|
|||
|
||||
elsif client_msg.type == ClientMessage::Type::HEARTBEAT
|
||||
time_it('heartbeat') { sane_logging { handle_heartbeat(client_msg.heartbeat, client_msg.message_id, client) } }
|
||||
elsif client_msg.type == ClientMessage::Type::USER_STATUS
|
||||
time_it('user_status') { sane_logging { handle_user_status(client_msg.user_status, client) } }
|
||||
elsif client_msg.type == ClientMessage::Type::SUBSCRIBE_BULK
|
||||
time_it('subscribe_bulk') { sane_logging { handle_bulk_subscribe(client_msg.subscribe_bulk, client) } }
|
||||
elsif client_msg.type == ClientMessage::Type::SUBSCRIBE
|
||||
|
|
@ -897,6 +918,10 @@ module JamWebsockets
|
|||
|
||||
end
|
||||
|
||||
def handle_user_status(user_status, client)
|
||||
client.context.active = user_status.active
|
||||
end
|
||||
|
||||
def handle_heartbeat(heartbeat, heartbeat_message_id, client)
|
||||
unless context = @clients[client]
|
||||
profile_it('heartbeat_context_gone') {
|
||||
|
|
@ -932,8 +957,8 @@ module JamWebsockets
|
|||
}
|
||||
|
||||
profile_it('heartbeat_touch') {
|
||||
# update connection updated_at
|
||||
connection.touch
|
||||
# update connection updated_at and if the user is active
|
||||
Connection.where(id: connection.id).update_all(user_active: heartbeat.active)
|
||||
}
|
||||
|
||||
profile_it('heartbeat_notification') {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ module JamWebsockets
|
|||
connect_time_stale_browser = options[:connect_time_stale_browser].to_i
|
||||
connect_time_expire_browser = options[:connect_time_expire_browser].to_i
|
||||
max_connections_per_user = options[:max_connections_per_user].to_i
|
||||
chat_enabled = options[:chat_enabled].nil? ? true : options[:chat_enabled]
|
||||
gateway_name = options[:gateway_name]
|
||||
rabbitmq_host = options[:rabbitmq_host]
|
||||
rabbitmq_port = options[:rabbitmq_port].to_i
|
||||
|
|
@ -43,7 +44,7 @@ module JamWebsockets
|
|||
}
|
||||
|
||||
EventMachine.run do
|
||||
@router.start(connect_time_stale_client, connect_time_expire_client, connect_time_stale_browser, connect_time_expire_browser, host: rabbitmq_host, port: rabbitmq_port, max_connections_per_user: max_connections_per_user, gateway: gateway_name, allow_dynamic_registration: allow_dynamic_registration) do
|
||||
@router.start(connect_time_stale_client, connect_time_expire_client, connect_time_stale_browser, connect_time_expire_browser, host: rabbitmq_host, port: rabbitmq_port, max_connections_per_user: max_connections_per_user, gateway: gateway_name, allow_dynamic_registration: allow_dynamic_registration, chat_enabled: chat_enabled) do
|
||||
start_connection_expiration
|
||||
start_client_expiration
|
||||
start_connection_flagger
|
||||
|
|
|
|||
Loading…
Reference in New Issue