2015-01-20 01:53:18 -05:00
var FFZ = window . FrankerFaceZ ,
2015-07-29 01:03:10 -04:00
HOSTED _SUB = / subscribed to / ,
2015-01-20 01:53:18 -05:00
constants = require ( '../constants' ) ,
utils = require ( '../utils' ) ,
2015-11-07 22:56:15 -05:00
helpers ,
2015-01-20 01:53:18 -05:00
2016-09-09 17:34:20 -04:00
NOTICE _MAPPING = {
'slow' : 'slow_on' ,
'slowoff' : 'slow_off' ,
'r9kbeta' : 'r9k_on' ,
'r9kbetaoff' : 'r9k_off' ,
'subscribers' : 'subs_on' ,
'subscribersoff' : 'subs_off' ,
'emoteonly' : 'emote_only_on' ,
2016-09-30 13:09:03 -04:00
'emoteonlyoff' : 'emote_only_off' ,
'host' : 'host_on' ,
2016-10-14 20:43:34 -04:00
'unhost' : 'host_off' ,
'clear' : 'clear_chat'
2016-09-09 17:34:20 -04:00
} ,
2016-05-06 02:23:12 -04:00
STATUS _BADGES = [
[ "r9k" , "r9k" , "This room is in R9K-mode." ] ,
2016-05-20 17:30:34 -04:00
[ "emote" , "emoteOnly" , "This room is in Twitch emoticons only mode. Emoticons added by extensions are not available in this mode." ] ,
2016-05-06 02:23:12 -04:00
[ "sub" , "subsOnly" , "This room is in subscribers-only mode." ] ,
2016-09-30 13:09:03 -04:00
[ "slow" , "slow" , function ( room ) { return "This room is in slow mode. You may send messages every <nobr>" + utils . number _commas ( room && room . get ( 'slow' ) || 120 ) + " seconds</nobr>." } ] ,
2016-05-06 02:23:12 -04:00
[ "ban" , "ffz_banned" , "You have been banned from talking in this room." ] ,
2016-09-30 13:09:03 -04:00
[ "delay" , function ( room ) {
return room && ( this . settings . chat _delay === - 1 ?
room . get ( 'roomProperties.chat_delay_duration' )
: room . get ( 'ffz_chat_delay' ) )
} , function ( room ) {
var is _mod = this . settings . chat _delay === - 1 ;
return "Artificial chat delay is enabled" + ( is _mod ? " for this channel" : "" ) + ". Messages are displayed after " + ( room ? ( is _mod ? room . get ( 'roomProperties.chat_delay_duration' ) : room . get ( 'ffz_chat_delay' ) / 1000 ) : 0 ) + " seconds" + ( is _mod ? " for <nobr>non-moderators</nobr>." : "." ) ;
} , null , function ( room ) {
return room && this . settings . chat _delay === - 1 && room . get ( 'isModeratorOrHigher' ) || false ;
} ] ,
[ "batch" , function ( ) { return this . settings . chat _batching !== 0 } , function ( ) { return "You have enabled chat message batching. Messages are displayed in <nobr>" + ( this . settings . chat _batching / 1000 ) + " second</nobr> increments." } ]
2016-05-06 02:23:12 -04:00
] ,
2015-07-18 21:10:27 -04:00
// StrimBagZ Support
is _android = navigator . userAgent . indexOf ( 'Android' ) !== - 1 ,
2015-01-20 01:53:18 -05:00
moderator _css = function ( room ) {
if ( ! room . moderator _badge )
return "" ;
2016-10-05 23:07:10 -04:00
return '.from-display-preview[data-room="' + room . id + '"] .badges .moderator:not(.ffz-badge-replacement):not(.colored),' +
'.chat-line[data-room="' + room . id + '"] .badges .moderator:not(.ffz-badge-replacement):not(.colored) {' +
'background-repeat: no-repeat;' +
'background-size: initial !important;' +
'background-position: center;' +
'background-image:url("' + room . moderator _badge + '") !important; }' +
'.from-display-preview[data-room="' + room . id + '"] .badges .moderator:not(.ffz-badge-replacement).colored,' +
'.chat-line[data-room="' + room . id + '"] .badges .moderator:not(.ffz-badge-replacement).colored {' +
'-webkit-mask-repeat: no-repeat;' +
'-webkit-mask-size: initial !important;' +
'-webkit-mask-position: center;' +
'-webkit-mask-image: url("' + room . moderator _badge + '"); }' ;
2015-11-07 22:56:15 -05:00
} ;
try {
helpers = window . require && window . require ( "ember-twitch-chat/helpers/chat-line-helpers" ) ;
} catch ( err ) { }
2015-01-20 01:53:18 -05:00
// --------------------
// Initialization
// --------------------
FFZ . prototype . setup _room = function ( ) {
this . log ( "Creating room style element." ) ;
2016-09-09 17:34:20 -04:00
var f = this ,
s = this . _room _style = document . createElement ( "style" ) ;
2015-01-20 01:53:18 -05:00
s . id = "ffz-room-css" ;
document . head . appendChild ( s ) ;
2016-09-09 17:34:20 -04:00
this . log ( "Hooking the Ember Chat PubSub service." ) ;
var PubSub = utils . ember _lookup ( 'service:chat-pubsub' ) ;
if ( PubSub )
this . _modify _chat _pubsub ( PubSub ) ;
else
this . error ( "Cannot locate the Chat PubSub service." ) ;
2015-07-04 17:06:36 -04:00
this . log ( "Hooking the Ember Room controller." ) ;
2015-01-20 01:53:18 -05:00
2015-05-17 19:02:57 -04:00
// Responsive ban button.
2016-09-09 17:34:20 -04:00
var RC = utils . ember _lookup ( 'controller:room' ) ;
2016-03-23 19:28:22 -04:00
2015-05-17 19:02:57 -04:00
if ( RC ) {
2015-07-04 17:06:36 -04:00
var orig _ban = RC . _actions . banUser ,
2016-07-13 02:06:50 -04:00
orig _to = RC . _actions . timeoutUser ,
orig _show = RC . _actions . showModOverlay ;
2015-07-04 17:06:36 -04:00
2015-05-17 19:02:57 -04:00
RC . _actions . banUser = function ( e ) {
2016-03-23 19:28:22 -04:00
orig _ban . call ( this , e ) ;
2016-05-13 23:56:59 -04:00
this . get ( "model" ) . clearMessages ( e . user , null , true ) ;
2015-07-04 17:06:36 -04:00
}
RC . _actions . timeoutUser = function ( e ) {
2016-03-23 19:28:22 -04:00
orig _to . call ( this , e ) ;
2016-05-13 23:56:59 -04:00
this . get ( "model" ) . clearMessages ( e . user , null , true ) ;
2015-05-17 19:02:57 -04:00
}
2015-12-12 13:28:35 -05:00
2016-07-13 02:06:50 -04:00
2015-12-12 13:28:35 -05:00
RC . _actions . showModOverlay = function ( e ) {
2016-07-13 02:06:50 -04:00
var Channel = utils . ember _resolve ( 'model:deprecated-channel' ) ,
chan = Channel && Channel . find && Channel . find ( { id : e . sender } ) ;
2015-12-12 13:28:35 -05:00
2016-07-13 02:06:50 -04:00
if ( ! chan ) {
f . log ( "Error opening mod card. model:deprecated-channel does not exist or does not have find!" ) ;
return orig _show . call ( this , e ) ;
}
2015-12-12 13:28:35 -05:00
// Don't try loading the channel if it's already loaded. Don't make mod cards
// refresh the channel page when you click the broadcaster, basically.
if ( ! chan . get ( 'isLoaded' ) )
chan . load ( ) ;
this . set ( "showModerationCard" , true ) ;
// We pass in renderBottom and renderRight, which we use to reposition the window
2016-06-22 14:23:03 -04:00
// after we know how big it actually is. This doesn't work a lot of the time.
2015-12-12 13:28:35 -05:00
this . set ( "moderationCardInfo" , {
user : chan ,
2016-06-22 14:23:03 -04:00
renderTop : e . real _top || e . top ,
2015-12-12 13:28:35 -05:00
renderLeft : e . left ,
renderBottom : e . bottom ,
renderRight : e . right ,
isIgnored : this . get ( "tmiSession" ) . isIgnored ( e . sender ) ,
2016-03-23 19:28:22 -04:00
isChannelOwner : this . get ( "login.userData.login" ) === e . sender ,
2015-12-12 13:28:35 -05:00
profileHref : Twitch . uri . profile ( e . sender ) ,
isModeratorOrHigher : this . get ( "model.isModeratorOrHigher" )
} ) ;
}
2015-05-17 19:02:57 -04:00
}
2015-07-04 17:06:36 -04:00
this . log ( "Hooking the Ember Room model." ) ;
2016-03-23 20:23:04 -04:00
var Room = utils . ember _resolve ( 'model:room' ) ;
2015-01-20 01:53:18 -05:00
this . _modify _room ( Room ) ;
// Modify all current instances of Room, as the changes to the base
// class won't be inherited automatically.
var instances = Room . instances ;
for ( var key in instances ) {
if ( ! instances . hasOwnProperty ( key ) )
continue ;
var inst = instances [ key ] ;
this . add _room ( inst . id , inst ) ;
this . _modify _room ( inst ) ;
2016-10-16 15:05:11 -04:00
inst . ffzUpdateBadges ( ) ;
2015-06-05 03:59:28 -04:00
inst . ffzPatchTMI ( ) ;
2015-01-20 01:53:18 -05:00
}
2015-07-04 17:06:36 -04:00
this . log ( "Hooking the Ember Room view." ) ;
2016-07-13 02:06:50 -04:00
this . update _views ( 'view:room' , this . modify _room _view ) ;
2015-07-04 17:06:36 -04:00
}
2016-10-14 20:43:34 -04:00
// --------------------
// Ban Message Formatting
// --------------------
FFZ . prototype . format _ban _notice = function ( username , is _me , duration , count , reasons , moderators , notices ) {
var name = this . format _display _name ( FFZ . get _capitalization ( username ) , username , true ) ,
duration _tip = [ ] ;
for ( var mod _id in notices ) {
var notice = notices [ mod _id ] ;
if ( ! Array . isArray ( notice ) )
notice = [ notice ] ;
2016-10-18 00:35:31 -04:00
var nd = ( notice [ 0 ] === - Infinity || notice [ 0 ] === - 1 ) ? 'unban' : ( notice [ 0 ] !== null && isFinite ( notice [ 0 ] ) ) ? utils . duration _string ( notice [ 0 ] , true ) : 'ban' ;
2016-10-14 20:43:34 -04:00
duration _tip . push ( utils . sanitize ( mod _id ) + ' - ' + nd + ( notice [ 1 ] ? ': ' + utils . sanitize ( notice [ 1 ] ) : '' ) ) ;
}
return ( is _me ? 'You have' : '<span data-user="' + utils . quote _san ( username ) + '" class="ban-target html-tooltip" title="' + utils . quote _attr ( name [ 1 ] || '' ) + '">' + name [ 0 ] + '</span> has' ) +
' been ' + ( duration _tip . length ? '<span class="ban-tip html-tooltip" title="' + utils . quote _attr ( duration _tip . join ( '<br>' ) ) + '">' : '' ) + ( duration === - Infinity ? 'unbanned' :
( duration === 1 ? 'purged' : isFinite ( duration ) ? 'timed out for ' + utils . duration _string ( duration ) : 'banned' ) ) +
( count > 1 ? ' (' + utils . number _commas ( count ) + ' times)' : '' ) +
( moderators && moderators . length ? ' by ' + utils . sanitize ( moderators . join ( ', ' ) ) : '' ) + ( duration _tip . length ? '</span>' : '' ) +
( reasons && reasons . length ? ' with reason' + utils . pluralize ( reasons . length ) + ': ' + utils . sanitize ( reasons . join ( ', ' ) ) : '.' ) ;
}
2016-09-09 17:34:20 -04:00
// --------------------
// PubSub is fucking awful
// --------------------
FFZ . prototype . _modify _chat _pubsub = function ( pubsub ) {
var f = this ;
pubsub . reopen ( {
setupService : function ( room _id , t ) {
var n = this ;
this . get ( "session" ) . withCurrentUser ( function ( user ) {
if ( n . isDestroyed )
return ;
var ps = n . _pubsub ( ) ,
token = user . chat _oauth _token ,
new _topics = [
"chat_message_updated." + room _id ,
2016-09-30 13:09:03 -04:00
"chat_moderator_actions." + user . id + "." + room _id ] ;
2016-09-09 17:34:20 -04:00
for ( var i = 0 ; i < new _topics . length ; i ++ )
ps . Listen ( {
topic : new _topics [ i ] ,
auth : token ,
success : function ( ) { } ,
2016-09-30 13:09:03 -04:00
failure : function ( t ) { f . log ( "[PubSub] Failed to listen to topic: " + new _topics [ i ] , t ) ; } ,
2016-09-09 17:34:20 -04:00
message : Ember . run . bind ( n , n . _onPubsubMessage , new _topics [ i ] )
} ) ;
if ( n . chatTopics )
n . chatTopics = n . chatTopics . concat ( new _topics ) ;
else
n . chatTopics = new _topics ;
ps . on ( "connected" , Ember . run . bind ( n , n . _onPubsubConnect ) ) ;
ps . on ( "disconnected" , Ember . run . bind ( n , n . _onPubsubDisconnect ) ) ;
t ( ) ;
} ) ;
} ,
tearDownService : function ( room _id ) {
if ( ! this . chatTopics )
return ;
var ps = this . _pubsub ( ) ,
old _topics ;
if ( ! room _id )
room _id = this . get ( "ffz_teardown_target" ) ;
if ( room _id ) {
// Make sure it's a string.
room _id = '.' + room _id ;
old _topics = this . chatTopics . filter ( function ( x ) { return x . substr ( - room _id . length ) === room _id } ) ;
} else
old _topics = this . chatTopics ;
for ( var i = 0 ; i < old _topics . length ; i ++ ) {
2016-10-11 21:41:35 -04:00
var topic = old _topics [ i ] ;
// Try stupid stuff to remove duplicate events.
if ( topic . substr ( 0 , 23 ) === 'chat_moderator_actions.' )
ps . Unlisten ( {
topic : topic . split ( '.' , 2 ) . join ( '.' ) ,
success : function ( ) { } ,
failure : function ( ) { }
} ) ;
2016-09-09 17:34:20 -04:00
ps . Unlisten ( {
2016-10-11 21:41:35 -04:00
topic : topic ,
2016-09-09 17:34:20 -04:00
success : function ( ) { } ,
2016-10-11 21:41:35 -04:00
failure : function ( topic , t ) { f . log ( "[PubSub] Failed to unlisten to topic: " + topic , t ) ; } . bind ( this , topic )
2016-09-09 17:34:20 -04:00
} ) ;
this . chatTopics . removeObject ( old _topics [ i ] ) ;
}
if ( ! this . chatTopics . length )
this . chatTopics = null ;
} ,
_onPubsubMessage : function ( topic , e ) {
if ( this . isDestroyed )
return ;
var msg = JSON . parse ( e ) ,
msg _data = msg . data ,
msg _type = msg . type || msg _data . type ;
if ( msg _data )
msg _data . topic = topic ;
this . trigger ( msg _type , msg _data ) ;
}
} ) ;
if ( ! pubsub . chatTopics )
return ;
// Now that we've modified that, we need to re-listen to everything.
pubsub . get ( "session" ) . withCurrentUser ( function ( user ) {
if ( pubsub . isDestroyed )
return ;
var ps = pubsub . _pubsub ( ) ,
2016-10-14 20:43:34 -04:00
token = user . chat _oauth _token ,
internal _topics = ps . _client & ps . _client . _listens && ps . _client . _listens . _events || { } ;
2016-09-09 17:34:20 -04:00
for ( var i = 0 ; i < pubsub . chatTopics . length ; i ++ ) {
2016-10-11 21:41:35 -04:00
var topic = pubsub . chatTopics [ i ] ;
2016-09-09 17:34:20 -04:00
ps . Unlisten ( {
2016-10-11 21:41:35 -04:00
topic : topic ,
2016-09-09 17:34:20 -04:00
success : function ( ) { } ,
2016-10-11 21:41:35 -04:00
failure : function ( topic , t ) { f . log ( "[PubSub] Failed to unlisten to topic: " + topic , t ) ; } . bind ( this , topic )
2016-09-09 17:34:20 -04:00
} ) ;
2016-10-14 20:43:34 -04:00
// Find the event and manually remove our listeners. We still want the topic so
// we don't clean it up too much, but we need to get rid of the existing bound
// functions to avoid anything screwing up.
var it = internal _topics [ topic ] ;
if ( it && it . length )
it . splice ( 0 , it . length ) ;
// Now, register our own event handler.
2016-09-09 17:34:20 -04:00
ps . Listen ( {
2016-10-11 21:41:35 -04:00
topic : topic ,
2016-09-09 17:34:20 -04:00
auth : token ,
success : function ( ) { } ,
2016-10-11 21:41:35 -04:00
failure : function ( topic , t ) { f . log ( "[PubSub] Failed to listen to topic: " + topic , t ) ; } . bind ( this , topic ) ,
message : Ember . run . bind ( pubsub , pubsub . _onPubsubMessage , topic )
2016-09-09 17:34:20 -04:00
} ) ;
}
} ) ;
}
2015-07-04 17:06:36 -04:00
// --------------------
// View Customization
// --------------------
2016-07-13 02:06:50 -04:00
FFZ . prototype . modify _room _view = function ( view ) {
2015-07-04 17:06:36 -04:00
var f = this ;
2016-07-13 02:06:50 -04:00
utils . ember _reopen _view ( view , {
ffz _init : function ( ) {
2015-07-04 17:06:36 -04:00
f . _roomv = this ;
this . ffz _frozen = false ;
2016-06-22 14:23:03 -04:00
this . ffz _ctrl = false ;
2015-07-18 21:10:27 -04:00
// Fix scrolling.
this . _ffz _mouse _down = this . ffzMouseDown . bind ( this ) ;
if ( is _android )
// We don't unbind scroll because that messes with the scrollbar. ;_;
this . _$chatMessagesScroller . bind ( 'scroll' , this . _ffz _mouse _down ) ;
this . _$chatMessagesScroller . unbind ( 'mousedown' ) ;
this . _$chatMessagesScroller . bind ( 'mousedown' , this . _ffz _mouse _down ) ;
2015-07-04 17:06:36 -04:00
if ( f . settings . chat _hover _pause )
this . ffzEnableFreeze ( ) ;
if ( f . settings . room _status )
this . ffzUpdateStatus ( ) ;
var controller = this . get ( 'controller' ) ;
if ( controller ) {
controller . reopen ( {
2016-07-13 02:06:50 -04:00
calcRecipientEligibility : function ( e ) {
// Because this doesn't work properly with multiple channel rooms
// by default, do it ourselves.
2016-08-09 20:45:28 -04:00
if ( controller . get ( 'model.isGroupRoom' ) ) {
controller . set ( 'isRecipientBitsIneligible' , true ) ;
controller . set ( 'isBitsHelperShown' , false ) ;
controller . set ( 'minimumBits' , 0 ) ;
controller . set ( 'isBitsTooltipActive' , false ) ;
return ;
}
2016-07-13 02:06:50 -04:00
var id = controller . get ( 'model.roomProperties._id' ) ,
update = function ( data ) {
if ( controller . isDestroyed || controller . get ( 'model.roomProperties._id' ) !== id )
return ;
controller . set ( 'model._ffz_bits_eligibility' , data ) ;
controller . set ( 'isRecipientBitsIneligible' , ! data . eligible ) ;
controller . set ( 'isBitsHelperShown' , data . eligible ) ;
controller . set ( 'minimumBits' , data . minBits ) ;
if ( ! data . eligible )
controller . set ( 'isBitsTooltipActive' , false ) ;
} ;
if ( id === undefined )
return ;
var data = controller . get ( 'model._ffz_bits_eligibility' ) ;
if ( data === undefined )
controller . get ( 'bits' ) . loadRecipientEligibility ( id ) . then ( update ) ;
else
update ( data ) ;
} ,
2015-07-04 17:06:36 -04:00
submitButtonText : function ( ) {
if ( this . get ( "model.isWhisperMessage" ) && this . get ( "model.isWhispersEnabled" ) )
return i18n ( "Whisper" ) ;
var wait = this . get ( "model.slowWait" ) ,
msg = this . get ( "model.messageToSend" ) || "" ;
if ( ( msg . charAt ( 0 ) === "/" && msg . substr ( 0 , 4 ) !== "/me " ) || ! wait || ! f . settings . room _status )
return i18n ( "Chat" ) ;
return utils . time _to _string ( wait , false , false , true ) ;
} . property ( "model.isWhisperMessage" , "model.isWhispersEnabled" , "model.slowWait" )
} ) ;
Ember . propertyDidChange ( controller , 'submitButtonText' ) ;
}
} ,
2016-07-13 02:06:50 -04:00
ffz _destroy : function ( ) {
2015-07-04 17:06:36 -04:00
if ( f . _roomv === this )
f . _roomv = undefined ;
2015-11-14 23:52:49 -05:00
if ( this . _ffz _chat _display )
this . _ffz _chat _display = undefined ;
2015-07-04 17:06:36 -04:00
this . ffzDisableFreeze ( ) ;
} ,
2016-09-09 17:34:20 -04:00
2016-06-22 14:23:03 -04:00
ffzOnKey : function ( event ) {
this . ffz _ctrl = event . ctrlKey ;
this . ffz _alt = event . altKey ;
this . ffz _shift = event . shiftKey ;
this . ffz _meta = event . metaKey ;
var cmi = f . settings . chat _mod _icon _visibility ;
if ( ! this . _ffz _outside && cmi > 1 )
this . get ( 'element' ) . classList . toggle ( 'show-mod-icons' ,
cmi === 2 ? this . ffz _ctrl :
cmi === 3 ? this . ffz _meta :
cmi === 4 ? this . ffz _alt :
this . ffz _shift ) ;
if ( this . _ffz _outside || f . settings . chat _hover _pause < 2 )
return ;
// Okay, so at this point we should change the state of the freeze?
var should _freeze = this . ffzShouldBeFrozen ( ) ,
freeze _change = this . ffz _frozen !== should _freeze ;
if ( freeze _change )
if ( should _freeze )
this . ffzFreeze ( ) ;
else
this . ffzUnfreeze ( ) ;
} ,
2015-07-04 17:06:36 -04:00
ffzUpdateStatus : function ( ) {
var room = this . get ( 'controller.model' ) ,
el = this . get ( 'element' ) ,
cont = el && el . querySelector ( '.chat-buttons-container' ) ;
if ( ! cont )
2015-07-13 21:52:44 -04:00
return ;
2015-07-04 17:06:36 -04:00
2016-05-06 02:23:12 -04:00
var btn = cont . querySelector ( 'button' ) ;
2015-07-04 17:06:36 -04:00
if ( f . has _bttv || ! f . settings . room _status ) {
2016-05-06 02:23:12 -04:00
jQuery ( ".ffz.room-state" , cont ) . remove ( ) ;
2015-07-04 17:06:36 -04:00
if ( btn )
btn . classList . remove ( 'ffz-waiting' ) ;
return ;
2016-05-06 02:23:12 -04:00
} else if ( btn ) {
btn . classList . toggle ( 'ffz-waiting' , ( room && room . get ( 'slowWait' ) || 0 ) ) ;
2016-05-12 00:11:50 -04:00
btn . classList . toggle ( 'ffz-banned' , ( room && room . get ( 'ffz_banned' ) || false ) ) ;
2015-07-04 17:06:36 -04:00
}
2016-07-13 02:06:50 -04:00
var badge , id , info , vis _count = 0 , label ;
2016-05-06 02:23:12 -04:00
for ( var i = 0 ; i < STATUS _BADGES . length ; i ++ ) {
info = STATUS _BADGES [ i ] ;
id = 'ffz-stat-' + info [ 0 ] ;
badge = cont . querySelector ( '#' + id ) ;
visible = typeof info [ 1 ] === "function" ? info [ 1 ] . call ( f , room ) : room && room . get ( info [ 1 ] ) ;
2016-05-20 17:30:34 -04:00
if ( typeof visible === "string" )
visible = visible === "1" ;
2016-07-13 02:06:50 -04:00
label = typeof info [ 3 ] === "function" ? info [ 3 ] . call ( f , room ) : undefined ;
2016-05-06 02:23:12 -04:00
if ( ! badge ) {
2016-07-13 02:06:50 -04:00
badge = utils . createElement ( 'span' , 'ffz room-state stat float-right' , ( label || info [ 0 ] ) . charAt ( 0 ) . toUpperCase ( ) + '<span>' + ( label || info [ 0 ] ) . substr ( 1 ) . toUpperCase ( ) + '</span>' ) ;
2016-05-06 02:23:12 -04:00
badge . id = id ;
2016-09-30 13:09:03 -04:00
jQuery ( badge ) . tipsy ( { html : true , gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'se' ) } ) ;
2016-05-06 02:23:12 -04:00
cont . appendChild ( badge ) ;
}
2015-08-09 23:32:29 -07:00
2016-07-13 02:06:50 -04:00
if ( label )
badge . innerHTML = ( label || info [ 0 ] ) . charAt ( 0 ) . toUpperCase ( ) + '<span>' + ( label || info [ 0 ] ) . substr ( 1 ) . toUpperCase ( ) + '</span>' ;
2016-05-06 02:23:12 -04:00
badge . title = typeof info [ 2 ] === "function" ? info [ 2 ] . call ( f , room ) : info [ 2 ] ;
badge . classList . toggle ( 'hidden' , ! visible ) ;
2016-09-30 13:09:03 -04:00
badge . classList . toggle ( 'faded' , info [ 4 ] !== undefined ? typeof info [ 4 ] === "function" ? info [ 4 ] . call ( f , room ) : info [ 4 ] : false ) ;
2016-05-06 02:23:12 -04:00
if ( visible )
vis _count ++ ;
2015-11-01 17:28:19 -05:00
}
2015-08-14 13:21:57 -04:00
2016-05-06 02:23:12 -04:00
jQuery ( ".ffz.room-state" , cont ) . toggleClass ( "truncated" , vis _count > 3 ) ;
2015-07-04 17:06:36 -04:00
} . observes ( 'controller.model' ) ,
ffzEnableFreeze : function ( ) {
var el = this . get ( 'element' ) ,
messages = el . querySelector ( '.chat-messages' ) ;
if ( ! messages )
return ;
this . _ffz _messages = messages ;
2015-08-14 13:21:57 -04:00
2016-10-16 15:05:11 -04:00
if ( ! this . _ffz _interval )
this . _ffz _interval = setInterval ( this . ffzPulse . bind ( this ) , 200 ) ;
if ( ! this . _ffz _mouse _move ) {
this . _ffz _mouse _move = this . ffzMouseMove . bind ( this ) ;
messages . addEventListener ( 'mousemove' , this . _ffz _mouse _move ) ;
messages . addEventListener ( 'touchmove' , this . _ffz _mouse _move ) ;
}
if ( ! this . _ffz _mouse _out ) {
this . _ffz _mouse _out = this . ffzMouseOut . bind ( this ) ;
messages . addEventListener ( 'mouseout' , this . _ffz _mouse _out ) ;
}
2015-07-04 17:06:36 -04:00
2016-10-16 15:05:11 -04:00
// Monitor the Ctrl key.
if ( ! this . _ffz _keyw ) {
this . _ffz _keyw = this . ffzOnKey . bind ( this ) ;
document . body . addEventListener ( 'keydown' , this . _ffz _keyw ) ;
document . body . addEventListener ( 'keyup' , this . _ffz _keyw ) ;
}
2015-07-04 17:06:36 -04:00
} ,
ffzDisableFreeze : function ( ) {
if ( this . _ffz _interval ) {
clearInterval ( this . _ffz _interval ) ;
this . _ffz _interval = undefined ;
}
this . ffzUnfreeze ( ) ;
var messages = this . _ffz _messages ;
if ( ! messages )
return ;
this . _ffz _messages = undefined ;
if ( this . _ffz _mouse _move ) {
messages . removeEventListener ( 'mousemove' , this . _ffz _mouse _move ) ;
2016-07-13 02:31:26 -04:00
messages . removeEventListener ( 'touchmove' , this . _ffz _mouse _move ) ;
2015-07-04 17:06:36 -04:00
this . _ffz _mouse _move = undefined ;
}
if ( this . _ffz _mouse _out ) {
messages . removeEventListener ( 'mouseout' , this . _ffz _mouse _out ) ;
this . _ffz _mouse _out = undefined ;
}
2016-10-16 15:05:11 -04:00
if ( this . _ffz _keyw ) {
document . body . removeEventListener ( 'keydown' , this . _ffz _keyw ) ;
document . body . removeEventListener ( 'keyup' , this . _ffz _keyw ) ;
this . _ffz _keyw = undefined ;
}
2015-07-04 17:06:36 -04:00
} ,
ffzPulse : function ( ) {
2016-06-22 14:23:03 -04:00
if ( this . ffz _frozen && ! this . ffzShouldBeFrozen ( ) )
this . ffzUnfreeze ( ) ;
2015-07-04 17:06:36 -04:00
} ,
2015-11-14 23:52:49 -05:00
ffzUnfreeze : function ( from _stuck ) {
2015-07-04 17:06:36 -04:00
this . ffz _frozen = false ;
this . _ffz _last _move = 0 ;
this . ffzUnwarnPaused ( ) ;
2015-11-14 23:52:49 -05:00
if ( ! from _stuck && this . get ( 'stuckToBottom' ) )
2015-07-04 17:06:36 -04:00
this . _scrollToBottom ( ) ;
} ,
2016-06-22 14:23:03 -04:00
ffzFreeze : function ( ) {
this . ffz _frozen = true ;
if ( this . get ( 'stuckToBottom' ) ) {
this . set ( 'controller.model.messageBufferSize' , f . settings . scrollback _length + 150 ) ;
this . ffzWarnPaused ( ) ;
}
} ,
2015-07-13 21:52:44 -04:00
ffzMouseDown : function ( event ) {
var t = this . _$chatMessagesScroller ;
2015-07-29 01:03:10 -04:00
if ( t && t [ 0 ] && ( ( ! this . ffz _frozen && "mousedown" === event . type ) || "mousewheel" === event . type || ( is _android && "scroll" === event . type ) ) ) {
2015-07-13 21:52:44 -04:00
var r = t [ 0 ] . scrollHeight - t [ 0 ] . scrollTop - t [ 0 ] . offsetHeight ;
this . _setStuckToBottom ( 10 >= r ) ;
}
} ,
2015-07-04 17:06:36 -04:00
ffzMouseOut : function ( event ) {
this . _ffz _outside = true ;
var e = this ;
2015-07-06 00:09:21 -04:00
setTimeout ( function ( ) {
2016-06-22 14:23:03 -04:00
if ( e . _ffz _outside ) {
if ( f . settings . chat _mod _icon _visibility > 1 )
e . get ( 'element' ) . classList . toggle ( 'show-mod-icons' , false ) ;
2015-07-04 17:06:36 -04:00
e . ffzUnfreeze ( ) ;
2016-06-22 14:23:03 -04:00
}
2015-07-06 00:09:21 -04:00
} , 25 ) ;
2015-07-04 17:06:36 -04:00
} ,
2016-06-22 14:23:03 -04:00
ffzShouldBeFrozen : function ( since ) {
if ( since === undefined )
since = Date . now ( ) - this . _ffz _last _move ;
var hp = f . settings . chat _hover _pause ;
return ( this . ffz _ctrl && ( hp === 2 || hp === 6 ) ) || ( this . ffz _meta && ( hp === 3 || hp === 7 ) ) || ( this . ffz _alt && ( hp === 4 || hp === 8 ) ) || ( this . ffz _shift && ( hp === 5 || hp === 9 ) ) || ( since < 750 && ( hp === 1 || hp > 5 ) ) ;
} ,
2015-07-04 17:06:36 -04:00
ffzMouseMove : function ( event ) {
2016-06-22 14:23:03 -04:00
// Store the last move time.
2015-07-04 17:06:36 -04:00
this . _ffz _last _move = Date . now ( ) ;
this . _ffz _outside = false ;
2016-06-22 14:23:03 -04:00
// If nothing of interest has happened, stop.
if ( event . altKey === this . ffz _alt && event . shiftKey === this . ffz _shift && event . ctrlKey === this . ffz _ctrl && event . metaKey === this . ffz _meta && event . screenX === this . _ffz _last _screenx && event . screenY === this . _ffz _last _screeny )
2015-07-04 17:06:36 -04:00
return ;
2016-06-22 14:23:03 -04:00
// Grab a bit of state.
this . ffz _ctrl = event . ctrlKey ;
this . ffz _alt = event . altKey ;
this . ffz _shift = event . shiftKey ;
this . ffz _meta = event . metaKey ;
2015-07-04 17:06:36 -04:00
this . _ffz _last _screenx = event . screenX ;
this . _ffz _last _screeny = event . screenY ;
2016-06-22 14:23:03 -04:00
var cmi = f . settings . chat _mod _icon _visibility ;
if ( ! this . _ffz _outside && cmi > 1 )
this . get ( 'element' ) . classList . toggle ( 'show-mod-icons' ,
cmi === 2 ? this . ffz _ctrl :
cmi === 3 ? this . ffz _meta :
cmi === 4 ? this . ffz _alt :
this . ffz _shift ) ;
// Should the state have changed?
var should _freeze = this . ffzShouldBeFrozen ( ) ,
freeze _change = this . ffz _frozen !== should _freeze ;
if ( freeze _change )
if ( should _freeze )
this . ffzFreeze ( ) ;
else
this . ffzUnfreeze ( ) ;
2015-07-04 17:06:36 -04:00
} ,
_scrollToBottom : _ . throttle ( function ( ) {
var e = this ,
s = this . _$chatMessagesScroller ;
2016-09-30 13:09:03 -04:00
//this.runTask(function() {
Ember . run . next ( function ( ) {
2016-06-02 20:04:40 -04:00
// Trying random performance tweaks for fun and profit!
( window . requestAnimationFrame || setTimeout ) ( function ( ) {
2015-07-29 01:03:10 -04:00
if ( e . ffz _frozen || ! s || ! s . length )
return ;
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
s [ 0 ] . scrollTop = s [ 0 ] . scrollHeight ;
e . _setStuckToBottom ( true ) ;
2015-07-04 17:06:36 -04:00
} )
} )
} , 200 ) ,
_setStuckToBottom : function ( val ) {
this . set ( "stuckToBottom" , val ) ;
2016-09-30 13:09:03 -04:00
var model = this . get ( "controller.model" ) ;
if ( model )
model . messageBufferSize = f . settings . scrollback _length + ( val ? 0 : 150 ) ;
2015-07-13 21:52:44 -04:00
if ( ! val )
2015-11-14 23:52:49 -05:00
this . ffzUnfreeze ( true ) ;
2015-07-04 17:06:36 -04:00
} ,
// Warnings~!
ffzWarnPaused : function ( ) {
var el = this . get ( 'element' ) ,
warning = el && el . querySelector ( '.chat-interface .more-messages-indicator.ffz-freeze-indicator' ) ;
if ( ! el )
return ;
if ( ! warning ) {
warning = document . createElement ( 'div' ) ;
warning . className = 'more-messages-indicator ffz-freeze-indicator' ;
2016-06-22 14:23:03 -04:00
var hp = f . settings . chat _hover _pause ,
label = hp === 2 ? 'Ctrl Key' :
hp === 3 ? ( constants . META _NAME + ' Key' ) :
hp === 4 ? 'Alt Key' :
hp === 5 ? 'Shift Key' :
hp === 6 ? 'Ctrl or Mouse' :
hp === 7 ? ( constants . META _NAME + ' or Mouse' ) :
hp === 8 ? 'Alt or Mouse' :
hp === 9 ? 'Shift or Mouse' :
'Mouse Movement' ;
warning . innerHTML = '(Chat Paused Due to ' + label + ')' ;
2015-07-04 17:06:36 -04:00
var cont = el . querySelector ( '.chat-interface' ) ;
if ( ! cont )
return ;
cont . insertBefore ( warning , cont . childNodes [ 0 ] )
}
warning . classList . remove ( 'hidden' ) ;
} ,
ffzUnwarnPaused : function ( ) {
var el = this . get ( 'element' ) ,
warning = el && el . querySelector ( '.chat-interface .more-messages-indicator.ffz-freeze-indicator' ) ;
if ( warning )
warning . classList . add ( 'hidden' ) ;
}
} ) ;
2015-01-20 01:53:18 -05:00
}
// --------------------
// Command System
// --------------------
FFZ . chat _commands = { } ;
2015-02-10 01:34:23 -05:00
FFZ . ffz _commands = { } ;
2015-01-20 01:53:18 -05:00
FFZ . prototype . room _message = function ( room , text ) {
if ( this . has _bttv ) {
2016-09-30 13:09:03 -04:00
var lines = text . split ( "\n" ) ;
2015-01-20 01:53:18 -05:00
for ( var i = 0 ; i < lines . length ; i ++ )
BetterTTV . chat . handlers . onPrivmsg ( room . id , { style : 'admin' , date : new Date ( ) , from : 'jtv' , message : lines [ i ] } ) ;
2016-09-30 13:09:03 -04:00
} else
room . room . addMessage ( { ffz _line _returns : true , style : 'ffz admin' , date : new Date ( ) , from : 'FFZ' , message : text } ) ;
2015-01-20 01:53:18 -05:00
}
FFZ . prototype . run _command = function ( text , room _id ) {
2015-02-10 01:34:23 -05:00
var room = this . rooms [ room _id ] ;
if ( ! room || ! room . room )
return false ;
if ( ! text )
return ;
var args = text . split ( " " ) ,
cmd = args . shift ( ) . substr ( 1 ) . toLowerCase ( ) ,
command = FFZ . chat _commands [ cmd ] ,
output ;
if ( ! command )
return false ;
if ( command . hasOwnProperty ( 'enabled' ) ) {
var val = command . enabled ;
if ( typeof val == "function" ) {
try {
2016-03-23 19:28:22 -04:00
val = command . enabled . call ( this , room , args ) ;
2015-02-10 01:34:23 -05:00
} catch ( err ) {
2016-10-12 16:42:43 -04:00
this . error ( 'command "' + cmd + '" enabled' , err ) ;
2015-02-10 01:34:23 -05:00
val = false ;
}
}
if ( ! val )
return false ;
}
this . log ( "Received Command: " + cmd , args , true ) ;
try {
2016-03-23 19:28:22 -04:00
output = command . call ( this , room , args ) ;
2015-02-10 01:34:23 -05:00
} catch ( err ) {
this . error ( 'command "' + cmd + '" runner: ' + err ) ;
output = "There was an error running the command." ;
}
if ( output )
this . room _message ( room , output ) ;
return true ;
}
FFZ . prototype . run _ffz _command = function ( text , room _id ) {
2015-01-20 01:53:18 -05:00
var room = this . rooms [ room _id ] ;
if ( ! room || ! room . room )
return ;
if ( ! text ) {
// Try to pop-up the menu.
var link = document . querySelector ( 'a.ffz-ui-toggle' ) ;
if ( link )
return link . click ( ) ;
text = "help" ;
}
var args = text . split ( " " ) ,
cmd = args . shift ( ) . toLowerCase ( ) ;
this . log ( "Received Command: " + cmd , args , true ) ;
2015-02-10 01:34:23 -05:00
var command = FFZ . ffz _commands [ cmd ] , output ;
2015-01-20 01:53:18 -05:00
if ( command ) {
try {
2016-03-23 19:28:22 -04:00
output = command . call ( this , room , args ) ;
2015-01-20 01:53:18 -05:00
} catch ( err ) {
this . log ( "Error Running Command - " + cmd + ": " + err , room ) ;
output = "There was an error running the command." ;
}
} else
output = 'There is no "' + cmd + '" command.' ;
if ( output )
this . room _message ( room , output ) ;
}
2015-02-10 01:34:23 -05:00
FFZ . ffz _commands . help = function ( room , args ) {
2015-01-20 01:53:18 -05:00
if ( args && args . length ) {
2015-02-10 01:34:23 -05:00
var command = FFZ . ffz _commands [ args [ 0 ] . toLowerCase ( ) ] ;
2015-01-20 01:53:18 -05:00
if ( ! command )
return 'There is no "' + args [ 0 ] + '" command.' ;
else if ( ! command . help )
return 'No help is available for the command "' + args [ 0 ] + '".' ;
else
return command . help ;
}
var cmds = [ ] ;
2015-02-10 01:34:23 -05:00
for ( var c in FFZ . ffz _commands )
FFZ . ffz _commands . hasOwnProperty ( c ) && cmds . push ( c ) ;
2015-01-20 01:53:18 -05:00
return "The available commands are: " + cmds . join ( ", " ) ;
}
2015-02-10 01:34:23 -05:00
FFZ . ffz _commands . help . help = "Usage: /ffz help [command]\nList available commands, or show help for a specific command." ;
2015-01-20 01:53:18 -05:00
// --------------------
// Room Management
// --------------------
2016-05-22 14:08:11 -04:00
FFZ . prototype . update _room _important = function ( id , controller ) {
var Chat = controller || utils . ember _lookup ( 'controller:chat' ) ,
2016-07-13 02:06:50 -04:00
current _room = Chat && Chat . get ( 'currentChannelRoom' ) ,
2016-05-22 14:08:11 -04:00
room = this . rooms [ id ] ;
if ( ! room )
return ;
2016-07-13 02:06:50 -04:00
room . important = ( room . room && current _room === room . room ) || ( current _room && current _room . ffz _host _target === id ) || ( room . room && room . room . get ( 'isGroupRoom' ) ) || ( this . settings . pinned _rooms . indexOf ( id ) !== - 1 ) ;
2016-05-22 14:08:11 -04:00
} ;
2016-10-18 14:06:20 -04:00
FFZ . prototype . _update _room _badge _css = function ( room _id ) {
var room = this . rooms [ room _id ] ,
badges = room && room . badges || { } ,
output = [ ] ;
for ( var badge _id in badges ) {
var versions = badges [ badge _id ] && badges [ badge _id ] . versions || { } ;
for ( var version in versions )
output . push ( utils . room _badge _css ( room _id , badge _id , version , versions [ version ] ) ) ;
}
utils . update _css ( this . _badge _style , 'twitch-room-' + room _id , output . join ( '' ) ) ;
}
2016-10-20 16:41:04 -04:00
FFZ . prototype . add _room = function ( room _id , room ) {
if ( this . rooms [ room _id ] )
return this . log ( "Tried to add existing room: " + room _id ) ;
2015-01-20 01:53:18 -05:00
2016-10-20 16:41:04 -04:00
this . log ( "Adding Room: " + room _id ) ;
2015-01-20 01:53:18 -05:00
// Create a basic data table for this room.
2016-10-20 16:41:04 -04:00
var data = this . rooms [ room _id ] = { id : room _id , room : room , sets : [ ] , ext _sets : [ ] , css : null , needs _history : false } ;
2015-01-20 01:53:18 -05:00
2016-10-20 16:41:04 -04:00
if ( this . follow _sets && this . follow _sets [ room _id ] ) {
data . extra _sets = this . follow _sets [ room _id ] ;
delete this . follow _sets [ room _id ] ;
2015-07-04 17:06:36 -04:00
for ( var i = 0 ; i < data . extra _sets . length ; i ++ ) {
var sid = data . extra _sets [ i ] ,
set = this . emote _sets && this . emote _sets [ sid ] ;
if ( set ) {
2016-10-20 16:41:04 -04:00
if ( set . users . indexOf ( room _id ) === - 1 )
set . users . push ( room _id ) ;
2015-07-04 17:06:36 -04:00
continue ;
}
this . load _set ( sid , function ( success , data ) {
if ( success )
2016-10-20 16:41:04 -04:00
data . users . push ( room _id ) ;
2015-07-04 17:06:36 -04:00
} ) ;
}
}
2016-10-16 15:05:11 -04:00
// Store the badges for this room now if we have them.
var bs = utils . ember _lookup ( 'service:badges' ) ;
2016-10-20 16:41:04 -04:00
if ( bs && bs . badgeCollection && bs . badgeCollection . channel && bs . badgeCollection . channel . broadcasterName === room _id ) {
2016-10-16 15:05:11 -04:00
data . badges = bs . badgeCollection . channel ;
2016-10-20 16:41:04 -04:00
this . _update _room _badge _css ( room _id ) ;
2016-10-18 14:06:20 -04:00
}
2016-10-16 15:05:11 -04:00
2016-10-05 01:31:10 -04:00
// Look up if the room has moderation logs.
var f = this ;
2016-10-20 16:41:04 -04:00
this . ws _send ( "has_logs" , room _id , function ( success , response ) {
2016-10-05 01:31:10 -04:00
if ( ! success )
return ;
data . has _logs = response . has _logs ;
data . log _source = response . log _source ;
} , true ) ;
2016-05-22 14:08:11 -04:00
// Is the room important?
2016-10-20 16:41:04 -04:00
this . update _room _important ( room _id ) ;
2016-05-22 14:08:11 -04:00
2016-10-20 16:41:04 -04:00
if ( data . important )
2016-05-22 14:08:11 -04:00
// Let the server know where we are.
2016-10-20 16:41:04 -04:00
this . ws _sub ( "room." + room _id ) ;
2015-01-20 01:53:18 -05:00
2016-05-22 14:08:11 -04:00
// Do we want history?
2016-09-30 13:09:03 -04:00
/ * i f ( ! t h i s . h a s _ b t t v & & t h i s . s e t t i n g s . c h a t _ h i s t o r y & & r o o m & & ( r o o m . g e t ( ' m e s s a g e s . l e n g t h ' ) | | 0 ) < 1 0 ) {
2016-05-22 14:08:11 -04:00
if ( ! this . ws _send ( "chat_history" , [ id , 25 ] , this . _load _history . bind ( this , id ) ) )
data . needs _history = true ;
2016-09-30 13:09:03 -04:00
} * /
2015-06-10 18:46:04 -04:00
2016-05-22 14:08:11 -04:00
2015-07-04 17:06:36 -04:00
// Why don't we set the scrollback length, too?
2016-10-20 16:41:04 -04:00
room . set ( 'messageBufferSize' , this . settings . scrollback _length + ( ( this . _roomv && ! this . _roomv . get ( 'stuckToBottom' ) && this . _roomv . get ( 'controller.model.id' ) === room _id ) ? 150 : 0 ) ) ;
2015-07-04 17:06:36 -04:00
2015-10-24 22:44:00 -04:00
// Load the room's data from the API.
2016-10-20 16:41:04 -04:00
this . load _room ( room _id ) ;
2015-10-24 22:44:00 -04:00
// Announce this room to any extension callback functions.
2016-10-20 16:41:04 -04:00
this . api _trigger ( 'room-add' , room _id ) ;
2015-01-20 01:53:18 -05:00
}
2016-10-20 16:41:04 -04:00
FFZ . prototype . remove _room = function ( room _id ) {
var room = this . rooms [ room _id ] ;
2015-01-20 01:53:18 -05:00
if ( ! room )
return ;
2016-10-20 16:41:04 -04:00
this . log ( "Removing Room: " + room _id ) ;
2015-01-20 01:53:18 -05:00
// Remove the CSS
if ( room . css || room . moderator _badge )
2016-10-20 16:41:04 -04:00
utils . update _css ( this . _room _style , room _id , null ) ;
2015-01-20 01:53:18 -05:00
// Let the server know we're gone and delete our data for this room.
2016-10-20 16:41:04 -04:00
this . ws _unsub ( "room." + room _id ) ;
delete this . rooms [ room _id ] ;
2015-01-20 01:53:18 -05:00
// Clean up sets we aren't using any longer.
2015-06-05 03:59:28 -04:00
var set = this . emote _sets [ room . set ] ;
if ( set ) {
2016-10-20 16:41:04 -04:00
set . users . removeObject ( room _id ) ;
2015-06-05 03:59:28 -04:00
if ( ! this . global _sets . contains ( room . set ) && ! set . users . length )
this . unload _set ( room . set ) ;
2015-01-20 01:53:18 -05:00
}
2016-10-20 02:34:55 -04:00
2016-10-20 16:41:04 -04:00
this . api _trigger ( 'room-remove' , room _id ) ;
2015-01-20 01:53:18 -05:00
}
// --------------------
// Receiving Set Info
// --------------------
2015-06-05 03:59:28 -04:00
FFZ . prototype . load _room = function ( room _id , callback , tries ) {
var f = this ;
2015-12-12 13:28:35 -05:00
jQuery . getJSON ( constants . API _SERVER + "v1/room/" + room _id )
2015-06-05 03:59:28 -04:00
. done ( function ( data ) {
if ( data . sets ) {
for ( var key in data . sets )
data . sets . hasOwnProperty ( key ) && f . _load _set _json ( key , undefined , data . sets [ key ] ) ;
}
f . _load _room _json ( room _id , callback , data ) ;
} ) . fail ( function ( data ) {
if ( data . status == 404 )
return typeof callback == "function" && callback ( false ) ;
tries = ( tries || 0 ) + 1 ;
if ( tries < 10 )
return f . load _room ( room _id , callback , tries ) ;
return typeof callback == "function" && callback ( false ) ;
} ) ;
2015-01-20 01:53:18 -05:00
}
FFZ . prototype . _load _room _json = function ( room _id , callback , data ) {
2015-06-05 03:59:28 -04:00
if ( ! data || ! data . room )
return typeof callback == "function" && callback ( false ) ;
data = data . room ;
2016-10-05 01:31:10 -04:00
// Apply the data we've received to the room data model.
var model = this . rooms [ room _id ] = this . rooms [ room _id ] || { } ;
for ( var key in data )
if ( key !== 'room' && data . hasOwnProperty ( key ) )
model [ key ] = data [ key ] ;
2015-01-20 01:53:18 -05:00
// Preserve the pointer to the Room instance.
2016-10-05 01:31:10 -04:00
/ * i f ( t h i s . r o o m s [ r o o m _ i d ] )
2015-01-20 01:53:18 -05:00
data . room = this . rooms [ room _id ] . room ;
2015-07-04 17:06:36 -04:00
// Preserve everything else.
for ( var key in this . rooms [ room _id ] ) {
if ( key !== 'room' && this . rooms [ room _id ] . hasOwnProperty ( key ) && ! data . hasOwnProperty ( key ) )
data [ key ] = this . rooms [ room _id ] [ key ] ;
}
2015-06-10 18:46:04 -04:00
data . needs _history = this . rooms [ room _id ] && this . rooms [ room _id ] . needs _history || false ;
2016-10-05 01:31:10 -04:00
this . rooms [ room _id ] = data ; * /
2015-01-20 01:53:18 -05:00
2016-10-05 01:31:10 -04:00
if ( model . css || model . moderator _badge )
utils . update _css ( this . _room _style , room _id , moderator _css ( model ) + ( model . css || "" ) ) ;
2015-01-20 01:53:18 -05:00
2016-10-05 01:31:10 -04:00
if ( ! this . emote _sets . hasOwnProperty ( model . set ) )
this . load _set ( model . set , function ( success , set ) {
2015-07-04 17:06:36 -04:00
if ( set . users . indexOf ( room _id ) === - 1 )
set . users . push ( room _id ) ;
} ) ;
2016-10-05 01:31:10 -04:00
else if ( this . emote _sets [ model . set ] . users . indexOf ( room _id ) === - 1 )
this . emote _sets [ model . set ] . users . push ( room _id ) ;
2015-01-20 01:53:18 -05:00
this . update _ui _link ( ) ;
2016-10-05 01:31:10 -04:00
if ( model . set )
this . rerender _feed _cards ( model . set ) ;
2016-04-11 18:57:25 -04:00
2015-01-20 01:53:18 -05:00
if ( callback )
2016-10-05 01:31:10 -04:00
callback ( true , model ) ;
2015-01-20 01:53:18 -05:00
}
// --------------------
// Ember Modifications
// --------------------
FFZ . prototype . _modify _room = function ( room ) {
var f = this ;
room . reopen ( {
2015-07-04 17:06:36 -04:00
slowWaiting : false ,
2015-07-29 01:03:10 -04:00
slow : 0 ,
2015-07-04 17:06:36 -04:00
2016-05-12 00:11:50 -04:00
ffz _banned : false ,
2015-07-06 00:09:21 -04:00
mru _list : [ ] ,
2016-10-16 15:05:11 -04:00
ffzUpdateBadges : function ( ) {
2016-10-20 02:34:55 -04:00
if ( this . get ( 'isGroupRoom' ) )
return ;
2016-10-16 15:05:11 -04:00
var room _name = this . get ( 'id' ) ,
room _id = this . get ( 'roomProperties._id' ) ,
ffz _room = f . rooms && f . rooms [ room _name ] ;
if ( ! ffz _room || ! room _id || ffz _room . badges )
return ;
fetch ( "https://badges.twitch.tv/v1/badges/channels/" + room _id + "/display?language=" + ( Twitch . receivedLanguage || "en" ) , {
headers : {
'Client-ID' : constants . CLIENT _ID
}
} ) . then ( utils . json ) . then ( function ( data ) {
ffz _room . badges = data && data . badge _sets ;
2016-10-18 14:06:20 -04:00
f . _update _room _badge _css ( room _name ) ;
2016-10-16 15:05:11 -04:00
} ) ;
} . observes ( 'roomProperties._id' ) ,
2016-05-13 23:56:59 -04:00
updateWait : function ( value , was _banned , update ) {
2015-07-04 17:06:36 -04:00
var wait = this . get ( 'slowWait' ) || 0 ;
this . set ( 'slowWait' , value ) ;
if ( wait < 1 && value > 0 ) {
2015-07-13 21:52:44 -04:00
if ( this . _ffz _wait _timer )
clearTimeout ( this . _ffz _wait _timer ) ;
this . _ffz _wait _timer = setTimeout ( this . ffzUpdateWait . bind ( this ) , 1000 ) ;
2016-05-13 23:56:59 -04:00
! update && f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
2015-07-04 17:06:36 -04:00
} else if ( ( wait > 0 && value < 1 ) || was _banned ) {
this . set ( 'ffz_banned' , false ) ;
2016-05-13 23:56:59 -04:00
! update && f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
2015-07-04 17:06:36 -04:00
}
} ,
ffzUpdateWait : function ( ) {
2015-07-13 21:52:44 -04:00
this . _ffz _wait _timer = undefined ;
2015-07-04 17:06:36 -04:00
var wait = this . get ( 'slowWait' ) || 0 ;
if ( wait < 1 )
return ;
this . set ( 'slowWait' , -- wait ) ;
if ( wait > 0 )
2015-07-13 21:52:44 -04:00
this . _ffz _wait _timer = setTimeout ( this . ffzUpdateWait . bind ( this ) , 1000 ) ;
2015-07-04 17:06:36 -04:00
else {
this . set ( 'ffz_banned' , false ) ;
f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
}
} ,
2015-12-12 13:28:35 -05:00
ffzScheduleDestroy : function ( ) {
if ( this . _ffz _destroy _timer )
return ;
var t = this ;
this . _ffz _destroy _timer = setTimeout ( function ( ) {
t . _ffz _destroy _timer = null ;
t . ffzCheckDestroy ( ) ;
} , 5000 ) ;
} ,
ffzCheckDestroy : function ( ) {
2016-03-23 20:23:04 -04:00
var Chat = utils . ember _lookup ( 'controller:chat' ) ,
2015-12-12 13:28:35 -05:00
user = f . get _user ( ) ,
room _id = this . get ( 'id' ) ;
2016-08-09 20:45:28 -04:00
// Don't destroy the room if it's still relevant.
if ( ( Chat && Chat . get ( 'currentChannelRoom' ) === this ) || ( user && user . login === room _id ) || ( f . _chatv && f . _chatv . _ffz _host === room _id ) || ( f . settings . pinned _rooms && f . settings . pinned _rooms . indexOf ( room _id ) !== - 1 ) )
return ;
2015-12-12 13:28:35 -05:00
this . destroy ( ) ;
} ,
2015-07-04 17:06:36 -04:00
ffzUpdateStatus : function ( ) {
if ( f . _roomv )
f . _roomv . ffzUpdateStatus ( ) ;
2016-03-23 19:28:22 -04:00
} . observes ( 'r9k' , 'subsOnly' , 'emoteOnly' , 'slow' , 'ffz_banned' ) ,
2015-07-29 01:03:10 -04:00
2016-05-06 02:23:12 -04:00
2015-07-29 01:03:10 -04:00
// User Level
ffzUserLevel : function ( ) {
if ( this . get ( 'isStaff' ) )
return 5 ;
else if ( this . get ( 'isAdmin' ) )
return 4 ;
else if ( this . get ( 'isBroadcaster' ) )
return 3 ;
else if ( this . get ( 'isGlobalModerator' ) )
return 2 ;
else if ( this . get ( 'isModerator' ) )
return 1 ;
return 0 ;
} . property ( 'id' , 'chatLabels.[]' ) ,
2015-07-04 17:06:36 -04:00
2015-01-20 20:25:26 -05:00
// Track which rooms the user is currently in.
2015-01-20 01:53:18 -05:00
init : function ( ) {
this . _super ( ) ;
2015-08-09 23:32:29 -07:00
2015-02-10 01:34:23 -05:00
try {
f . add _room ( this . id , this ) ;
2015-06-05 03:59:28 -04:00
this . set ( "ffz_chatters" , { } ) ;
2016-07-13 02:06:50 -04:00
this . set ( "ffz_ids" , this . get ( 'ffz_ids' ) || { } ) ;
2016-09-09 17:34:20 -04:00
this . set ( "ffz_last_notices" , this . get ( 'ffz_last_notices' ) || { } ) ;
2015-02-10 01:34:23 -05:00
} catch ( err ) {
f . error ( "add_room: " + err ) ;
}
2015-01-20 01:53:18 -05:00
} ,
willDestroy : function ( ) {
2016-09-09 17:34:20 -04:00
this . get ( "pubsub" ) . set ( "ffz_teardown_target" , this . get ( 'roomProperties._id' ) ) ;
2015-01-20 01:53:18 -05:00
this . _super ( ) ;
2016-09-09 17:34:20 -04:00
this . get ( "pubsub" ) . set ( "ffz_teardown_target" , null ) ;
2015-08-09 23:32:29 -07:00
2015-02-10 01:34:23 -05:00
try {
f . remove _room ( this . id ) ;
} catch ( err ) {
f . error ( "remove_room: " + err ) ;
}
2015-01-20 01:53:18 -05:00
} ,
2016-09-09 17:34:20 -04:00
addChannelModerationMessage : function ( event ) {
// Throw out messages that are for other rooms.
var room _id = '.' + this . get ( "roomProperties._id" ) ;
if ( event . topic && event . topic . substr ( - room _id . length ) !== room _id || event . created _by === this . get ( "session.userData.login" ) )
return ;
2016-09-30 13:09:03 -04:00
if ( ! f . settings . get _twitch ( 'showModerationActions' ) )
return ;
2016-09-09 17:34:20 -04:00
var target _notice = NOTICE _MAPPING [ event . moderation _action ] ;
if ( target _notice ) {
var last _notice = this . ffz _last _notices && this . ffz _last _notices [ target _notice ] ;
if ( last _notice && ! last _notice . has _owner ) {
last _notice . message += ' (By: ' + event . created _by + ')' ;
last _notice . has _owner = true ;
last _notice . cachedTokens = undefined ;
if ( last _notice . _line )
2016-09-30 13:09:03 -04:00
Ember . propertyDidChange ( last _notice . _line , 'ffzTokenizedMessage' ) ;
2016-09-09 17:34:20 -04:00
} else {
var waiting = this . ffz _waiting _notices = this . ffz _waiting _notices || { } ;
waiting [ target _notice ] = event . created _by ;
}
2016-09-30 13:09:03 -04:00
} else
2016-09-09 17:34:20 -04:00
this . _super ( event ) ;
} ,
addLoginModerationMessage : function ( event ) {
2016-10-14 20:43:34 -04:00
// Throw out messages that are for other rooms or that don't have topics.
2016-09-09 17:34:20 -04:00
var room _id = '.' + this . get ( "roomProperties._id" ) ;
2016-10-14 20:43:34 -04:00
if ( ! event . topic || event . topic . substr ( - room _id . length ) !== room _id || event . created _by === this . get ( "session.userData.login" ) )
2016-09-09 17:34:20 -04:00
return ;
2016-10-14 20:43:34 -04:00
//f.log("Login Moderation for " + this.get('id') + ' [' + room_id + ']', event);
2016-09-30 13:09:03 -04:00
2016-09-09 17:34:20 -04:00
// In case we get unexpected input, do the other thing.
2016-10-22 17:27:13 -04:00
if ( f . has _bttv || [ "ban" , "unban" , "timeout" ] . indexOf ( event . moderation _action ) === - 1 )
2016-09-09 17:34:20 -04:00
return this . _super ( event ) ;
2016-10-14 20:43:34 -04:00
var msg _id ,
reason = event . args [ 2 ] ,
2016-10-18 00:35:31 -04:00
duration = event . moderation _action === 'unban' ? - Infinity : event . args [ 1 ] ;
2016-10-14 20:43:34 -04:00
if ( typeof duration === "string" )
duration = parseInt ( duration ) ;
2016-09-09 17:34:20 -04:00
2016-10-14 20:43:34 -04:00
if ( isNaN ( duration ) )
duration = Infinity ;
if ( reason ) {
var match = constants . UUID _TEST . exec ( reason ) ;
if ( match ) {
msg _id = match [ 1 ] ;
reason = reason . substr ( 0 , reason . length - match [ 0 ] . length ) ;
if ( ! reason . length )
reason = null ;
}
}
this . addBanNotice ( event . args [ 0 ] . toLowerCase ( ) , duration , reason , event . created _by , msg _id , true ) ;
2016-09-09 17:34:20 -04:00
} ,
2016-10-14 20:43:34 -04:00
addBanNotice : function ( username , duration , reason , moderator , msg _id , report _only ) {
var current _user = f . get _user ( ) ,
is _me = current _user && current _user . login === username ,
show _notice = is _me || this . ffzShouldDisplayNotice ( ) ,
show _reason = is _me || this . get ( 'isModeratorOrHigher' ) ,
show _moderator = f . settings . get _twitch ( 'showModerationActions' ) ,
now = new Date ,
room _id = this . get ( 'id' ) ,
ffz _room = f . rooms [ room _id ] ,
ban _history , last _ban ;
// Find an existing ban to modify.
if ( ffz _room ) {
var ban _history = ffz _room . ban _history = ffz _room . ban _history || { } ;
last _ban = ban _history [ username ] ;
// Only overwrite bans in the last 15 seconds.
if ( ! last _ban || Math . abs ( now - last _ban . date ) > 15000 )
last _ban = null ;
}
// If we have an existing ban, modify that.
if ( last _ban ) {
if ( reason && last _ban . reasons . indexOf ( reason ) === - 1 )
last _ban . reasons . push ( reason ) ;
if ( moderator && last _ban . moderators . indexOf ( moderator ) === - 1 )
last _ban . moderators . push ( moderator ) ;
if ( moderator )
last _ban . notices [ moderator ] = [ duration , reason ] ;
if ( ! report _only )
last _ban . count ++ ;
// Don't update the displayed duration if the new end time is within five
// seconds to avoid changing messages when bots do multiple timeouts.
var end _time = now . getTime ( ) + ( duration * 1000 ) ;
if ( Math . abs ( end _time - last _ban . end _time ) > 5000 ) {
last _ban . duration = duration ;
last _ban . end _time = end _time ;
} else
duration = last _ban . duration ;
last _ban . message = f . format _ban _notice ( username , is _me , duration , last _ban . count , show _reason && last _ban . reasons , show _moderator && last _ban . moderators , show _moderator && last _ban . notices ) ;
last _ban . cachedTokens = [ { type : "raw" , html : last _ban . message } ] ;
if ( last _ban . _line )
Ember . propertyDidChange ( last _ban . _line , 'ffzTokenizedMessage' ) ;
} else {
var notices = { } ;
if ( moderator )
notices [ moderator ] = [ duration , reason ] ;
var count = report _only ? 0 : 1 ,
msg = f . format _ban _notice ( username , is _me , duration , count , show _reason && reason && [ reason ] , show _moderator && moderator && [ moderator ] , show _moderator && notices ) ,
message = {
style : 'admin' ,
date : now ,
room : room _id ,
ffz _ban _target : username ,
reasons : reason ? [ reason ] : [ ] ,
moderators : moderator ? [ moderator ] : [ ] ,
notices : notices ,
duration : duration ,
end _time : now . getTime ( ) + ( duration * 1000 ) ,
count : count ,
message : msg ,
cachedTokens : [ { type : "raw" , html : msg } ]
} ;
if ( ban _history )
ban _history [ username ] = message ;
if ( show _notice )
this . addMessage ( message ) ;
this . addUserHistory ( message ) ;
}
} ,
addUserHistory : function ( message ) {
var room _id = this . get ( 'id' ) ,
ffz _room = f . rooms [ room _id ] ;
if ( ! ffz _room || ! f . settings . mod _card _history )
return ;
var username = message . ffz _ban _target || message . from ,
historical = message . tags && message . tags . historical ,
chat _history = ffz _room . user _history = ffz _room . user _history || { } ,
user _history = chat _history [ username ] = chat _history [ username ] || [ ] ;
if ( historical ) {
if ( user _history . length >= 20 )
return ;
user _history . unshift ( message ) ;
} else {
user _history . push ( message ) ;
while ( user _history . length > 20 )
user _history . shift ( ) ;
}
if ( f . _mod _card && f . _mod _card . ffz _room _id === room _id && f . _mod _card . get ( 'cardInfo.user.id' ) === username ) {
var el = f . _mod _card . get ( 'element' ) ,
history = el && el . querySelector ( '.chat-history.live-history' ) ;
if ( history ) {
var was _at _top = history . scrollTop >= ( history . scrollHeight - history . clientHeight ) ,
line = f . _build _mod _card _history ( message , f . _mod _card ) ;
if ( historical )
history . insertBefore ( line , history . firstElementChild ) ;
else
history . appendChild ( line ) ;
if ( was _at _top )
setTimeout ( function ( ) { history . scrollTop = history . scrollHeight } ) ;
if ( history . childElementCount > 20 )
history . removeChild ( history . firstElementChild ) ;
}
}
} ,
2016-05-13 23:56:59 -04:00
2016-10-14 20:43:34 -04:00
clearMessages : function ( user , tags , disable _log ) {
var t = this ;
2015-06-10 18:46:04 -04:00
if ( user ) {
2016-05-20 17:30:34 -04:00
var duration = Infinity ,
2016-05-13 23:56:59 -04:00
reason = undefined ,
2016-09-09 17:34:20 -04:00
moderator = undefined ,
2016-07-13 02:06:50 -04:00
msg _id = undefined ,
2016-05-13 23:56:59 -04:00
current _user = f . get _user ( ) ,
is _me = current _user && current _user . login === user ;
// Read the ban duration and reason from the message tags.
2016-09-09 17:34:20 -04:00
if ( tags && tags [ 'ban-duration' ] ) {
duration = tags [ 'ban-duration' ] ;
if ( typeof duration === 'string' )
duration = parseInt ( duration ) ;
2016-05-20 17:30:34 -04:00
2016-09-09 17:34:20 -04:00
if ( isNaN ( duration ) )
duration = Infinity ;
}
2016-05-13 23:56:59 -04:00
if ( tags && tags [ 'ban-reason' ] && ( is _me || t . get ( 'isModeratorOrHigher' ) ) )
reason = tags [ 'ban-reason' ] ;
2016-09-09 17:34:20 -04:00
if ( tags && tags [ 'ban-moderator' ] && ( is _me || t . get ( 'isModeratorOrHigher' ) ) )
moderator = tags [ 'ban-moderator' ] ;
2016-07-13 02:06:50 -04:00
// Is there a UUID on the end of the ban reason?
if ( reason ) {
var match = constants . UUID _TEST . exec ( reason ) ;
if ( match ) {
msg _id = match [ 1 ] ;
reason = reason . substr ( 0 , reason . length - match [ 0 ] . length ) ;
if ( ! reason . length )
reason = undefined ;
}
}
2016-05-13 23:56:59 -04:00
// If we were banned, set the state and update the UI.
if ( is _me ) {
t . set ( 'ffz_banned' , true ) ;
2016-10-14 20:43:34 -04:00
if ( duration )
if ( isFinite ( duration ) )
t . updateWait ( duration ) ;
else {
t . set ( 'slowWait' , 0 ) ;
f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
}
2016-05-13 23:56:59 -04:00
}
// Mark the user as recently banned.
if ( ! t . ffzRecentlyBanned )
t . ffzRecentlyBanned = [ ] ;
t . ffzRecentlyBanned . push ( user ) ;
while ( t . ffzRecentlyBanned . length > 100 )
t . ffzRecentlyBanned . shift ( ) ;
2016-07-13 02:06:50 -04:00
// Are we deleting a specific message?
if ( msg _id && this . ffz _ids ) {
var msg = this . ffz _ids [ msg _id ] ;
if ( msg && msg . from === user ) {
msg . ffz _deleted = true ;
if ( ! f . settings . prevent _clear )
msg . deleted = true ;
if ( f . settings . remove _deleted )
if ( msg . pending )
msg . removed = true ;
else {
var msgs = t . get ( 'messages' ) ,
total = msgs . get ( 'length' ) ,
i = total ;
while ( i -- ) {
var msg = msgs . get ( i ) ;
if ( msg . tags && msg . tags . id === msg _id ) {
msgs . removeAt ( i ) ;
delete this . ffz _ids [ msg _id ] ;
2016-09-09 17:34:20 -04:00
var notice _type = msg . tags && msg . tags [ 'msg-id' ] ;
if ( notice _type && this . ffz _last _notices && this . ffz _last _notices [ notice _type ] === msg )
delete this . ffz _last _notices [ notice _type ] ;
2016-07-13 02:06:50 -04:00
break ;
}
}
}
2015-08-14 13:21:57 -04:00
2016-07-13 02:06:50 -04:00
if ( msg . _line ) {
Ember . propertyDidChange ( msg . _line , 'msgObject.ffz_deleted' ) ;
Ember . propertyDidChange ( msg . _line , 'msgObject.deleted' ) ;
2015-07-29 01:03:10 -04:00
}
2015-08-14 13:21:57 -04:00
2016-07-13 02:06:50 -04:00
} else if ( msg . from !== user )
f . log ( "Banned Message ID #" + msg _id + " not owned by: " + user ) ;
else
f . log ( "Banned Message ID #" + msg _id + " not found in chat." ) ;
2015-07-04 17:06:36 -04:00
2016-07-13 02:06:50 -04:00
} else {
// Delete all messages from this user / chat.
// Delete Visible Messages
var msgs = t . get ( 'messages' ) ,
total = msgs . get ( 'length' ) ,
i = total ,
removed = 0 ;
2016-05-13 23:56:59 -04:00
2015-08-09 23:32:29 -07:00
while ( i -- ) {
var msg = msgs . get ( i ) ;
2016-07-13 02:06:50 -04:00
if ( msg . from === user ) {
if ( f . settings . remove _deleted ) {
// Remove this message from the ID tracker.
2016-09-09 17:34:20 -04:00
var msg _id = msg . tags && msg . tags . id ,
notice _type = msg . tags && msg . tags [ 'msg-id' ] ;
if ( msg _id && this . ffz _ids && this . ffz _ids [ msg _id ] )
delete this . ffz _ids [ msg _id ] ;
if ( notice _type && this . ffz _last _notices && this . ffz _last _notices [ notice _type ] === msg )
delete this . ffz _last _notices [ notice _type ] ;
2016-07-13 02:06:50 -04:00
msgs . removeAt ( i ) ;
removed ++ ;
continue ;
}
t . set ( 'messages.' + i + '.ffz_deleted' , true ) ;
if ( ! f . settings . prevent _clear )
t . set ( 'messages.' + i + '.deleted' , true ) ;
}
}
// Delete Panding Messages
if ( t . ffzPending ) {
msgs = t . ffzPending ;
i = msgs . length ;
while ( i -- ) {
var msg = msgs . get ( i ) ;
if ( msg . from !== user ) continue ;
msg . ffz _deleted = true ;
msg . deleted = ! f . settings . prevent _clear ;
msg . removed = f . settings . remove _deleted ;
}
2015-08-09 23:32:29 -07:00
}
}
2016-10-14 20:43:34 -04:00
if ( ! disable _log )
this . addBanNotice ( user , duration , reason , null , msg _id ) ;
2016-05-20 17:30:34 -04:00
2015-06-10 18:46:04 -04:00
} else {
if ( f . settings . prevent _clear )
2016-10-14 20:43:34 -04:00
t . addMessage ( {
style : 'admin' ,
message : "A moderator's attempt to clear chat was ignored." ,
tags : {
'msg-id' : 'clear_chat'
}
} ) ;
2015-06-10 18:46:04 -04:00
else {
var msgs = t . get ( "messages" ) ;
t . set ( "messages" , [ ] ) ;
t . addMessage ( {
style : 'admin' ,
2016-10-14 20:43:34 -04:00
message : i18n ( "Chat was cleared by a moderator" ) ,
tags : {
'msg-id' : 'clear_chat'
}
2015-06-10 18:46:04 -04:00
} ) ;
}
}
} ,
2015-08-09 23:32:29 -07:00
// Artificial chat delay
2016-08-09 20:45:28 -04:00
ffz _chat _delay : function ( ) {
var val = f . settings . chat _delay ;
if ( val !== - 1 )
return val ;
val = this . get ( 'roomProperties.chat_delay_duration' ) ;
return ( Number . isNaN ( val ) || ! Number . isFinite ( val ) || this . get ( 'isModeratorOrHigher' ) ) ? 0 : val ;
} . property ( 'roomProperties.chat_delay_duration' , 'isModeratorOrHigher' ) ,
ffz _update _display : function ( ) {
if ( f . _roomv )
f . _roomv . ffzUpdateStatus ( ) ;
} . observes ( 'roomProperties.chat_delay_duration' ) ,
2015-06-10 18:46:04 -04:00
pushMessage : function ( msg ) {
2016-08-09 20:45:28 -04:00
if ( f . settings . chat _batching !== 0 || this . get ( 'ffz_chat_delay' ) !== 0 || ( this . ffzPending && this . ffzPending . length ) ) {
2015-08-14 13:21:57 -04:00
if ( ! this . ffzPending )
2015-08-09 23:32:29 -07:00
this . ffzPending = [ ] ;
2015-11-19 02:45:56 -05:00
var now = msg . time = Date . now ( ) ;
2016-07-13 02:06:50 -04:00
msg . pending = true ;
2015-08-09 23:32:29 -07:00
this . ffzPending . push ( msg ) ;
2015-08-14 13:21:57 -04:00
this . ffzSchedulePendingFlush ( now ) ;
2015-08-09 23:32:29 -07:00
} else {
2016-09-30 13:09:03 -04:00
this . ffzPushMessages ( [ msg ] ) ;
}
} ,
ffzPushMessages : function ( messages ) {
var new _messages = [ ] ,
new _unread = 0 ;
for ( var i = 0 ; i < messages . length ; i ++ ) {
var msg = messages [ i ] ;
if ( this . shouldShowMessage ( msg ) && this . ffzShouldShowMessage ( msg ) ) {
new _messages . push ( msg ) ;
if ( ! ( msg . tags && msg . tags . historical ) && msg . style !== "admin" && msg . style !== "whisper" ) {
if ( msg . ffz _has _mention )
this . ffz _last _mention = Date . now ( ) ;
new _unread ++ ;
}
}
}
if ( ! new _messages . length )
return ;
var room _messages = this . get ( "messages" ) ,
2016-10-11 21:41:35 -04:00
raw _remove = room _messages . length + new _messages . length > this . messageBufferSize ?
Math . max ( 0 , room _messages . length - this . messageBufferSize ) + new _messages . length : 0 ,
to _remove = raw _remove - raw _remove % 2 ,
2016-10-14 20:43:34 -04:00
removed = room _messages . slice ( 0 , to _remove ) ,
2016-10-11 21:41:35 -04:00
trimmed = room _messages . slice ( to _remove , room _messages . length ) ;
2016-09-30 13:09:03 -04:00
2016-10-14 20:43:34 -04:00
// Garbage collect removed messages.
for ( var i = 0 ; i < removed . length ; i ++ ) {
var msg = removed [ i ] ,
msg _id = msg . tags && msg . tags . id ,
notice _type = msg . tags && msg . tags [ 'msg-id' ] ;
if ( msg _id && this . ffz _ids && this . ffz _ids [ msg _id ] )
delete this . ffz _ids [ msg _id ] ;
if ( notice _type && this . ffz _last _notices && this . ffz _last _notices [ notice _type ] === msg )
delete this . ffz _last _notices [ notice _type ] ;
}
2016-09-30 13:09:03 -04:00
var earliest _message ;
for ( var i = 0 ; i < trimmed . length ; i ++ )
if ( trimmed [ i ] . date ) {
earliest _message = trimmed [ i ] . date ;
break ;
}
for ( var i = 0 ; i < new _messages . length ; i ++ ) {
var msg = new _messages [ i ] ;
if ( msg . tags && msg . tags . historical ) {
// Add a warning about really old messages.
if ( earliest _message ) {
var age = earliest _message - msg . date ;
if ( age > 300000 )
trimmed . unshift ( {
color : '#755000' ,
date : msg . date ,
from : 'frankerfacez_admin' ,
style : 'admin' ,
message : '(Last message is ' + utils . human _time ( age / 1000 ) + ' old.)' ,
room : msg . room ,
from _server : true
} ) ;
}
trimmed . unshift ( msg ) ;
earliest _message = null ;
} else
trimmed . push ( msg ) ;
}
this . set ( "messages" , trimmed ) ;
if ( new _unread ) {
this . incrementProperty ( "unreadCount" , new _unread ) ;
this . ffz _last _activity = Date . now ( ) ;
2015-08-09 23:32:29 -07:00
}
} ,
2015-08-14 13:21:57 -04:00
ffzSchedulePendingFlush : function ( now ) {
// Instead of just blindly looping every x seconds, we want to calculate the time until
// the next message should be displayed, and then set the timeout for that. We'll
// end up looping a bit more frequently, but it'll make chat feel more responsive.
2015-11-01 17:28:19 -05:00
// If we have a pending flush, don't reschedule. It wouldn't change.
2015-08-14 13:21:57 -04:00
if ( this . _ffz _pending _flush )
2015-11-01 17:28:19 -05:00
return ;
2015-08-14 13:21:57 -04:00
if ( this . ffzPending && this . ffzPending . length ) {
2015-11-01 17:28:19 -05:00
// We need either the amount of chat delay past the first message, if chat_delay is on, or the
// amount of time from the last batch.
2015-11-19 02:45:56 -05:00
now = now || Date . now ( ) ;
2016-06-02 20:04:40 -04:00
var t = this ,
2016-08-09 20:45:28 -04:00
chat _delay = this . get ( 'ffz_chat_delay' ) ,
2016-06-02 20:04:40 -04:00
delay = Math . max (
2016-08-09 20:45:28 -04:00
( chat _delay !== 0 ? 50 + Math . max ( 0 , ( chat _delay + ( this . ffzPending [ 0 ] . time || 0 ) ) - now ) : 0 ) ,
2015-11-19 02:45:56 -05:00
( f . settings . chat _batching !== 0 ? Math . max ( 0 , f . settings . chat _batching - ( now - ( this . _ffz _last _batch || 0 ) ) ) : 0 ) ) ;
2015-11-01 17:28:19 -05:00
2015-08-14 13:21:57 -04:00
this . _ffz _pending _flush = setTimeout ( this . ffzPendingFlush . bind ( this ) , delay ) ;
}
} ,
2015-08-09 23:32:29 -07:00
ffzPendingFlush : function ( ) {
2015-08-14 13:21:57 -04:00
this . _ffz _pending _flush = null ;
2016-08-09 20:45:28 -04:00
var now = this . _ffz _last _batch = Date . now ( ) ,
2016-09-30 13:09:03 -04:00
chat _delay = this . get ( 'ffz_chat_delay' ) ,
to _display = [ ] ;
2015-11-01 17:28:19 -05:00
2015-08-09 23:32:29 -07:00
for ( var i = 0 , l = this . ffzPending . length ; i < l ; i ++ ) {
var msg = this . ffzPending [ i ] ;
2016-07-13 02:06:50 -04:00
if ( msg . removed ) {
// Don't keep this message ID around.
2016-09-09 17:34:20 -04:00
var msg _id = msg && msg . tags && msg . tags . id ,
notice _type = msg && msg . tags && msg . tags [ 'msg-id' ] ;
2016-07-13 02:06:50 -04:00
if ( msg _id && this . ffz _ids && this . ffz _ids [ msg _id ] )
delete this . ffz _ids [ msg _id ] ;
2016-09-09 17:34:20 -04:00
if ( notice _type && this . ffz _last _notices && this . ffz _last _notices [ notice _type ] === msg )
delete this . ffz _last _notices [ notice _type ] ;
2015-08-14 13:21:57 -04:00
continue ;
2016-07-13 02:06:50 -04:00
}
2015-08-14 13:21:57 -04:00
2016-08-09 20:45:28 -04:00
if ( chat _delay !== 0 && ( chat _delay + msg . time > now ) )
2015-08-14 13:21:57 -04:00
break ;
2016-07-13 02:06:50 -04:00
msg . pending = false ;
2016-09-30 13:09:03 -04:00
to _display . push ( msg ) ;
2015-08-09 23:32:29 -07:00
}
2016-09-30 13:09:03 -04:00
this . ffzPushMessages ( to _display ) ;
2015-08-14 13:21:57 -04:00
this . ffzPending = this . ffzPending . slice ( i ) ;
this . ffzSchedulePendingFlush ( now ) ;
2015-08-09 23:32:29 -07:00
} ,
ffzShouldShowMessage : function ( msg ) {
2015-11-19 02:45:56 -05:00
if ( ! f . settings . hosted _sub _notices && msg . style === 'notification' && HOSTED _SUB . test ( msg . message ) )
return false ;
2015-08-09 23:32:29 -07:00
if ( f . settings . remove _bot _ban _notices && this . ffzRecentlyBanned ) {
var banned = '(' + this . ffzRecentlyBanned . join ( '|' ) + ')' ;
var bots = {
'nightbot' : '^' + banned ,
'moobot' : '\\(' + banned + '\\)' ,
'xanbot' : '^' + banned ,
} ;
if ( msg . from in bots && ( new RegExp ( bots [ msg . from ] ) ) . test ( msg . message ) ) {
return false ;
}
}
return true ;
} ,
2016-05-13 23:56:59 -04:00
ffzShouldDisplayNotice : function ( ) {
return f . settings . timeout _notices === 2 || ( f . settings . timeout _notices === 1 && this . get ( 'isModeratorOrHigher' ) ) ;
} ,
addNotification : function ( msg ) {
if ( msg ) {
// We don't want to display these notices because we're injecting our own messages.
if ( ( msg . msgId === 'timeout_success' || msg . msgId === 'ban_success' ) && this . ffzShouldDisplayNotice ( ) )
return ;
2016-09-30 13:09:03 -04:00
// f.log("Notification", msg);
2016-09-09 17:34:20 -04:00
if ( ! msg . tags )
msg . tags = { } ;
if ( ! msg . tags [ 'msg-id' ] )
msg . tags [ 'msg-id' ] = msg . msgId ;
if ( ! msg . style )
msg . style = 'admin' ;
if ( this . ffz _waiting _notices && this . ffz _waiting _notices [ msg . msgId ] ) {
msg . has _owner = true ;
msg . message += ' (By: ' + this . ffz _waiting _notices [ msg . msgId ] + ')' ;
delete this . ffz _waiting _notices [ msg . msgId ] ;
}
return this . addMessage ( msg ) ;
2016-05-13 23:56:59 -04:00
}
} ,
2016-07-13 02:06:50 -04:00
onMessage : function ( msg ) {
// We do our own batching. With blackjack, and hookers. You know what? Forget the batching.
this . addMessage ( msg ) ;
} ,
2016-09-30 13:09:03 -04:00
ffzProcessMessage : function ( msg ) {
2015-07-29 01:03:10 -04:00
if ( msg ) {
2016-09-09 17:34:20 -04:00
var notice _type = msg . tags && msg . tags [ 'msg-id' ] ,
is _resub = notice _type === 'resub' ,
2016-07-13 02:06:50 -04:00
room _id = this . get ( 'id' ) ,
msg _id = msg . tags && msg . tags . id ;
2016-06-22 14:23:03 -04:00
// Ignore resubs in other rooms.
if ( is _resub && ! f . settings . hosted _sub _notices && ( msg . tags [ 'room-id' ] != this . get ( 'roomProperties._id' ) || HOSTED _SUB . test ( msg . tags [ 'system-msg' ] ) ) )
return ;
// Split this into two messages if requested.
if ( is _resub && f . settings . old _sub _notices ) {
2016-10-16 15:05:11 -04:00
var message = msg . tags [ 'system-msg' ] ,
months = /for (\d+) months/ . exec ( message ) ;
2016-06-22 14:23:03 -04:00
this . addMessage ( {
style : "notification" ,
from : "twitchnotify" ,
date : msg . date || new Date ,
room : room _id ,
2016-10-16 15:05:11 -04:00
message : message ,
ffz _sub _months : months && parseInt ( months [ 1 ] )
2016-06-22 14:23:03 -04:00
} ) ;
// If there's no message just quit now.
if ( ! msg . message )
return ;
// And delete the system message so it won't render weirdly.
msg . tags [ 'system-msg' ] = '' ;
}
2016-09-30 13:09:03 -04:00
// Fix dates for historical messages.
if ( ! msg . date && msg . tags && msg . tags [ 'tmi-sent-ts' ] ) {
var sent = parseInt ( msg . tags [ 'tmi-sent-ts' ] ) ;
if ( sent && ! isNaN ( sent ) && isFinite ( sent ) )
msg . date = new Date ( sent ) ;
}
2015-07-29 01:03:10 -04:00
var is _whisper = msg . style === 'whisper' ;
2015-11-10 21:37:30 -05:00
// Ignore whispers if conversations are enabled.
2016-09-09 17:34:20 -04:00
if ( is _whisper ) {
var conv _enabled = utils . ember _lookup ( 'controller:application' ) . get ( 'isConversationsEnabled' ) ;
if ( conv _enabled || ( ! conv _enabled && f . settings . hide _whispers _in _embedded _chat ) )
return ;
}
2015-06-10 18:46:04 -04:00
2015-07-29 01:03:10 -04:00
if ( ! is _whisper )
2016-06-22 14:23:03 -04:00
msg . room = room _id ;
2015-07-29 01:03:10 -04:00
2015-08-21 19:00:48 -04:00
// Look up color and labels.
if ( this . tmiRoom && msg . from ) {
if ( ! msg . color )
msg . color = msg . tags && msg . tags . color ? msg . tags . color : this . tmiSession . getColor ( msg . from . toLowerCase ( ) ) ;
if ( ! msg . labels )
msg . labels = this . tmiRoom . getLabels ( msg . from ) ;
}
2016-10-14 20:43:34 -04:00
// Tag the broadcaster.
if ( room _id === msg . from )
msg . tags . mod = true ;
2015-07-29 01:03:10 -04:00
// Tokenization
f . tokenize _chat _line ( msg , false , this . get ( 'roomProperties.hide_chat_links' ) ) ;
2016-09-30 13:09:03 -04:00
// Check for a new subscription line that would need a chat badge.
2016-07-13 02:31:26 -04:00
if ( msg . from === 'twitchnotify' && msg . message . indexOf ( 'subscribed to' ) === - 1 && msg . message . indexOf ( 'subscribed' ) !== - 1 ) {
if ( ! msg . tags )
msg . tags = { } ;
2016-06-05 22:12:54 -04:00
if ( ! msg . tags . badges )
msg . tags . badges = { } ;
2016-10-16 15:05:11 -04:00
var ffz _room = f . rooms && f . rooms [ room _id ] ,
badges = ffz _room && ffz _room . badges || { } ,
sub _version = badges && badges . subscriber && badges . subscriber . versions ;
if ( ! isNaN ( msg . ffz _sub _months ) ) {
var months = msg . ffz _sub _months ;
msg . tags . badges . subscriber = ( months >= 24 && sub _version [ 24 ] ) ? 24 :
( months >= 12 && sub _version [ 12 ] ) ? 12 :
( months >= 6 && sub _version [ 6 ] ) ? 6 :
( months >= 3 && sub _version [ 3 ] ) ? 3 : 1 ;
} else
msg . tags . badges . subscriber = 1 ;
2016-07-13 02:31:26 -04:00
msg . tags . subscriber = true ;
if ( msg . labels && msg . labels . indexOf ( "subscriber" ) === - 1 )
msg . labels . push ( "subscriber" ) ;
}
2016-03-23 19:28:22 -04:00
2016-09-30 13:09:03 -04:00
// Color processing.
if ( msg . color )
f . _handle _color ( msg . color ) ;
return msg ;
}
} ,
addMessage : function ( msg ) {
msg = this . ffzProcessMessage ( msg ) ;
if ( ! msg )
return ;
var msg _id = msg . tags && msg . tags . id ,
notice _type = msg . tags && msg . tags [ 'msg-id' ] ,
is _whisper = msg . style === 'whisper' ;
// If this message is already in the room, discard the duplicate.
if ( msg _id && this . ffz _ids && this . ffz _ids [ msg _id ] )
return ;
2016-10-05 23:07:10 -04:00
// If it's historical, make sure it's for this room.
if ( msg . tags && msg . tags . historical && msg . tags [ 'room-id' ] != this . get ( 'roomProperties._id' ) )
return ;
2016-06-02 20:04:40 -04:00
2016-09-30 13:09:03 -04:00
// Keep the history.
2016-10-14 20:43:34 -04:00
if ( ! is _whisper && msg . from && msg . from !== 'jtv' && msg . from !== 'twitchnotify' )
this . addUserHistory ( {
from : msg . from ,
tags : {
id : msg . tags && msg . tags . id ,
'display-name' : msg . tags && msg . tags [ 'display-name' ] ,
bits : msg . tags && msg . tags . bits
} ,
message : msg . message ,
cachedTokens : msg . cachedTokens ,
style : msg . style ,
date : msg . date
} ) ;
2015-07-04 17:06:36 -04:00
2016-09-30 13:09:03 -04:00
// Clear the last ban for that user.
var f _room = f . rooms && f . rooms [ msg . room ] ,
ban _history = f _room && f _room . ban _history ;
2016-05-13 23:56:59 -04:00
2016-09-30 13:09:03 -04:00
if ( ban _history && msg . from ) {
// Is the last ban within 200ms? Chances are Twitch screwed up message order.
if ( ban _history [ msg . from ] && ( new Date - ban _history [ msg . from ] . date ) <= 200 ) {
msg . ffz _deleted = true ;
msg . deleted = ! f . settings . prevent _clear ;
2016-05-22 14:08:11 -04:00
2016-09-30 13:09:03 -04:00
} else
ban _history [ msg . from ] = false ;
}
2016-05-22 14:08:11 -04:00
2016-05-13 23:56:59 -04:00
2016-09-30 13:09:03 -04:00
// Check for message from us.
if ( ! is _whisper && ! msg . ffz _deleted ) {
var user = f . get _user ( ) ;
if ( user && user . login === msg . from ) {
var was _banned = this . get ( 'ffz_banned' ) ;
this . set ( 'ffz_banned' , false ) ;
2015-07-29 01:03:10 -04:00
2016-09-30 13:09:03 -04:00
// Update the wait time.
if ( this . get ( 'isSubscriber' ) || this . get ( 'isModeratorOrHigher' ) || ! this . get ( 'slowMode' ) )
this . updateWait ( 0 , was _banned )
else if ( this . get ( 'slowMode' ) )
this . updateWait ( this . get ( 'slow' ) ) ;
2015-06-05 03:59:28 -04:00
}
2015-07-29 01:03:10 -04:00
}
2015-07-18 21:10:27 -04:00
2015-07-29 01:03:10 -04:00
2016-05-06 02:23:12 -04:00
// Message Filtering
2016-10-20 02:34:55 -04:00
f . api _trigger ( 'room-message' , msg ) ;
2016-05-06 02:23:12 -04:00
2016-09-30 13:09:03 -04:00
// Also update chatters.
if ( ! is _whisper && this . chatters && ! this . chatters [ msg . from ] && msg . from !== 'twitchnotify' && msg . from !== 'jtv' )
this . ffzUpdateChatters ( msg . from ) ;
2016-07-13 02:06:50 -04:00
// We're past the last return, so store the message
// now that we know we're keeping it.
if ( msg _id ) {
var ids = this . ffz _ids = this . ffz _ids || { } ;
ids [ msg _id ] = msg ;
}
2016-09-09 17:34:20 -04:00
// If this is a notice, store that this is the last of its type.
if ( notice _type ) {
var ids = this . ffz _last _notices = this . ffz _last _notices || { } ;
ids [ notice _type ] = msg ;
}
2016-07-13 02:31:26 -04:00
// Report this message to the dashboard.
if ( window !== window . parent && parent . postMessage && msg . from && msg . from !== "jtv" && msg . from !== "twitchnotify" )
parent . postMessage ( { from _ffz : true , command : 'chat_message' , data : { from : msg . from , room : msg . room } } , "*" ) ; //location.protocol + "//www.twitch.tv/");
2016-03-23 19:28:22 -04:00
2016-09-30 13:09:03 -04:00
// Flagging for review.
if ( msg . tags && msg . tags . risk === "high" )
msg . flaggedForReview = true ;
// Add the message. We don't do super anymore because it does stupid stuff.'
this . pushMessage ( msg ) ;
msg . from && msg . style !== "admin" && msg . style !== "notification" && msg . tags && this . addChatter ( msg ) ;
this . trackLatency ( msg ) ;
//return this._super(msg);
2015-06-05 03:59:28 -04:00
} ,
setHostMode : function ( e ) {
2015-07-13 21:52:44 -04:00
this . set ( 'ffz_host_target' , e && e . hostTarget || null ) ;
var user = f . get _user ( ) ;
if ( user && f . _cindex && this . get ( 'id' ) === user . login )
f . _cindex . ffzUpdateHostButton ( ) ;
2015-08-14 13:21:57 -04:00
2016-10-01 13:43:08 -04:00
// If hosting is disabled, or this isn't the current channel room,
// ignore the host mode.
2016-03-23 20:23:04 -04:00
var Chat = utils . ember _lookup ( 'controller:chat' ) ;
2015-06-05 03:59:28 -04:00
if ( ! Chat || Chat . get ( 'currentChannelRoom' ) !== this )
return ;
2016-10-01 13:43:08 -04:00
var target = f . settings . hosted _channels ? ( e . hostTarget ? e . hostTarget . toLowerCase ( ) : null ) : null ,
channel = this . get ( "channel" ) ;
if ( channel ) {
var delay = 0 ;
if ( target && ! e . recentlyJoined ) {
var percentile = Math . max ( ( e . numViewers || 0 ) / . 5 , 4000 ) ;
delay = 3000 + Math . floor ( ( percentile || 0 ) * Math . random ( ) ) ;
}
if ( this . get ( "experiments.shouldSeeRedesign" ) ) {
var c = this . get ( "store" ) . peekRecord ( "channel" , channel . get ( "name" ) ) ;
if ( c ) {
if ( target )
this . pendingFetchHostModeTarget = Ember . run . debounce ( this , "fetchHostModeTarget" , {
currentChannel : c ,
targetName : target
} , delay ) ;
else
c . set ( "hostModeTarget" , null ) ;
}
} else channel . setHostMode ( {
target : target , delay : delay
} ) ;
}
2015-01-20 20:25:26 -05:00
} ,
2016-09-30 13:09:03 -04:00
send : function ( text , ignore _history , used _aliases ) {
2015-02-10 01:34:23 -05:00
try {
2015-12-12 13:28:35 -05:00
this . ffz _last _input = Date . now ( ) ;
2016-06-22 14:23:03 -04:00
var first _char = text . charAt ( 0 ) ,
is _cmd = first _char === '/' || first _char === '.' ;
// Strip trailing whitespace from commands.
if ( is _cmd )
text = text . replace ( /\s+$/ , '' ) ;
2015-10-17 18:05:44 -04:00
if ( text && ! ignore _history ) {
2015-07-06 00:09:21 -04:00
// Command History
var mru = this . get ( 'mru_list' ) ,
ind = mru . indexOf ( text ) ;
2015-08-14 13:21:57 -04:00
2015-07-06 00:09:21 -04:00
if ( ind !== - 1 )
mru . splice ( ind , 1 )
else if ( mru . length > 20 )
mru . pop ( ) ;
2015-08-14 13:21:57 -04:00
2015-07-06 00:09:21 -04:00
mru . unshift ( text ) ;
}
2015-08-14 13:21:57 -04:00
2016-09-30 13:09:03 -04:00
if ( is _cmd ) {
var cmd = text . substr ( 1 ) . split ( ' ' , 1 ) [ 0 ] . toLowerCase ( ) ,
was _handled = false ;
2015-02-10 01:34:23 -05:00
2016-09-30 13:09:03 -04:00
if ( cmd === "ffz" ) {
f . run _ffz _command ( text . substr ( 5 ) , this . get ( 'id' ) ) ;
was _handled = true ;
} else if ( f . _command _aliases [ cmd ] ) {
used _aliases = used _aliases || [ ] ;
if ( used _aliases . indexOf ( cmd ) !== - 1 ) {
f . room _message ( f . rooms [ this . get ( 'id' ) ] , "Error: Your command aliases are recursing. [Path: " + used _aliases . join ( ", " ) + "]" ) ;
was _handled = true ;
} else {
2016-10-12 16:42:43 -04:00
var alias = f . _command _aliases [ cmd ] [ 0 ] ,
2016-09-30 13:09:03 -04:00
args = text . substr ( 1 + cmd . length ) . trimLeft ( ) . split ( /\s+/g ) ,
output = utils . replace _cmd _variables ( alias , null , this , null , args ) ;
used _aliases . push ( cmd ) ;
this . set ( "messageToSend" , "" ) ;
var lines = output . split ( /\s*<LINE>\s*/g ) ;
for ( var i = 0 ; i < lines . length ; i ++ )
this . send ( lines [ i ] , true , used _aliases ) ;
return ;
}
} else if ( f . run _command ( text , this . get ( 'id' ) ) )
was _handled = true ;
if ( was _handled ) {
this . set ( "messageToSend" , "" ) ;
return ;
}
2015-02-10 01:34:23 -05:00
}
} catch ( err ) {
f . error ( "send: " + err ) ;
}
return this . _super ( text ) ;
2015-06-05 03:59:28 -04:00
} ,
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
ffzUpdateUnread : function ( ) {
2016-03-23 20:23:04 -04:00
var Chat = utils . ember _lookup ( 'controller:chat' ) ;
2015-12-12 13:28:35 -05:00
if ( Chat && Chat . get ( 'currentRoom' ) === this )
this . resetUnreadCount ( ) ;
else if ( f . _chatv )
f . _chatv . ffzUpdateUnread ( this . get ( 'id' ) ) ;
2015-06-05 03:59:28 -04:00
} . observes ( 'unreadCount' ) ,
ffzInitChatterCount : function ( ) {
if ( ! this . tmiRoom )
return ;
2015-07-29 01:03:10 -04:00
if ( this . _ffz _chatter _timer ) {
clearTimeout ( this . _ffz _chatter _timer ) ;
this . _ffz _chatter _timer = undefined ;
}
2015-06-05 03:59:28 -04:00
var room = this ;
this . tmiRoom . list ( ) . done ( function ( data ) {
var chatters = { } ;
data = data . data . chatters ;
2015-08-28 22:54:12 -04:00
if ( data && data . admins )
for ( var i = 0 ; i < data . admins . length ; i ++ )
chatters [ data . admins [ i ] ] = true ;
if ( data && data . global _mods )
for ( var i = 0 ; i < data . global _mods . length ; i ++ )
chatters [ data . global _mods [ i ] ] = true ;
if ( data && data . moderators )
for ( var i = 0 ; i < data . moderators . length ; i ++ )
chatters [ data . moderators [ i ] ] = true ;
if ( data && data . staff )
for ( var i = 0 ; i < data . staff . length ; i ++ )
chatters [ data . staff [ i ] ] = true ;
if ( data && data . viewers )
for ( var i = 0 ; i < data . viewers . length ; i ++ )
chatters [ data . viewers [ i ] ] = true ;
2015-06-05 03:59:28 -04:00
room . set ( "ffz_chatters" , chatters ) ;
room . ffzUpdateChatters ( ) ;
2015-07-29 01:03:10 -04:00
} ) . always ( function ( ) {
room . _ffz _chatter _timer = setTimeout ( room . ffzInitChatterCount . bind ( room ) , 300000 ) ;
2015-06-05 03:59:28 -04:00
} ) ;
} ,
2015-01-20 01:53:18 -05:00
2015-07-04 17:06:36 -04:00
2015-06-05 03:59:28 -04:00
ffzUpdateChatters : function ( add , remove ) {
var chatters = this . get ( "ffz_chatters" ) || { } ;
if ( add )
chatters [ add ] = true ;
if ( remove && chatters [ remove ] )
delete chatters [ remove ] ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
if ( ! f . settings . chatter _count )
return ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
if ( f . _cindex )
2016-10-13 23:05:54 -04:00
f . _cindex . ffzUpdateMetadata ( 'chatters' ) ;
2015-01-20 01:53:18 -05:00
2016-07-13 02:31:26 -04:00
if ( window !== window . parent && parent . postMessage )
2016-10-18 00:35:31 -04:00
parent . postMessage ( { from _ffz : true , command : 'chatter_count' , data : { room : this . get ( 'id' ) , chatters : Object . keys ( this . get ( 'ffz_chatters' ) || { } ) . length } } , "*" ) ; //location.protocol + "//www.twitch.tv/");
2015-06-05 03:59:28 -04:00
} ,
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
ffzPatchTMI : function ( ) {
2016-05-13 23:56:59 -04:00
var tmi = this . get ( 'tmiRoom' ) ,
room = this ;
2016-05-20 17:30:34 -04:00
if ( this . get ( 'ffz_is_patched' ) || ! tmi )
2015-06-05 03:59:28 -04:00
return ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
if ( f . settings . chatter _count )
this . ffzInitChatterCount ( ) ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
// Let's get chatter information!
2015-07-04 17:06:36 -04:00
// TODO: Remove this cause it's terrible.
2015-06-05 03:59:28 -04:00
var connection = tmi . _roomConn . _connection ;
if ( ! connection . ffz _cap _patched ) {
connection . ffz _cap _patched = true ;
connection . _send ( "CAP REQ :twitch.tv/membership" ) ;
connection . on ( "opened" , function ( ) {
this . _send ( "CAP REQ :twitch.tv/membership" ) ;
} , connection ) ;
2015-07-04 17:06:36 -04:00
}
2015-06-05 03:59:28 -04:00
2015-07-04 17:06:36 -04:00
// NOTICE for catching slow-mode updates
tmi . on ( 'notice' , function ( msg ) {
if ( msg . msgId === 'msg_slowmode' ) {
var match = /in (\d+) seconds/ . exec ( msg . message ) ;
if ( match ) {
room . updateWait ( parseInt ( match [ 1 ] ) ) ;
}
2015-06-05 03:59:28 -04:00
}
2015-07-04 17:06:36 -04:00
if ( msg . msgId === 'msg_timedout' ) {
var match = /for (\d+) more seconds/ . exec ( msg . message ) ;
if ( match ) {
room . set ( 'ffz_banned' , true ) ;
room . updateWait ( parseInt ( match [ 1 ] ) ) ;
}
}
if ( msg . msgId === 'msg_banned' ) {
room . set ( 'ffz_banned' , true ) ;
f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
}
2015-07-13 21:52:44 -04:00
if ( msg . msgId === 'hosts_remaining' ) {
var match = /(\d+) host command/ . exec ( msg . message ) ;
if ( match ) {
room . set ( 'ffz_hosts_left' , parseInt ( match [ 1 ] || 0 ) ) ;
f . _cindex && f . _cindex . ffzUpdateHostButton ( ) ;
}
}
2015-07-04 17:06:36 -04:00
} ) ;
2015-06-05 03:59:28 -04:00
// Check this shit.
tmi . _roomConn . _connection . off ( "message" , tmi . _roomConn . _onIrcMessage , tmi . _roomConn ) ;
tmi . _roomConn . _onIrcMessage = function ( ircMsg ) {
if ( ircMsg . target != this . ircChannel )
return ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
switch ( ircMsg . command ) {
case "JOIN" :
if ( this . _session && this . _session . nickname === ircMsg . sender ) {
this . _onIrcJoin ( ircMsg ) ;
} else
f . settings . chatter _count && room . ffzUpdateChatters ( ircMsg . sender ) ;
break ;
case "PART" :
if ( this . _session && this . _session . nickname === ircMsg . sender ) {
this . _resetActiveState ( ) ;
this . _connection . _exitedRoomConn ( ) ;
this . _trigger ( "exited" ) ;
} else
f . settings . chatter _count && room . ffzUpdateChatters ( null , ircMsg . sender ) ;
break ;
default :
break ;
}
}
tmi . _roomConn . _connection . on ( "message" , tmi . _roomConn . _onIrcMessage , tmi . _roomConn ) ;
this . set ( 'ffz_is_patched' , true ) ;
2015-07-29 01:03:10 -04:00
} . observes ( 'tmiRoom' ) ,
// Room State Stuff
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
onSlowOff : function ( ) {
if ( ! this . get ( 'slowMode' ) )
this . updateWait ( 0 ) ;
} . observes ( 'slowMode' )
2015-06-05 03:59:28 -04:00
} ) ;
2015-01-12 17:58:07 -05:00
}