2015-02-10 01:34:23 -05:00
var FFZ = window . FrankerFaceZ ,
utils = require ( "../utils" ) ,
2015-07-13 21:52:44 -04:00
constants = require ( "../constants" ) ,
2016-06-22 14:23:03 -04:00
styles = require ( "../compiled_styles" ) ,
2015-07-04 17:06:36 -04:00
helpers ,
2015-02-10 01:34:23 -05:00
2016-05-22 14:08:11 -04:00
TO _REG = /^\/t(?:imeout)? +([^ ]+)(?: +(\d+)(?: +(.+))?)?$/ ,
BAN _REG = /^\/b(?:an)? +([^ ]+)(?: +(.+))?$/ ,
2015-02-10 01:34:23 -05:00
keycodes = {
ESC : 27 ,
2016-05-24 19:24:45 -04:00
R : 82 ,
2015-02-10 01:34:23 -05:00
P : 80 ,
B : 66 ,
2015-05-17 19:02:57 -04:00
T : 84 ,
2016-10-05 01:31:10 -04:00
U : 85 ,
C : 67 ,
H : 72 ,
S : 83 ,
2017-02-26 19:11:28 -05:00
Y : 89 ,
2016-10-05 01:31:10 -04:00
N : 78
2015-02-10 01:34:23 -05:00
} ,
2015-02-10 02:42:11 -05:00
MESSAGE = '<svg class="svg-messages" height="16px" version="1.1" viewBox="0 0 18 18" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M1,15V3h16v12H1z M15.354,5.354l-0.707-0.707L9,10.293L3.354,4.646L2.646,5.354L6.293,9l-3.646,3.646l0.707,0.707L7,9.707l1.646,1.646h0.707L11,9.707l3.646,3.646l0.707-0.707L11.707,9L15.354,5.354z" fill-rule="evenodd"></path></svg>' ,
2015-10-17 18:05:44 -04:00
CHECK = '<svg class="svg-unban" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path fill-rule="evenodd" clip-rule="evenodd" fill="#888888" d="M6.5,12.75L2,8.25l2-2l2.5,2.5l5.5-5.5l2,2L6.5,12.75z"/></svg>' ;
2015-07-04 17:06:36 -04:00
try {
2016-05-25 01:06:27 -04:00
helpers = window . require && window . require ( "web-client/helpers/chat/chat-line-helpers" ) ;
2015-07-04 17:06:36 -04:00
} catch ( err ) { }
2015-02-10 01:34:23 -05:00
// ----------------
// Settings
// ----------------
2017-04-10 15:03:05 -04:00
FFZ . settings _info . disable _bttv _mod _cards = {
type : "boolean" ,
value : false ,
require _bttv : 7 ,
category : "Chat Moderation" ,
name : "Disable BTTV Mod Cards" ,
help : "This disables mod cards from BetterTTV, forcing FFZ mod cards to show instead." ,
on _update : function ( val ) {
var CL = utils . ember _resolve ( 'component:chat/chat-line' ) ,
views = CL ? utils . ember _views ( ) : [ ] ;
for ( var vid in views ) {
var view = views [ vid ] ;
if ( view instanceof CL && view . buildFromHTML ) {
view . $ ( '.from' ) . replaceWith ( view . buildFromHTML ( ) ) ;
if ( view . get ( 'msgObject.to' ) )
view . $ ( '.to' ) . replaceWith ( view . buildFromHTML ( true ) ) ;
}
}
}
} ;
2015-08-04 01:43:08 -04:00
FFZ . basic _settings . enhanced _moderation _cards = {
type : "boolean" ,
no _bttv : true ,
category : "Chat" ,
name : "Enhanced Moderation Cards" ,
help : "Improve moderation cards with hotkeys, additional buttons, chat history, and other information to make moderating easier." ,
get : function ( ) {
return this . settings . mod _card _hotkeys &&
this . settings . mod _card _info &&
this . settings . mod _card _history ;
} ,
set : function ( val ) {
this . settings . set ( 'mod_card_hotkeys' , val ) ;
this . settings . set ( 'mod_card_info' , val ) ;
this . settings . set ( 'mod_card_history' , val ) ;
}
} ;
FFZ . basic _settings . chat _hover _pause = {
type : "boolean" ,
2017-04-10 15:03:05 -04:00
no _bttv : 6 ,
2015-08-04 01:43:08 -04:00
category : "Chat" ,
name : "Pause Chat Scrolling on Mouse Hover" ,
help : "Automatically prevent the chat from scrolling when moving the mouse over it to prevent moderation mistakes and link misclicks." ,
get : 'chat_hover_pause' ,
set : 'chat_hover_pause'
} ;
2016-06-22 14:23:03 -04:00
FFZ . settings _info . highlight _messages _with _mod _card = {
2015-02-10 01:34:23 -05:00
type : "boolean" ,
value : false ,
2016-06-22 14:23:03 -04:00
no _bttv : true ,
category : "Chat Moderation" ,
name : "Highlight Messages with Mod Card Open" ,
help : "Highlight a user's messages in chat when their moderation card is open." ,
on _update : function ( val ) {
if ( ! this . _mod _card )
return ;
if ( val )
utils . update _css ( this . _chat _style , 'mod-card-highlight' , styles [ 'chat-user-bg' ] . replace ( /{user_id}/g , this . _mod _card . get ( 'cardInfo.user.id' ) ) ) ;
else
utils . update _css ( this . _chat _style , 'mod-card-highlight' ) ;
}
} ;
2016-10-05 01:31:10 -04:00
FFZ . settings _info . logviewer _test = {
type : "boolean" ,
2016-10-16 15:05:11 -04:00
value : true ,
2016-10-05 01:31:10 -04:00
no _bttv : true ,
category : "Chat Moderation" ,
2016-10-16 15:05:11 -04:00
name : "Logviewer Integration" ,
help : "Display information from CBenni's Logviewer directly on moderation cards."
2016-10-05 01:31:10 -04:00
}
2016-06-22 14:23:03 -04:00
FFZ . settings _info . chat _mod _icon _visibility = {
type : "select" ,
options : {
0 : "Disabled" ,
1 : "Enabled" ,
2 : "When Ctrl is Held" ,
3 : "When " + constants . META _NAME + " is Held" ,
4 : "When Alt is Held" ,
5 : "When Shift is Held"
} ,
value : function ( ) {
2016-09-30 13:09:03 -04:00
return this . settings . get _twitch ( "showModIcons" ) ? 1 : 0 ;
2016-06-22 14:23:03 -04:00
} ,
2016-10-01 14:11:49 -04:00
process _value : utils . process _int ( 0 ) ,
2016-06-22 14:23:03 -04:00
2017-04-10 15:03:05 -04:00
no _bttv : 6 ,
2015-02-10 01:34:23 -05:00
2015-07-04 17:06:36 -04:00
category : "Chat Moderation" ,
2016-06-22 14:23:03 -04:00
name : "Display In-Line Mod Icons" ,
help : "Choose when you should see in-line moderation icons in chat." ,
on _update : function ( val ) {
2016-09-30 13:09:03 -04:00
var settings = utils . ember _settings ( ) ;
2016-06-22 14:23:03 -04:00
if ( settings )
2016-09-30 13:09:03 -04:00
settings . set ( 'showModIcons' , val === 1 ) ;
2016-06-22 14:23:03 -04:00
}
}
FFZ . settings _info . chat _hover _pause = {
type : "select" ,
options : {
0 : "Disabled" ,
1 : "On Hover" ,
2 : "When Ctrl is Held" ,
3 : "When " + constants . META _NAME + " is Held" ,
4 : "When Alt is Held" ,
5 : "When Shift is Held" ,
6 : "Ctrl or Hover" ,
7 : constants . META _NAME + " or Hover" ,
8 : "Alt or Hover" ,
9 : "Shift or Hover"
} ,
2016-10-01 14:11:49 -04:00
value : 0 ,
process _value : utils . process _int ( 0 , 0 , 1 ) ,
2016-06-22 14:23:03 -04:00
2017-04-10 15:03:05 -04:00
no _bttv : 6 ,
2016-06-22 14:23:03 -04:00
category : "Chat Moderation" ,
name : "Pause Chat Scrolling" ,
help : "Automatically prevent the chat from scrolling when moving the mouse over it or holding Ctrl to prevent moderation mistakes and link misclicks." ,
2015-07-04 17:06:36 -04:00
on _update : function ( val ) {
if ( ! this . _roomv )
return ;
2016-06-22 14:23:03 -04:00
this . _roomv . ffzDisableFreeze ( ) ;
// Remove the old warning to make sure the label updates.
var el = this . _roomv . get ( 'element' ) ,
warning = el && el . querySelector ( '.chat-interface .more-messages-indicator.ffz-freeze-indicator' ) ;
if ( warning )
warning . parentElement . removeChild ( warning ) ;
2015-07-04 17:06:36 -04:00
if ( val )
this . _roomv . ffzEnableFreeze ( ) ;
}
} ;
FFZ . settings _info . short _commands = {
type : "boolean" ,
value : true ,
2017-04-10 15:03:05 -04:00
no _bttv : 6 ,
2015-07-04 17:06:36 -04:00
2016-10-01 14:11:49 -04:00
category : "Chat Moderation" ,
2015-07-04 17:06:36 -04:00
name : "Short Moderation Commands" ,
help : "Use /t, /b, and /u in chat in place of /timeout, /ban, /unban for quicker moderation, and use /p for 1 second timeouts."
2016-10-01 14:11:49 -04:00
} ;
2015-07-04 17:06:36 -04:00
FFZ . settings _info . mod _card _hotkeys = {
type : "boolean" ,
value : false ,
no _bttv : true ,
2016-10-01 14:11:49 -04:00
category : "Chat Moderation" ,
2015-07-04 17:06:36 -04:00
name : "Moderation Card Hotkeys" ,
help : "With a moderation card selected, press B to ban the user, T to time them out for 10 minutes, P to time them out for 1 second, or U to unban them. ESC closes the card."
2016-10-01 14:11:49 -04:00
} ;
2015-07-04 17:06:36 -04:00
2015-07-13 21:52:44 -04:00
FFZ . settings _info . mod _card _info = {
type : "boolean" ,
2015-07-18 21:10:27 -04:00
value : true ,
2015-07-13 21:52:44 -04:00
no _bttv : true ,
2016-10-01 14:11:49 -04:00
category : "Chat Moderation" ,
2015-07-13 21:52:44 -04:00
name : "Moderation Card Additional Information" ,
help : "Display a channel's follower count, view count, and account age on moderation cards."
2016-10-01 14:11:49 -04:00
} ;
2015-07-13 21:52:44 -04:00
2016-05-13 23:56:59 -04:00
FFZ . settings _info . timeout _notices = {
type : "select" ,
options : {
0 : "Disabled" ,
1 : "If I'm a Moderator" ,
2 : "Always"
} ,
value : 1 ,
2016-10-01 14:11:49 -04:00
process _value : utils . process _int ( 1 ) ,
2016-05-13 23:56:59 -04:00
2017-04-10 15:03:05 -04:00
no _bttv : 6 ,
2016-05-13 23:56:59 -04:00
2016-10-01 14:11:49 -04:00
category : "Chat Moderation" ,
2016-05-13 23:56:59 -04:00
name : "Display Timeout / Ban Notices" ,
help : "Display notices in chat when a user is timed out or banned. (You always see your own bans.)"
2016-10-01 14:11:49 -04:00
} ;
2016-05-13 23:56:59 -04:00
2015-07-04 17:06:36 -04:00
FFZ . settings _info . mod _card _history = {
2017-01-30 16:36:33 -05:00
type : "select" ,
options : {
0 : "Disabled" ,
1 : "On Rooms without Logviewer" ,
2 : "Always"
} ,
value : 0 ,
process _value : utils . process _int ( 0 , 0 , 1 ) ,
2015-07-04 17:06:36 -04:00
no _bttv : true ,
category : "Chat Moderation" ,
name : "Moderation Card History" ,
help : "Display a few of the user's previously sent messages on moderation cards." ,
on _update : function ( val ) {
2017-01-30 16:36:33 -05:00
if ( val === 2 || ! this . rooms )
2016-10-01 14:11:49 -04:00
return ;
2015-07-04 17:06:36 -04:00
2016-10-01 14:11:49 -04:00
// Delete all history~!
for ( var room _id in this . rooms ) {
var room = this . rooms [ room _id ] ;
2017-01-30 16:36:33 -05:00
if ( room && ( val === 0 || room . has _logs ) )
2016-10-01 14:11:49 -04:00
room . user _history = undefined ;
2015-07-04 17:06:36 -04:00
}
2016-10-01 14:11:49 -04:00
}
} ;
2015-07-04 17:06:36 -04:00
2016-09-30 13:09:03 -04:00
FFZ . settings _info . mod _button _context = {
type : "select" ,
options : {
0 : "Disabled" ,
1 : "Show Ban Reasons Only" ,
2 : "Show Chat Rules Only" ,
3 : "Ban Reasons + Chat Rules"
} ,
value : 3 ,
2016-10-01 14:11:49 -04:00
process _value : utils . process _int ( 3 ) ,
2016-09-30 13:09:03 -04:00
2017-04-10 15:03:05 -04:00
no _bttv : 6 ,
2016-09-30 13:09:03 -04:00
2016-10-01 14:11:49 -04:00
category : "Chat Moderation" ,
2016-09-30 13:09:03 -04:00
name : "Mod Icon Context Menus" ,
help : "Choose the available options when right-clicking an in-line moderation icon."
2016-10-01 14:11:49 -04:00
} ;
2016-09-30 13:09:03 -04:00
2016-05-22 14:08:11 -04:00
FFZ . settings _info . mod _card _reasons = {
type : "button" ,
value : [
"One-Man Spam" ,
"Posting Bad Links" ,
"Ban Evasion" ,
"Threats / Personal Info" ,
"Hate / Harassment" ,
"Ignoring Broadcaster / Moderators"
] ,
category : "Chat Moderation" ,
2017-04-10 15:03:05 -04:00
no _bttv : 6 ,
2016-05-22 14:08:11 -04:00
2016-09-30 13:09:03 -04:00
name : "Ban / Timeout Reasons" ,
help : "Change the available options in the chat ban reasons list shown in moderation cards and when right-clicking an in-line ban or timeout button." ,
2016-05-22 14:08:11 -04:00
method : function ( ) {
var f = this ,
old _val = this . settings . mod _card _reasons . join ( "\n" ) ,
input = utils . createElement ( 'textarea' ) ;
input . style . marginBottom = "20px" ;
utils . prompt (
"Moderation Card Ban Reasons" ,
2016-09-30 13:09:03 -04:00
"Please enter a list of ban reasons to select from. One item per line." ,
2016-05-22 14:08:11 -04:00
old _val ,
function ( new _val ) {
if ( new _val === null || new _val === undefined )
return ;
var vals = new _val . trim ( ) . split ( /\s*\n\s*/g ) ,
i = vals . length ;
while ( i -- )
if ( vals [ i ] . length === 0 )
vals . splice ( i , 1 ) ;
f . settings . set ( 'mod_card_reasons' , vals ) ;
} ,
600 , input
) ;
}
} ;
2015-10-17 18:05:44 -04:00
FFZ . settings _info . mod _buttons = {
type : "button" ,
// Special Values
// false = Ban/Unban
// integer = Timeout (that amount of time)
value : [ [ '' , false , false ] , [ '' , 600 , false ] ] , //, ['', 1, false]],
2017-04-10 15:03:05 -04:00
no _bttv : 6 ,
2015-10-17 18:05:44 -04:00
2016-10-01 14:11:49 -04:00
category : "Chat Moderation" ,
2015-10-17 18:05:44 -04:00
name : "Custom In-Line Moderation Icons" ,
help : "Change out the different in-line moderation icons to use any command quickly." ,
method : function ( ) {
2016-10-01 14:11:49 -04:00
var f = this ,
old _val = "" ,
input = utils . createElement ( 'textarea' ) ;
2016-09-30 13:09:03 -04:00
2016-10-01 14:11:49 -04:00
input . style . marginBottom = '20px' ;
input . placeholder = '/ban\n600' ;
2016-07-13 02:31:26 -04:00
2016-10-01 14:11:49 -04:00
for ( var i = 0 ; i < this . settings . mod _buttons . length ; i ++ ) {
var pair = this . settings . mod _buttons [ i ] ,
2017-05-09 16:49:24 -04:00
prefix = pair [ 0 ] , cmd = pair [ 1 ] , had _prefix = pair [ 2 ] , non _mod = pair [ 4 ] ;
2016-07-13 02:31:26 -04:00
2016-10-01 14:11:49 -04:00
if ( cmd === false )
cmd = "/ban" ;
else if ( cmd === 600 )
cmd = "/timeout" ;
else if ( typeof cmd !== "string" )
cmd = '' + cmd ;
2016-07-13 02:31:26 -04:00
2016-10-01 14:11:49 -04:00
prefix = had _prefix ? 'name:' + prefix + '=' : '' ;
2017-05-09 16:49:24 -04:00
old _val += ( old _val . length ? '\n' : '' ) + ( non _mod ? 'nonmod:' : '' ) + prefix + cmd ;
2016-10-01 14:11:49 -04:00
}
2016-09-30 13:09:03 -04:00
2016-10-01 14:11:49 -04:00
utils . prompt (
"Custom In-Line Moderation Icons" ,
"Please enter a list of commands to be displayed as moderation buttons within chat lines. " +
"One item per line. As a shortcut for specific duration timeouts, you can enter the number of seconds by itself. " +
" To send multiple commands, separate them with <code><LINE></code>. " +
"Variables, such as the target user's name, can be inserted into your commands. If no variables are detected " +
"in a line, <code>{user}</code> will be added to the end of the first command.<hr>" +
"To set a custom label for the button, start your line with <code>name:</code> followed by the " +
"name of the button. End the name with an equals sign. Only the first character will be displayed.<br>" +
"<strong>Example:</strong> <code>name:B=/ban {user}</code><hr>" +
2017-05-09 16:49:24 -04:00
"To create a button that will be visible even if you don't have moderator privileges over a user, " +
"start your line with <code>nonmod:</code><br>" +
"<strong>Example:</strong> <code>nonmod:/w some_bot !info {user}</code><hr>" +
2016-10-01 14:11:49 -04:00
"<strong>Allowed Variables</strong><br><table><tbody>" +
"<tr><td><code>{user}</code></td><td>target user's name</td>" +
"<td><code>{user_name}</code></td><td>target user's name</td></tr>" +
"<tr><td><code>{user_display_name}</code></td><td>target user's display name</td>" +
"<td><code>{user_id}</code></td><td>target user's numeric ID</td></tr>" +
"<tr><td><code>{room}</code></td><td>chat room's name</td>" +
"<td><code>{room_name}</code></td><td>chat room's name</td></tr>" +
"<tr><td><code>{room_display_name}</code></td><td>chat room's display name</td>" +
"<td><code>{room_id}</code></td><td>chat room's numeric ID</td></tr>" +
"<tr><td><code>{id}</code></td><td>message's UUID</td></tr>" +
"</tbody></table>" ,
old _val ,
function ( new _val ) {
if ( new _val === null || new _val === undefined )
return ;
var vals = new _val . trim ( ) . split ( /\s*\n\s*/g ) ,
output = [ ] ;
for ( var i = 0 ; i < vals . length ; i ++ ) {
var cmd = vals [ i ] ,
prefix ,
is _emoji = false ,
2017-05-09 16:49:24 -04:00
non _mod = /^nonmod:/ . test ( cmd ) ;
2016-10-01 14:11:49 -04:00
if ( ! cmd || ! cmd . length )
continue ;
2016-07-13 02:31:26 -04:00
2017-05-09 16:49:24 -04:00
if ( non _mod )
cmd = cmd . substr ( 7 ) . trim ( ) ;
var name _match = /^name:([^=]+)=/ . exec ( cmd ) ;
2016-10-01 14:11:49 -04:00
if ( name _match ) {
label = name _match [ 1 ] ;
if ( window . punycode && punycode . ucs2 )
label = punycode . ucs2 . encode ( [ punycode . ucs2 . decode ( label ) [ 0 ] ] ) ;
// Check for an emoji
var tokens = f . tokenize _emoji ( label ) ;
if ( tokens && tokens [ 0 ] && tokens [ 0 ] . ffzEmoji )
is _emoji = tokens [ 0 ] . ffzEmoji ;
cmd = cmd . substr ( name _match [ 0 ] . length ) . trim ( ) ;
2017-05-09 16:49:24 -04:00
if ( ! non _mod ) {
non _mod = /^nonmod:/ . test ( cmd ) ;
if ( non _mod )
cmd = cmd . substr ( 7 ) . trim ( ) ;
}
2016-10-01 14:11:49 -04:00
} else
label = undefined ;
// Check for a plain ban.
if ( /^\/b(?:an)?(?:\s+{user(?:_name)?})?\s*$/ . test ( cmd ) )
cmd = false ;
// Numeric Timeout
else if ( /^\d+$/ . test ( cmd ) )
cmd = parseInt ( cmd ) ;
// Command Timeout
else if ( /^\/t(?:imeout)?(?:\s+{user(?:_name)?}(?:\s+(\d+))?)?\s*$/ . test ( cmd ) ) {
cmd = parseInt ( /^\/t(?:imeout)?(?:\s+{user(?:_name)?}(?:\s+(\d+))?)?\s*$/ . exec ( cmd ) [ 1 ] ) ;
if ( isNaN ( cmd ) || ! isFinite ( cmd ) )
cmd = 600 ;
2016-07-13 02:31:26 -04:00
}
2016-10-01 14:11:49 -04:00
// Okay. Do we still need a prefix?
if ( label === undefined ) {
var tmp ;
if ( typeof cmd === "string" )
tmp = /\w/ . exec ( cmd ) ;
else
tmp = utils . duration _string ( cmd ) ;
2016-07-13 02:31:26 -04:00
2016-10-01 14:11:49 -04:00
label = tmp && tmp . length ? tmp [ 0 ] . toUpperCase ( ) : 'C' ;
2016-07-13 02:31:26 -04:00
}
2016-10-01 14:11:49 -04:00
// Add {user} to the first command if it's a custom command and missing.
if ( typeof cmd === "string" ) {
utils . CMD _VAR _REGEX . lastIndex = 0 ;
if ( ! utils . CMD _VAR _REGEX . test ( cmd ) ) {
var lines = cmd . split ( /\s*<LINE>\s*/g ) ;
lines [ 0 ] += ' {user}' ;
cmd = lines . join ( "<LINE>" ) ;
}
}
2017-05-09 16:49:24 -04:00
output . push ( [ label , cmd , name _match != null , is _emoji , non _mod ] ) ;
2016-10-01 14:11:49 -04:00
}
f . settings . set ( 'mod_buttons' , output ) ;
// Update existing chat lines.
var CL = utils . ember _resolve ( 'component:chat/chat-line' ) ,
views = CL ? utils . ember _views ( ) : [ ] ;
for ( var vid in views ) {
var view = views [ vid ] ;
if ( view instanceof CL && view . buildModIconsHTML )
view . $ ( '.mod-icons' ) . replaceWith ( view . buildModIconsHTML ( ) ) ;
}
} , 600 , input ) ;
}
} ;
2015-10-17 18:05:44 -04:00
2015-07-13 21:52:44 -04:00
FFZ . settings _info . mod _card _buttons = {
type : "button" ,
value : [ ] ,
category : "Chat Moderation" ,
no _bttv : true ,
name : "Moderation Card Additional Buttons" ,
help : "Add additional buttons to moderation cards for running chat commands on those users." ,
method : function ( ) {
2016-03-23 19:28:22 -04:00
var f = this ,
2016-09-30 13:09:03 -04:00
old _val = "" ,
input = utils . createElement ( 'textarea' ) ;
input . style . marginBottom = '20px' ;
2015-07-13 21:52:44 -04:00
for ( var i = 0 ; i < this . settings . mod _card _buttons . length ; i ++ ) {
2016-09-30 13:09:03 -04:00
var label , cmd , had _label , pair = this . settings . mod _card _buttons [ i ] ;
if ( Array . isArray ( pair ) ) {
label = pair [ 0 ] ;
cmd = pair [ 1 ] ;
had _label = pair [ 2 ] ;
} else {
cmd = pair ;
had _label = false ;
}
label = had _label ? 'name:' + label + '=' : '' ;
old _val += ( old _val . length ? '\n' : '' ) + label + cmd ;
2015-07-13 21:52:44 -04:00
}
2015-08-04 01:43:08 -04:00
2016-07-13 02:31:26 -04:00
utils . prompt (
"Moderation Card Additional Buttons" ,
2016-09-30 13:09:03 -04:00
"Please enter a list of additional commands to display buttons for on moderation cards. " +
"One item per line. To send multiple commands, separate them with <code><LINE></code>. " +
"Variables, such as the target user's name, can be inserted into your commands. If no variables are detected " +
"in a line, <code>{user}</code> will be added to the end of the first command.<hr>" +
"To set a custom label for the button, start your line with <code>name:</code> followed by the name of the button. " +
"End the name with an equals sign.<br>" +
"<strong>Example:</strong> <code>name:Boop=/timeout {user} 15 Boop!</code><hr>" +
"<strong>Allowed Variables</strong><br><table><tbody>" +
"<tr><td><code>{user}</code></td><td>target user's name</td>" +
"<td><code>{user_name}</code></td><td>target user's name</td></tr>" +
"<tr><td><code>{user_display_name}</code></td><td>target user's display name</td>" +
"<td><code>{user_id}</code></td><td>target user's numeric ID</td></tr>" +
"<tr><td><code>{room}</code></td><td>chat room's name</td>" +
"<td><code>{room_name}</code></td><td>chat room's name</td></tr>" +
"<tr><td><code>{room_display_name}</code></td><td>chat room's display name</td>" +
"<td><code>{room_id}</code></td><td>chat room's numeric ID</td></tr>" +
"</tbody></table>" ,
old _val ,
2016-07-13 02:31:26 -04:00
function ( new _val ) {
if ( new _val === null || new _val === undefined )
return ;
2016-09-30 13:09:03 -04:00
var vals = new _val . trim ( ) . split ( /\s*\n\s*/g ) ,
output = [ ] ;
2016-07-13 02:31:26 -04:00
2016-09-30 13:09:03 -04:00
for ( var i = 0 ; i < vals . length ; i ++ ) {
var cmd = vals [ i ] ,
label ,
name _match = /^name:([^=]+)=/ . exec ( cmd ) ;
2016-07-13 02:31:26 -04:00
2016-09-30 13:09:03 -04:00
if ( ! cmd || ! cmd . length )
continue ;
2016-07-13 02:31:26 -04:00
2016-09-30 13:09:03 -04:00
if ( name _match ) {
label = name _match [ 1 ] ;
cmd = cmd . substr ( name _match [ 0 ] . length ) ;
} else
label = cmd . split ( ' ' , 1 ) [ 0 ]
2016-07-13 02:31:26 -04:00
2016-09-30 13:09:03 -04:00
output . push ( [ label , cmd , name _match != null ] ) ;
2016-07-13 02:31:26 -04:00
}
2016-09-30 13:09:03 -04:00
f . settings . set ( "mod_card_buttons" , output ) ;
} , 600 , input ) ;
2015-07-13 21:52:44 -04:00
}
} ;
2015-07-04 17:06:36 -04:00
FFZ . settings _info . mod _card _durations = {
type : "button" ,
value : [ 300 , 600 , 3600 , 43200 , 86400 , 604800 ] ,
category : "Chat Moderation" ,
no _bttv : true ,
name : "Moderation Card Timeout Buttons" ,
help : "Add additional timeout buttons to moderation cards with specific durations." ,
method : function ( ) {
2016-07-13 02:31:26 -04:00
var f = this ,
old _val = this . settings . mod _card _durations . join ( ", " ) ;
utils . prompt (
"Moderation Card Timeout Buttons" ,
"Please enter a comma-separated list of durations that you would like to have timeout buttons for. " +
"Durations must be expressed in seconds.</p><p><b>Default:</b> 300, 600, 3600, 43200, 86400, 604800" ,
old _val ,
function ( new _val ) {
if ( new _val === null || new _val === undefined )
return ;
if ( new _val === "reset" )
new _val = FFZ . settings _info . mod _card _durations . value . join ( ", " ) ;
// Split them up.
new _val = new _val . trim ( ) . split ( /[ ,]+/ ) ;
var vals = [ ] ;
for ( var i = 0 ; i < new _val . length ; i ++ ) {
var val = parseInt ( new _val [ i ] ) ;
if ( val === 0 )
val = 1 ;
if ( ! Number . isNaN ( val ) && val > 0 )
vals . push ( val ) ;
}
f . settings . set ( "mod_card_durations" , vals ) ;
} , 600 ) ;
2015-07-04 17:06:36 -04:00
}
2015-02-10 01:34:23 -05:00
} ;
// ----------------
// Initialization
// ----------------
FFZ . prototype . setup _mod _card = function ( ) {
2016-05-25 01:06:27 -04:00
try {
helpers = window . require && window . require ( "web-client/helpers/chat/chat-line-helpers" ) ;
} catch ( err ) { }
2016-06-22 14:23:03 -04:00
this . log ( "Listening to the Settings controller to catch mod icon state changes." ) ;
var f = this ,
2016-09-30 13:09:03 -04:00
Settings = utils . ember _settings ( ) ;
2016-06-22 14:23:03 -04:00
if ( Settings )
2016-09-30 13:09:03 -04:00
Settings . addObserver ( 'showModIcons' , function ( ) {
if ( Settings . get ( 'showModIcons' ) )
2016-06-22 14:23:03 -04:00
f . settings . set ( 'chat_mod_icon_visibility' , 1 ) ;
} ) ;
2015-07-04 17:06:36 -04:00
this . log ( "Modifying Mousetrap stopCallback so we can catch ESC." ) ;
var orig _stop = Mousetrap . stopCallback ;
Mousetrap . stopCallback = function ( e , element , combo ) {
if ( element . classList . contains ( 'no-mousetrap' ) )
return true ;
return orig _stop ( e , element , combo ) ;
}
2016-05-24 19:24:45 -04:00
Mousetrap . bind ( "up up down down left right left right b a" , function ( ) {
2015-07-04 17:06:36 -04:00
var el = document . querySelector ( ".app-main" ) || document . querySelector ( ".ember-chat-container" ) ;
el && el . classList . toggle ( 'ffz-flip' ) ;
} ) ;
2015-02-10 01:34:23 -05:00
this . log ( "Hooking the Ember Moderation Card view." ) ;
2016-07-13 02:06:50 -04:00
this . update _views ( 'component:chat/moderation-card' , this . modify _moderation _card ) ;
}
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
FFZ . prototype . modify _moderation _card = function ( component ) {
var f = this ;
utils . ember _reopen _view ( component , {
2015-07-04 17:06:36 -04:00
ffzForceRedraw : function ( ) {
this . rerender ( ) ;
2016-10-05 01:31:10 -04:00
var el = this . get ( 'element' ) ;
this . ffzChangePage ( null ) ;
var chat = utils . ember _lookup ( 'controller:chat' ) ,
room _id = chat && chat . get ( 'currentRoom.id' ) ,
2017-08-16 13:24:55 -04:00
user _id = this . get ( 'cardInfo.user.id' ) ,
user = f . get _user ( ) ,
is _me = user && user . login === user _id ;
2016-10-05 01:31:10 -04:00
if ( ( this . _lv _sock _room && this . _lv _sock _room !== room _id ) || ( this . _lv _sock _user && this . _lv _sock _user !== user _id ) ) {
2016-10-14 20:43:34 -04:00
f . lv _ws _unsub ( 'logs-' + this . _lv _sock _room + '-' + this . _lv _sock _user ) ;
2016-10-05 01:31:10 -04:00
this . _lv _sock _room = null ;
this . _lv _sock _user = null ;
}
2015-12-12 13:28:35 -05:00
2016-10-07 18:11:05 -04:00
if ( f . settings . mod _card _history )
this . ffzRenderHistory ( ) ;
2017-08-16 13:24:55 -04:00
Ember . run . next ( this . ffzFixDefaultActions . bind ( this ) ) ;
2016-06-22 14:23:03 -04:00
// Highlight this user's chat messages.
if ( f . settings . highlight _messages _with _mod _card )
utils . update _css ( f . _chat _style , 'mod-card-highlight' , styles [ 'chat-user-bg' ] . replace ( /{user_id}/g , this . get ( 'cardInfo.user.id' ) ) ) ;
2015-12-12 13:28:35 -05:00
} . observes ( "cardInfo.isModeratorOrHigher" , "cardInfo.user.id" ) ,
2015-07-04 17:06:36 -04:00
2015-07-13 21:52:44 -04:00
ffzRebuildInfo : function ( ) {
var el = this . get ( 'element' ) ,
info = el && el . querySelector ( '.info' ) ;
if ( ! info )
return ;
2016-06-22 14:23:03 -04:00
var out = '<span class="stat html-tooltip" title="Total Views">' + constants . EYE + ' ' + utils . number _commas ( this . get ( 'cardInfo.user.views' ) || 0 ) + '</span>' ,
2015-07-13 21:52:44 -04:00
since = utils . parse _date ( this . get ( 'cardInfo.user.created_at' ) || '' ) ,
followers = this . get ( 'cardInfo.user.ffz_followers' ) ;
if ( typeof followers === "number" ) {
2016-06-22 14:23:03 -04:00
out += '<span class="stat html-tooltip" title="Followers">' + constants . HEART + ' ' + utils . number _commas ( followers || 0 ) + '</span>' ;
2015-08-04 01:43:08 -04:00
2015-07-13 21:52:44 -04:00
} else if ( followers === undefined ) {
var t = this ;
this . set ( 'cardInfo.user.ffz_followers' , false ) ;
2016-03-23 19:28:22 -04:00
utils . api . get ( "channels/" + this . get ( 'cardInfo.user.id' ) + '/follows' , { limit : 1 } ) . done ( function ( data ) {
2015-07-13 21:52:44 -04:00
t . set ( 'cardInfo.user.ffz_followers' , data . _total ) ;
t . ffzRebuildInfo ( ) ;
} ) . fail ( function ( data ) {
t . set ( 'cardInfo.user.ffz_followers' , undefined ) ;
} ) ;
}
if ( since ) {
2015-11-19 02:45:56 -05:00
var now = Date . now ( ) - ( f . _ws _server _offset || 0 ) ,
age = Math . floor ( ( now - since . getTime ( ) ) / 1000 ) ;
2015-07-13 21:52:44 -04:00
if ( age > 0 ) {
2016-06-22 14:23:03 -04:00
out += '<span class="stat html-tooltip" title="Member Since: ' + utils . quote _san ( age > 86400 ? since . toLocaleDateString ( ) : since . toLocaleString ( ) ) + '">' + constants . CLOCK + ' ' + utils . human _time ( age , 10 ) + '</span>' ;
2015-07-13 21:52:44 -04:00
}
}
info . innerHTML = out ;
} . observes ( "cardInfo.user.views" ) ,
2016-10-05 01:31:10 -04:00
lvGetLogs : function ( ) {
var t = this ,
logs = this . _lv _logs ,
chat = utils . ember _lookup ( 'controller:chat' ) ,
room _id = chat && chat . get ( 'currentRoom.id' ) ,
user _id = this . get ( 'cardInfo.user.id' ) ;
return new Promise ( function ( succeed , fail ) {
// Don't expire data if we're connected to the websocket.
if ( logs && ( f . _lv _ws _open || logs . expires > Date . now ( ) ) && logs . room === room _id && logs . user === user _id )
return succeed ( logs . data ) ;
if ( t . _lv _log _requests )
return t . _lv _log _requests . push ( succeed ) ;
t . _lv _log _requests = [ succeed ] ;
f . lv _get _logs ( room _id , user _id ) . then ( function ( data ) {
var new _room _id = chat && chat . get ( 'currentRoom.id' ) ,
new _user _id = t . get ( 'cardInfo.user.id' ) ;
if ( user _id !== new _user _id || room _id !== new _room _id )
return ;
t . _lv _logs = {
expires : Date . now ( ) + 30000 ,
room : room _id ,
user : user _id ,
data : data
} ;
t . _lv _sock _room = room _id ;
t . _lv _sock _user = user _id ;
2016-10-14 20:43:34 -04:00
f . lv _ws _sub ( 'logs-' + room _id + '-' + user _id ) ;
2016-10-05 01:31:10 -04:00
var requests = t . _lv _log _requests ;
t . _lv _log _requests = null ;
for ( var i = 0 ; i < requests . length ; i ++ )
requests [ i ] ( data ) ;
} ) ;
} ) ;
} ,
lvOnMessage : function ( cmd , data ) {
//f.log("[LV] Socket Message: " + cmd, data)
2016-10-07 18:11:05 -04:00
if ( cmd === "comment-add" ) {
if ( data . topic !== this . get ( 'cardInfo.user.id' ) )
return ;
2016-10-05 01:31:10 -04:00
2016-10-07 18:11:05 -04:00
FFZ . mod _card _pages . notes . add _note . call ( f , this , this . get ( 'element' ) , data ) ;
2016-10-05 01:31:10 -04:00
2016-10-07 18:11:05 -04:00
} else if ( cmd === "comment-update" ) {
var el = this . get ( 'element' ) ,
line = el && el . querySelector ( '.user-notes .chat-line[data-lv-id="' + data . id + '"]' ) ;
if ( ! line )
return ;
var new _line = FFZ . mod _card _pages . notes . build _note . call ( f , this , data ) ;
line . outerHTML = new _line . outerHTML ;
} else if ( cmd === "comment-delete" ) {
var el = this . get ( 'element' ) ,
line = el && el . querySelector ( '.user-notes .chat-line[data-lv-id="' + data . id + '"]' ) ;
if ( ! line )
return ;
// If we're the only message on this date, remove the timestamp line.
var before _line = line . previousElementSibling ,
after _line = line . nextElementSibling ;
2016-10-05 01:31:10 -04:00
2016-10-07 18:11:05 -04:00
if ( before _line && before _line . classList . contains ( 'timestamp-line' ) &&
( ! after _line || after _line . classList . contains ( 'timestamp-line' ) ) )
before _line . parentElement . removeChild ( before _line ) ;
2016-10-05 01:31:10 -04:00
2016-10-07 18:11:05 -04:00
// Remove the line itself.
line . parentElement . removeChild ( line ) ;
2016-10-14 20:43:34 -04:00
} else if ( cmd === "log-update" ) {
if ( ! this . _lv _logs || ! this . _lv _logs . data || data . nick !== this . _lv _logs . data . user . nick )
return ;
// Parse the message. Store the data.
var message = f . lv _parse _message ( data ) ,
msgs = this . _lv _logs . data . before ,
ind = - 1 ,
i = msgs . length ;
// Find the existing entry.
while ( -- i ) {
var msg = msgs [ i ] ;
if ( msg . lv _id === message . lv _id ) {
ind = i ;
break ;
}
}
// Nothing to update, so don't.
if ( ind === - 1 )
return ;
msgs [ ind ] = message ;
var el = this . get ( 'element' ) ,
container = el && el . querySelector ( '.ffz-tab-container' ) ,
line = container && container . querySelector ( '.lv-history .chat-line[data-lv-id="' + message . lv _id + '"]' ) ;
if ( ! line )
return ;
var new _line = f . _build _mod _card _history ( message , this , false ,
FFZ . mod _card _pages . history . render _adjacent . bind ( f , this , container , message ) ) ;
line . parentElement . insertBefore ( new _line , line ) ;
line . parentElement . removeChild ( line ) ;
2016-10-07 18:11:05 -04:00
} else if ( cmd === "log-add" ) {
if ( ! this . _lv _logs || ! this . _lv _logs . data || data . nick !== this . _lv _logs . data . user . nick )
return ;
// Parse the message. Store the data.
2016-10-14 20:43:34 -04:00
var message = f . lv _parse _message ( data ) ;
2016-10-07 18:11:05 -04:00
this . _lv _logs . data . before . push ( message ) ;
2016-10-18 14:06:20 -04:00
if ( message . is _ban )
this . _lv _logs . data . user . timeouts ++ ;
else if ( ! message . is _admin && ( ! message . is _notice || message . message . indexOf ( 'Message: ' ) !== - 1 ) )
this . _lv _logs . data . user . messages ++ ;
2016-10-07 18:11:05 -04:00
// If we're viewing the chat history, update it.
var el = this . get ( 'element' ) ,
container = el && el . querySelector ( '.ffz-tab-container' ) ,
2017-06-11 13:30:37 -04:00
history = container && container . querySelector ( '.ffz-tab-container[data-page="history"] .chat-history.lv-history' ) ;
2016-10-07 18:11:05 -04:00
if ( history ) {
var was _at _bottom = history . scrollTop >= ( history . scrollHeight - history . clientHeight ) ,
last _line = history . querySelector ( '.chat-line:last-of-type' ) ,
ll _date = last _line && last _line . getAttribute ( 'data-date' ) ,
2016-12-23 13:40:05 -05:00
date = message . date && message . date . toLocaleDateString ( ) ;
2016-10-07 18:11:05 -04:00
if ( last _line . classList . contains ( 'no-messages' ) ) {
last _line . parentElement . removeChild ( last _line ) ;
last _line = null ;
ll _date = null ;
}
if ( last _line && ll _date !== date ) {
var date _line = utils . createElement ( 'li' , 'chat-line timestamp-line' , date ) ;
date _line . setAttribute ( 'data-date' , date ) ;
history . appendChild ( date _line ) ;
}
history . appendChild ( f . _build _mod _card _history ( message , this , false ,
2016-10-14 20:43:34 -04:00
FFZ . mod _card _pages . history . render _adjacent . bind ( f , this , container , message ) ) ) ;
2016-10-07 18:11:05 -04:00
if ( was _at _bottom )
setTimeout ( function ( ) { history . scrollTop = history . scrollHeight ; } )
}
2016-10-05 01:31:10 -04:00
}
} ,
lvUpdateLevels : function ( levels ) {
var channel = levels . channel ,
user = levels . me ,
level = user && user . level || 0 ;
this . lv _view = level >= channel . viewlogs ;
this . lv _view _mod = level >= channel . viewmodlogs ;
this . lv _view _notes = level >= channel . viewcomments ;
this . lv _write _notes = level >= channel . writecomments ;
this . lv _delete _notes = level >= channel . deletecomments ;
var el = this . get ( 'element' ) ;
2016-10-11 21:41:35 -04:00
if ( el ) {
el . classList . toggle ( 'lv-notes' , this . lv _view _notes ) ;
el . classList . toggle ( 'lv-logs' , this . lv _view ) ;
el . classList . toggle ( 'lv-tabs' , this . lv _view || this . lv _view _notes ) ;
}
2016-10-05 01:31:10 -04:00
} ,
2016-07-13 02:06:50 -04:00
ffz _destroy : function ( ) {
2015-11-07 22:56:15 -05:00
if ( f . _mod _card === this )
f . _mod _card = undefined ;
2016-06-22 14:23:03 -04:00
2016-10-05 01:31:10 -04:00
if ( this . _lv _sock _room && this . _lv _sock _user ) {
2016-10-14 20:43:34 -04:00
f . lv _ws _unsub ( 'logs-' + this . _lv _sock _room + '-' + this . _lv _sock _user ) ;
2016-10-05 01:31:10 -04:00
this . _lv _sock _room = null ;
this . _lv _sock _user = null ;
}
if ( this . _lv _callback ) {
f . lv _ws _remove _callback ( this . _lv _callback ) ;
this . _lv _callback = null ;
}
2016-06-22 14:23:03 -04:00
utils . update _css ( f . _chat _style , 'mod-card-highlight' ) ;
2015-11-07 22:56:15 -05:00
} ,
2017-08-16 13:24:55 -04:00
ffzFixDefaultActions : function ( ) {
2016-07-13 02:06:50 -04:00
var el = this . get ( 'element' ) ,
t = this ,
line ,
2016-06-22 14:23:03 -04:00
2016-12-03 22:44:26 -05:00
is _mod = t . get ( 'cardInfo.isModeratorOrHigher' ) ,
2016-07-13 02:06:50 -04:00
ban _reasons ,
2016-05-22 14:08:11 -04:00
2016-07-13 02:06:50 -04:00
chat = utils . ember _lookup ( 'controller:chat' ) ,
user = f . get _user ( ) ,
2016-09-30 13:09:03 -04:00
room = chat && chat . get ( 'currentRoom' ) ,
room _id = room && room . get ( 'id' ) ,
2016-10-05 01:31:10 -04:00
ffz _room = f . rooms && f . rooms [ room _id ] || { } ,
2016-07-13 02:06:50 -04:00
is _broadcaster = user && room _id === user . login ,
2015-07-29 01:03:10 -04:00
2016-12-03 22:44:26 -05:00
user _id = this . get ( 'cardInfo.user.id' ) ,
2017-08-16 13:24:55 -04:00
is _me = user && user . login === user _id ,
2016-07-13 02:06:50 -04:00
alias = f . aliases [ user _id ] ,
2015-11-07 22:56:15 -05:00
2016-07-13 02:06:50 -04:00
handle _key ,
2016-05-24 19:24:45 -04:00
2016-07-13 02:06:50 -04:00
ban _reason = function ( ) {
return ban _reasons && ban _reasons . value ? ' ' + ban _reasons . value : "" ;
2017-08-16 13:24:55 -04:00
} ,
2016-05-24 19:24:45 -04:00
2017-08-16 13:24:55 -04:00
alias _btn = utils . createElement ( 'button' , 'alias button float-left button--icon-only html-tooltip' ) ;
2016-05-24 19:24:45 -04:00
2017-08-16 13:24:55 -04:00
alias _btn . innerHTML = '<figure class="icon">' + constants . EDIT + '</figure>' ;
alias _btn . title = "Set Alias" ;
2015-07-29 01:03:10 -04:00
2017-08-16 13:24:55 -04:00
alias _btn . addEventListener ( 'click' , function ( ) {
var user = t . get ( 'cardInfo.user.id' ) ,
alias = f . aliases [ user ] ,
results = f . format _display _name ( t . get ( 'cardInfo.user.display_name' ) , user , true ) ;
utils . prompt (
"Alias for <b" + ( results [ 1 ] ? ' class="html-tooltip" title="' + utils . quote _attr ( results [ 1 ] ) + '">' : '>' ) + results [ 0 ] + "</b>" ,
"Please enter an alias for the user. Leave it blank to remove the alias." ,
alias ,
function ( new _val ) {
if ( new _val === null || new _val === undefined )
2016-10-05 01:31:10 -04:00
return ;
2017-08-16 13:24:55 -04:00
new _val = new _val . trim ( ) ;
if ( ! new _val )
new _val = undefined ;
2016-10-05 01:31:10 -04:00
2017-08-16 13:24:55 -04:00
f . aliases [ user ] = new _val ;
f . save _aliases ( ) ;
2016-07-13 02:06:50 -04:00
2017-08-16 13:24:55 -04:00
// Update UI
f . _update _alias ( user ) ;
2016-07-13 02:06:50 -04:00
2017-08-16 13:24:55 -04:00
var name = el . querySelector ( 'h4.name' ) ;
if ( name ) {
name . classList . toggle ( 'ffz-alias' , new _val ) ;
var results = f . format _display _name ( t . get ( 'cardInfo.user.display_name' ) , user _id ) ;
name . innerHTML = results [ 0 ] ;
name . title = results [ 1 ] || '' ;
if ( results [ 1 ] )
jQuery ( name ) . tipsy ( { html : true , gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
}
} ) ;
2016-07-13 02:06:50 -04:00
} ) ;
2017-08-16 13:24:55 -04:00
if ( is _me === this . _ffz _was _me )
return ;
this . _ffz _was _me = is _me ;
if ( is _me ) {
jQuery ( '.ffz-extra-controls' , el ) . remove ( ) ;
line = utils . createElement ( 'div' , 'extra-interface ffz-extra-me-controls moderation-card__actions clearfix' ) ;
line . appendChild ( alias _btn ) ;
el . appendChild ( line ) ;
return ;
} else
jQuery ( '.ffz-extra-me-controls' , el ) . remove ( ) ;
2016-07-13 02:06:50 -04:00
2016-11-29 23:24:12 -05:00
// Move the default buttons.
2017-08-16 13:24:55 -04:00
var def _actions = el . querySelector ( '.moderation-card__actions .clearfix' ) ;
2016-11-29 23:24:12 -05:00
if ( def _actions ) {
2017-08-16 13:24:55 -04:00
def _actions = def _actions . parentElement ;
2016-11-29 23:24:12 -05:00
var def _line = def _actions . querySelector ( '.clearfix' ) ,
2016-11-30 16:58:11 -05:00
bad _line = def _actions . querySelector ( '.moderation-card__controls' ) ;
2016-11-29 23:24:12 -05:00
2016-11-30 16:58:11 -05:00
if ( def _line && bad _line ) {
var children = bad _line . querySelectorAll ( 'button' ) ;
for ( var i = 0 ; i < children . length ; i ++ ) {
bad _line . removeChild ( children [ i ] ) ;
def _line . appendChild ( children [ i ] ) ;
}
2016-11-29 23:24:12 -05:00
2016-11-30 16:58:11 -05:00
bad _line . classList . add ( 'hidden' ) ;
}
2016-11-29 23:24:12 -05:00
}
2017-08-16 13:24:55 -04:00
// Additional Buttons
if ( is _mod ) {
el . classList . add ( 'ffz-is-mod' ) ;
2016-11-29 23:24:12 -05:00
2017-08-16 13:24:55 -04:00
if ( f . settings . mod _card _buttons && f . settings . mod _card _buttons . length ) {
line = utils . createElement ( 'div' , 'extra-interface ffz-extra-controls moderation-card__actions clearfix' ) ;
2016-09-09 17:34:20 -04:00
2017-08-16 13:24:55 -04:00
var build _cmd = function ( user , room , cmd ) {
var lines = utils . replace _cmd _variables ( cmd , user , room ) . split ( /\s*<LINE>\s*/g ) ,
reason = ban _reason ( ) ;
2016-07-13 02:06:50 -04:00
2017-08-16 13:24:55 -04:00
if ( reason ) {
for ( var i = 0 ; i < lines . length ; i ++ ) {
var match = TO _REG . exec ( lines [ i ] ) ;
2016-11-12 11:58:38 -05:00
if ( match ) {
if ( ! match [ 2 ] )
2017-08-16 13:24:55 -04:00
lines [ i ] += ' 600' ;
if ( ! match [ 3 ] )
2016-11-12 11:58:38 -05:00
lines [ i ] += reason ;
2017-08-16 13:24:55 -04:00
2016-11-12 11:58:38 -05:00
break ;
2017-08-16 13:24:55 -04:00
} else {
match = BAN _REG . exec ( lines [ i ] ) ;
if ( match ) {
if ( ! match [ 2 ] )
lines [ i ] += reason ;
break ;
}
2016-11-12 11:58:38 -05:00
}
2016-05-22 14:08:11 -04:00
}
}
2015-07-13 21:52:44 -04:00
2017-08-16 13:24:55 -04:00
return lines ;
} ,
2015-07-13 21:52:44 -04:00
2017-08-16 13:24:55 -04:00
add _btn _click = function ( cmd ) {
var user = t . get ( 'cardInfo.user' ) ,
chat _controller = utils . ember _lookup ( 'controller:chat' ) ,
room = chat _controller && chat _controller . get ( 'currentRoom' ) ;
2016-10-05 02:16:36 -04:00
2017-08-16 13:24:55 -04:00
if ( ! room )
return ;
2016-10-05 01:31:10 -04:00
2017-08-16 13:24:55 -04:00
var lines = build _cmd ( user , room , cmd ) ;
for ( var i = 0 ; i < lines . length ; i ++ )
room . send ( lines [ i ] , true ) ;
} ,
2016-10-05 01:31:10 -04:00
2017-08-16 13:24:55 -04:00
add _btn _make = function ( label , cmd ) {
var btn = utils . createElement ( 'button' , 'button ffz-no-bg' , utils . sanitize ( label ) ) ;
2016-10-05 01:31:10 -04:00
2017-08-16 13:24:55 -04:00
jQuery ( btn ) . tipsy ( {
html : true ,
gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) ,
title : function ( ) {
var user = t . get ( 'cardInfo.user' ) ,
chat _controller = utils . ember _lookup ( 'controller:chat' ) ,
room = chat _controller && chat _controller . get ( 'currentRoom' ) ;
2017-02-26 19:11:28 -05:00
2017-08-16 13:24:55 -04:00
lines = build _cmd ( user , room , cmd ) ,
title = _ . map ( lines , utils . sanitize ) . join ( '<br>' ) ;
2016-10-05 01:31:10 -04:00
2017-08-16 13:24:55 -04:00
return "Custom Command" + ( lines . length > 1 ? 's' : '' ) +
"<br>" + title ;
}
} ) ;
2015-07-13 21:52:44 -04:00
2017-08-16 13:24:55 -04:00
btn . addEventListener ( 'click' , add _btn _click . bind ( this , cmd ) ) ;
return btn ;
} ;
2015-07-13 21:52:44 -04:00
2017-08-16 13:24:55 -04:00
for ( var i = 0 ; i < f . settings . mod _card _buttons . length ; i ++ ) {
var label , cmd , pair = f . settings . mod _card _buttons [ i ] ;
if ( ! Array . isArray ( pair ) ) {
cmd = pair ;
label = cmd . split ( ' ' , 1 ) [ 0 ] ;
} else {
label = pair [ 0 ] ;
cmd = pair [ 1 ] ;
}
2016-05-24 19:24:45 -04:00
2017-08-16 13:24:55 -04:00
utils . CMD _VAR _REGEX . lastIndex = 0 ;
if ( ! utils . CMD _VAR _REGEX . test ( cmd ) ) {
var lines = cmd . split ( /\s*<LINE>\s*/g ) ;
lines [ 0 ] += ' {user}' ;
cmd = lines . join ( "<LINE>" ) ;
}
2015-07-13 21:52:44 -04:00
2017-08-16 13:24:55 -04:00
line . appendChild ( add _btn _make ( label , cmd ) ) ;
2016-07-13 02:06:50 -04:00
}
2016-06-22 14:23:03 -04:00
2017-08-16 13:24:55 -04:00
el . appendChild ( line ) ;
}
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
var btn _click = function ( timeout ) {
2016-12-03 22:44:26 -05:00
var user _id = t . get ( 'cardInfo.user.id' ) ,
2016-07-13 02:06:50 -04:00
room = utils . ember _lookup ( 'controller:chat' ) . get ( 'currentRoom' ) ;
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
if ( timeout === - 1 )
room . send ( "/unban " + user _id , true ) ;
else
room . send ( "/timeout " + user _id + " " + timeout + ban _reason ( ) , true ) ;
} ,
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
btn _make = function ( timeout ) {
var btn = utils . createElement ( 'button' , 'button ffz-no-bg' ) ;
btn . innerHTML = utils . duration _string ( timeout ) ;
btn . title = "Timeout User for " + utils . number _commas ( timeout ) + " Second" + ( timeout != 1 ? "s" : "" ) ;
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
if ( f . settings . mod _card _hotkeys && timeout === 600 )
btn . title = "(T)" + btn . title . substr ( 1 ) ;
else if ( f . settings . mod _card _hotkeys && timeout === 1 )
btn . title = "(P)urge - " + btn . title ;
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
jQuery ( btn ) . tipsy ( { gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
btn . addEventListener ( 'click' , btn _click . bind ( this , timeout ) ) ;
return btn ;
} ;
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
if ( f . settings . mod _card _durations && f . settings . mod _card _durations . length ) {
// Extra Moderation
2017-08-16 13:24:55 -04:00
line = utils . createElement ( 'div' , 'extra-interface ffz-extra-controls moderation-card__actions clearfix' ) ;
2016-07-13 02:06:50 -04:00
line . appendChild ( btn _make ( 1 ) ) ;
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
var s = utils . createElement ( 'span' , 'right' ) ;
line . appendChild ( s ) ;
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
for ( var i = 0 ; i < f . settings . mod _card _durations . length ; i ++ )
s . appendChild ( btn _make ( f . settings . mod _card _durations [ i ] ) ) ;
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
el . appendChild ( line ) ;
2015-07-04 17:06:36 -04:00
2016-07-13 02:06:50 -04:00
// Fix Other Buttons
this . $ ( "button.timeout" ) . remove ( ) ;
}
2016-05-22 14:08:11 -04:00
2016-07-13 02:06:50 -04:00
if ( f . settings . mod _card _reasons && f . settings . mod _card _reasons . length ) {
// Moderation Reasons
2017-08-16 13:24:55 -04:00
line = utils . createElement ( 'div' , 'extra-interface ffz-extra-controls moderation-card__actions clearfix' ) ;
2016-07-13 02:06:50 -04:00
ban _reasons = utils . createElement ( 'select' , 'ffz-ban-reasons' , '<option value="">Select a Ban ' + ( f . settings . mod _card _hotkeys ? '(R)' : 'R' ) + 'eason</option>' ) ;
line . appendChild ( ban _reasons ) ;
2016-05-22 14:08:11 -04:00
2016-07-13 02:06:50 -04:00
for ( var i = 0 ; i < f . settings . mod _card _reasons . length ; i ++ ) {
var opt = utils . createElement ( 'option' ) , r = f . settings . mod _card _reasons [ i ] ;
opt . value = r ;
opt . textContent = ( i + 1 ) + ') ' + r ;
ban _reasons . appendChild ( opt ) ;
2016-05-22 14:08:11 -04:00
}
2016-07-13 02:06:50 -04:00
el . appendChild ( line ) ;
}
2016-05-22 14:08:11 -04:00
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
var ban _btn = el . querySelector ( 'button.ban' ) ;
2017-08-16 13:24:55 -04:00
if ( ban _btn ) {
if ( f . settings . mod _card _hotkeys )
ban _btn . setAttribute ( 'title' , '(B)an User' ) ;
2015-02-10 01:34:23 -05:00
2017-08-16 13:24:55 -04:00
// Unban Button
var unban _btn = utils . createElement ( 'button' , 'unban button button--icon-only light' ) ;
unban _btn . innerHTML = '<figure class="icon">' + CHECK + '</figure>' ;
unban _btn . title = ( f . settings . mod _card _hotkeys ? "(U)" : "U" ) + "nban User" ;
2015-02-10 01:34:23 -05:00
2017-08-16 13:24:55 -04:00
jQuery ( unban _btn ) . tipsy ( { gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
unban _btn . addEventListener ( "click" , btn _click . bind ( this , - 1 ) ) ;
2015-02-10 01:34:23 -05:00
2017-08-16 13:24:55 -04:00
jQuery ( ban _btn ) . after ( unban _btn ) ;
}
2016-07-13 02:06:50 -04:00
}
2016-06-22 14:23:03 -04:00
2016-07-13 02:06:50 -04:00
// Tooltips for ban and ignore.
jQuery ( "button.ignore, button.ban" ) . tipsy ( { gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
// More Fixing Other Buttons
var op _btn = el . querySelector ( 'button.mod' ) ;
if ( op _btn ) {
var can _op = is _broadcaster || ( user && user . is _admin ) || ( user && user . is _staff ) ;
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
if ( ! can _op )
op _btn . parentElement . removeChild ( op _btn ) ;
}
2016-05-06 02:23:12 -04:00
2016-07-13 02:06:50 -04:00
// Follow Button
var follow _button = el . querySelector ( ".follow-button" ) ;
if ( follow _button )
jQuery ( follow _button ) . tipsy ( { title : function ( ) { return follow _button . classList . contains ( 'is-following' ) ? "Unfollow" : "Follow" } } ) ;
2015-02-10 01:34:23 -05:00
2015-08-04 01:43:08 -04:00
2016-07-13 02:06:50 -04:00
// Whisper and Message Buttons
2016-11-29 23:24:12 -05:00
var msg _btn = el . querySelector ( "button.message-button" ) ;
2016-07-13 02:06:50 -04:00
if ( msg _btn ) {
msg _btn . innerHTML = 'W' ;
2016-11-29 23:24:12 -05:00
msg _btn . classList . remove ( 'button--hollow' ) ;
2016-07-13 02:06:50 -04:00
msg _btn . classList . add ( 'button--icon-only' ) ;
msg _btn . classList . add ( 'message' ) ;
2015-08-04 01:43:08 -04:00
2016-07-13 02:06:50 -04:00
msg _btn . title = "Whisper User" ;
jQuery ( msg _btn ) . tipsy ( { gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
2015-08-04 01:43:08 -04:00
2015-07-13 21:52:44 -04:00
2016-11-29 23:24:12 -05:00
var real _msg = utils . createElement ( 'button' , 'message-button button float-left button--icon-only message html-tooltip' ) ;
2016-07-13 02:06:50 -04:00
real _msg . innerHTML = '<figure class="icon">' + MESSAGE + '</figure>' ;
real _msg . title = "Message User" ;
2015-08-04 01:43:08 -04:00
2016-07-13 02:06:50 -04:00
real _msg . addEventListener ( 'click' , function ( ) {
2016-12-03 22:44:26 -05:00
window . open ( '//www.twitch.tv/message/compose?to=' + t . get ( 'cardInfo.user.id' ) ) ;
2016-07-13 02:06:50 -04:00
} )
2015-08-04 01:43:08 -04:00
2016-07-13 02:06:50 -04:00
msg _btn . parentElement . insertBefore ( real _msg , msg _btn . nextSibling ) ;
}
2015-08-04 01:43:08 -04:00
2017-08-16 13:24:55 -04:00
// Add the Alias button
if ( msg _btn )
msg _btn . parentElement . insertBefore ( alias _btn , msg _btn ) ;
else {
var follow _btn = el . querySelector ( ".friend-button" ) ;
if ( follow _btn )
follow _btn . parentElement . insertBefore ( alias _btn , follow _btn . nextSibling ) ;
}
} ,
2016-03-23 19:28:22 -04:00
2017-08-16 13:24:55 -04:00
ffz _init : function ( ) {
if ( f . has _bttv _6 )
return ;
2016-03-23 19:28:22 -04:00
2017-08-16 13:24:55 -04:00
f . _mod _card = this ;
if ( f . settings . logviewer _test ) {
this . _lv _callback = this . lvOnMessage . bind ( this ) ;
f . lv _ws _add _callback ( this . _lv _callback ) ;
}
var el = this . get ( 'element' ) ,
t = this ,
line ,
is _mod = t . get ( 'cardInfo.isModeratorOrHigher' ) ,
ban _reasons ,
chat = utils . ember _lookup ( 'controller:chat' ) ,
user = f . get _user ( ) ,
room = chat && chat . get ( 'currentRoom' ) ,
room _id = room && room . get ( 'id' ) ,
ffz _room = f . rooms && f . rooms [ room _id ] || { } ,
is _broadcaster = user && room _id === user . login ,
user _id = this . get ( 'cardInfo.user.id' ) ,
is _me = user && user . login === user _id ,
alias = f . aliases [ user _id ] ,
handle _key ,
ban _reason = function ( ) {
return ban _reasons && ban _reasons . value ? ' ' + ban _reasons . value : "" ;
} ;
this . ffz _room _id = room _id ;
// Should we be requesting a token? How about access levels?
if ( f . settings . logviewer _test && ffz _room . has _logs )
if ( ! ffz _room . logviewer _levels || ( user && user . login && ! ffz _room . logviewer _levels . me . valid ) )
f . lv _get _token ( ) . then ( function ( token ) {
if ( ! token )
2016-07-13 02:06:50 -04:00
return ;
2016-03-23 19:28:22 -04:00
2017-08-16 13:24:55 -04:00
utils . logviewer . get ( "channel/" + room _id , token )
. then ( utils . json ) . then ( function ( result ) {
f . log ( "[LV] Channel Info: " + room _id , result ) ;
ffz _room . logviewer _levels = result ;
t . lvUpdateLevels ( result ) ;
} ) ;
} )
else
t . lvUpdateLevels ( ffz _room . logviewer _levels ) ;
2016-03-23 19:28:22 -04:00
2015-08-04 01:43:08 -04:00
2017-08-16 13:24:55 -04:00
// Highlight this user's chat messages.
if ( f . settings . highlight _messages _with _mod _card )
utils . update _css ( f . _chat _style , 'mod-card-highlight' , styles [ 'chat-user-bg' ] . replace ( /{user_id}/g , user _id ) ) ;
2015-02-10 01:34:23 -05:00
2016-09-09 17:34:20 -04:00
2017-08-16 13:24:55 -04:00
// Action Override
this . set ( 'banAction' , function ( e ) {
var room = utils . ember _lookup ( 'controller:chat' ) . get ( 'currentRoom' ) ;
room . send ( "/ban " + e . user + ban _reason ( ) , true ) ;
2016-07-13 02:06:50 -04:00
} ) ;
2017-08-16 13:24:55 -04:00
this . set ( 'timeoutAction' , function ( e ) {
var room = utils . ember _lookup ( 'controller:chat' ) . get ( 'currentRoom' ) ;
room . send ( "/timeout " + e . user + " 600 " + ban _reason ( ) , true ) ;
} ) ;
// Alias Display
if ( alias ) {
var name = el . querySelector ( '.moderation-card__name a' ) ;
if ( name ) {
name . classList . add ( 'ffz-alias' ) ;
var results = f . format _display _name ( this . get ( 'cardInfo.user.display_name' ) , user _id ) ;
name . innerHTML = results [ 0 ] ;
name . title = results [ 1 ] || '' ;
if ( results [ 1 ] )
jQuery ( name ) . tipsy ( { html : true , gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
}
}
// Style it!
el . classList . add ( 'ffz-moderation-card' ) ;
// Info-tize it!
if ( f . settings . mod _card _info ) {
var info = utils . createElement ( 'div' , 'info channel-stats' ) ,
after = el . querySelector ( '.moderation-card__name' ) ;
if ( after ) {
el . classList . add ( 'ffz-has-info' ) ;
after . parentElement . insertBefore ( info , after . nextSibling ) ;
this . ffzRebuildInfo ( ) ;
}
}
// Key Handling
el . setAttribute ( 'tabindex' , 1 ) ;
if ( f . settings . mod _card _hotkeys ) {
el . classList . add ( 'no-mousetrap' ) ;
handle _key = function ( e ) {
var key = e . keyCode || e . which ,
is _meta = e . ctrlKey || e . altKey || e . metaKey ,
tag = e . target && e . target . tagName ,
user _id = t . get ( 'cardInfo.user.id' ) ,
is _mod = t . get ( 'cardInfo.isModeratorOrHigher' ) ,
room = utils . ember _lookup ( 'controller:chat' ) . get ( 'currentRoom' ) ;
// We don't want modifier keys. Also don't override input to input elements.
if ( is _meta || tag === 'TEXTAREA' || tag === 'INPUT' )
return ;
if ( key === keycodes . C )
return t . ffzChangePage ( 'default' ) ;
else if ( t . lv _view && key === keycodes . H )
return t . ffzChangePage ( 'history' ) ;
else if ( key === keycodes . S )
return t . ffzChangePage ( 'stats' ) ;
else if ( key === keycodes . Y )
return t . ffzChangePage ( 'name_history' ) ;
else if ( t . lv _view _notes && key === keycodes . N )
return t . ffzChangePage ( 'notes' ) ;
if ( is _mod && key == keycodes . P )
room . send ( "/timeout " + user _id + " 1" + ban _reason ( ) , true ) ;
else if ( is _mod && key == keycodes . B )
room . send ( "/ban " + user _id + ban _reason ( ) , true ) ;
else if ( is _mod && key == keycodes . T )
room . send ( "/timeout " + user _id + " 600" + ban _reason ( ) , true ) ;
else if ( is _mod && key == keycodes . U )
room . send ( "/unban " + user _id , true ) ;
else if ( is _mod && ban _reasons && key == keycodes . R ) {
var event = document . createEvent ( 'MouseEvents' ) ;
event . initMouseEvent ( 'mousedown' , true , true , window ) ;
ban _reasons . focus ( ) ;
ban _reasons . dispatchEvent ( event ) ;
return ;
}
else if ( key == keycodes . ESC && e . target === ban _reasons ) {
el . focus ( ) ;
return ;
}
else if ( key != keycodes . ESC )
return ;
t . get ( 'closeAction' ) ( ) ;
} ;
el . addEventListener ( 'keyup' , handle _key ) ;
2016-07-13 02:06:50 -04:00
}
2015-02-10 01:34:23 -05:00
2015-11-07 22:56:15 -05:00
2017-08-16 13:24:55 -04:00
// Only do the main controls row if we're not looking at ourselves.
this . ffzFixDefaultActions ( ) ;
2016-10-05 01:31:10 -04:00
// Tabbed Content
2016-11-29 23:24:12 -05:00
var tabs = utils . createElement ( 'ul' , 'moderation-card__actions menu clearfix' ) ,
2016-10-05 01:31:10 -04:00
tab _container = utils . createElement ( 'div' , 'ffz-tab-container' ) ;
for ( var page _id in FFZ . mod _card _pages ) {
var page = FFZ . mod _card _pages [ page _id ] ;
2016-10-07 18:11:05 -04:00
if ( page && page . title ) {
2016-10-05 01:31:10 -04:00
var tab = utils . createElement ( 'li' , 'item' , page . title ) ;
if ( page _id === 'default' )
tab . classList . add ( 'active' ) ;
2017-02-26 19:11:28 -05:00
if ( page . needs _lv )
tab . classList . add ( 'needs-lv' ) ;
2016-10-05 01:31:10 -04:00
tab . setAttribute ( 'data-page' , page _id ) ;
tabs . appendChild ( tab ) ;
tab . addEventListener ( 'click' , function ( e ) {
t . ffzChangePage ( this . getAttribute ( 'data-page' ) , tabs , tab _container ) ;
} ) ;
}
}
2017-08-16 13:24:55 -04:00
el . insertBefore ( tab _container , el . firstElementChild . nextSibling ) ;
//el.insertBefore(tab_container, el.querySelector('.moderation-card__actions'));
2016-10-05 01:31:10 -04:00
el . insertBefore ( tabs , tab _container ) ;
el . classList . add ( 'ffz-default-tab' ) ;
2016-07-13 02:06:50 -04:00
// Message History
2016-10-07 18:11:05 -04:00
if ( f . settings . mod _card _history )
this . ffzRenderHistory ( ) ;
2015-07-06 00:09:21 -04:00
2016-07-13 02:06:50 -04:00
// Reposition the menu if it's off-screen.
this . ffzReposition ( ) ;
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
// Focus the Element
this . $ ( ) . draggable ( {
start : function ( ) {
el . focus ( ) ;
} } ) ;
2015-02-10 01:34:23 -05:00
2016-07-13 02:06:50 -04:00
el . focus ( ) ;
2015-11-19 02:45:56 -05:00
} ,
2016-10-05 01:31:10 -04:00
ffzChangePage : function ( page _id , tabs , tab _container ) {
if ( ! tabs || ! tab _container ) {
var el = this . get ( 'element' ) ;
tabs = el . querySelector ( 'ul.menu' ) ;
tab _container = el . querySelector ( '.ffz-tab-container' ) ;
}
var active _page = tab _container . getAttribute ( 'data-page' ) ;
if ( active _page === page _id )
return ;
if ( page _id === null )
page _id = active _page ;
jQuery ( '.item' , tabs ) . removeClass ( 'active' ) ;
jQuery ( '.item[data-page="' + page _id + '"]' ) . addClass ( 'active' ) ;
this . get ( 'element' ) . classList . toggle ( 'ffz-default-tab' , page _id === 'default' ) ;
tab _container . setAttribute ( 'data-page' , page _id ) ;
tab _container . innerHTML = '' ;
FFZ . mod _card _pages [ page _id ] . render . call ( f , this , tab _container ) ;
} ,
2016-06-22 14:23:03 -04:00
ffzReposition : function ( ) {
var el = this . get ( 'element' ) ,
el _bound = el . getBoundingClientRect ( ) ,
body _bound = document . body . getBoundingClientRect ( ) ,
renderBottom = this . get ( 'cardInfo.renderBottom' ) ,
renderRight = this . get ( 'cardInfo.renderRight' ) ;
if ( renderRight ) {
var offset = ( el _bound . left + el _bound . width ) - renderRight ;
el . style . left = ( el _bound . left - offset ) + "px" ;
}
if ( renderBottom ) {
var offset = el _bound . bottom - renderBottom ;
el . style . top = ( el _bound . top - offset ) + "px" ;
} else if ( el _bound . bottom > body _bound . bottom ) {
var offset = el _bound . bottom - body _bound . bottom ;
if ( el _bound . top - offset > body _bound . top )
el . style . top = ( el _bound . top - offset ) + "px" ;
}
} . observes ( 'cardInfo.renderTop' , 'cardInfo.renderLeft' , 'cardInfo.renderRight' , 'cardInfo.renderBottom' ) ,
2016-10-07 18:11:05 -04:00
ffzRenderHistory : function ( ) {
2015-12-12 13:28:35 -05:00
var t = this ,
2016-03-23 20:23:04 -04:00
Chat = utils . ember _lookup ( 'controller:chat' ) ,
2016-10-07 18:11:05 -04:00
room _id = Chat && Chat . get ( 'currentRoom.id' ) ,
2015-12-12 13:28:35 -05:00
user _id = this . get ( 'cardInfo.user.id' ) ,
2016-10-07 18:11:05 -04:00
ffz _room = f . rooms && f . rooms [ room _id ] ,
2017-04-22 22:34:22 -04:00
chat _history = ffz _room && ffz _room . user _history ? ( ffz _room . user _history [ user _id ] || [ ] ) : null ,
2016-10-05 01:31:10 -04:00
2016-10-07 18:11:05 -04:00
el = this . get ( 'element' ) ,
history = el . querySelector ( '.chat-history.live-history' ) ;
2015-12-12 13:28:35 -05:00
2017-04-22 22:34:22 -04:00
if ( chat _history === null ) {
if ( history )
jQuery ( history ) . remove ( ) ;
return ;
}
2015-12-12 13:28:35 -05:00
if ( ! history ) {
2016-11-29 23:24:12 -05:00
history = utils . createElement ( 'ul' , 'moderation-card__actions chat-history live-history' ) ;
2015-12-12 13:28:35 -05:00
el . appendChild ( history ) ;
2016-10-07 18:11:05 -04:00
} else
2015-12-12 13:28:35 -05:00
history . innerHTML = '' ;
2015-11-19 02:45:56 -05:00
2016-10-07 18:11:05 -04:00
for ( var i = 0 ; i < chat _history . length ; i ++ )
history . appendChild ( f . _build _mod _card _history ( chat _history [ i ] , t , false ) ) ;
2015-11-19 02:45:56 -05:00
2016-10-07 18:11:05 -04:00
setTimeout ( function ( ) { history . scrollTop = history . scrollHeight } ) ;
}
2015-11-19 02:45:56 -05:00
} ) ;
2015-02-10 01:34:23 -05:00
}
2017-08-16 13:24:55 -04:00
FFZ . prototype . _build _mod _card _history = function ( msg , modcard , show _from , ts _click , mod _icons , show _ts ) {
2016-10-18 14:06:20 -04:00
var l _el = utils . createElement ( 'li' , 'message-line chat-line clearfix' ) ,
2015-11-19 02:45:56 -05:00
out = [ ] ,
2016-10-18 14:06:20 -04:00
f = this ,
2015-11-11 16:03:27 -05:00
2016-10-18 14:06:20 -04:00
is _notice = msg . style === 'admin' || msg . style === 'notification' ,
2015-11-19 02:45:56 -05:00
style = '' , colored = '' ;
2017-08-16 13:24:55 -04:00
if ( helpers && helpers . getTime && show _ts !== false )
2016-10-05 02:16:36 -04:00
out . push ( '<span class="timestamp' + ( ts _click ? ' ts-action' : '' ) + '">' + helpers . getTime ( msg . date , true ) + '</span>' ) ;
2015-11-19 02:45:56 -05:00
2016-10-05 02:16:36 -04:00
var alias = this . aliases [ msg . from ] ,
results = this . format _display _name ( msg . tags && msg . tags [ 'display-name' ] , msg . from ) ;
2016-09-09 17:34:20 -04:00
2016-10-07 18:11:05 -04:00
if ( mod _icons ) {
out . push ( '<span class="mod-icons">' ) ;
if ( typeof mod _icons === "string" )
out . push ( mod _icons ) ;
out . push ( '</span>' ) ;
}
2016-10-18 14:06:20 -04:00
if ( show _from && ! is _notice ) {
2015-11-19 02:45:56 -05:00
// Badges
2016-07-13 02:06:50 -04:00
out . push ( '<span class="badges">' ) ;
2015-11-19 02:45:56 -05:00
out . push ( this . render _badges ( this . get _line _badges ( msg , false ) ) ) ;
out . push ( '</span>' ) ;
// Colors
var raw _color = msg . color ,
colors = raw _color && this . _handle _color ( raw _color ) ,
2016-06-02 20:04:40 -04:00
Layout = utils . ember _lookup ( 'service:layout' ) ,
2017-09-26 17:01:51 -04:00
ThemeManager = utils . ember _lookup ( 'service:theme-manager' ) ,
2016-09-30 13:09:03 -04:00
Settings = utils . ember _settings ( ) ,
2015-11-19 02:45:56 -05:00
2017-09-26 17:01:51 -04:00
is _dark = ( ThemeManager && ThemeManager . get ( 'themes.activeTheme' ) === 'theme--dark' ) || ( Layout && Layout . get ( 'isTheatreMode' ) ) || this . settings . get _twitch ( "darkMode" ) ;
2015-11-19 02:45:56 -05:00
2016-09-09 17:34:20 -04:00
// Styling
2015-12-12 13:28:35 -05:00
var style = colors && 'color:' + ( is _dark ? colors [ 1 ] : colors [ 0 ] ) ,
2015-11-19 02:45:56 -05:00
colored = style ? ' has-color' : '' ;
2016-09-09 17:34:20 -04:00
out . push ( '<span class="from' +
( alias ? ' ffz-alias' : '' ) +
( results [ 1 ] ? ' html-tooltip' : '' ) +
( style ? ' has-color' : '' ) +
2017-08-16 13:24:55 -04:00
'" data-user="' + utils . quote _attr ( msg . from ) + '" style="' + style + '"' +
2016-09-09 17:34:20 -04:00
( colors ? ' data-color="' + raw _color + '"' : '' ) +
( results [ 1 ] ? ' title="' + utils . quote _attr ( results [ 1 ] ) + '"' : '' ) + '>'
+ results [ 0 ] + '</span>' ) ;
2015-11-19 02:45:56 -05:00
2016-10-05 01:31:10 -04:00
out . push ( msg . style !== 'action' ? '<span class="colon">:</span> ' : ' ' ) ;
2016-10-18 14:06:20 -04:00
} else if ( ! is _notice )
2016-10-05 02:16:36 -04:00
out . push ( '<span class="cp-hidden"> ' + results [ 0 ] + ( msg . style === 'action' ? '' : ':' ) + ' </span>' ) ;
2015-11-19 02:45:56 -05:00
// The message itself.
if ( msg . style !== 'action' ) {
style = '' ;
colored = '' ;
}
2017-02-03 02:01:18 -05:00
// Use cached tokens on the off chance we have them, but don't count on them.
var tokens = msg . cachedTokens || this . tokenize _chat _line ( msg , true , false , true ) ,
2017-01-30 16:36:33 -05:00
message = '<span class="message' + colored + '" style="' + style + ( colors ? '" data-color="' + raw _color : '' ) + '">' +
( msg . style === 'action' && ! show _from ? '*' + name + ' ' : '' ) + this . render _tokens ( tokens , true , false , msg . tags && msg . tags . bits ) + '</span>' ;
2015-11-19 02:45:56 -05:00
if ( msg . deleted )
out . push ( '<span class="deleted"><a class="undelete" href="#" data-message="' + utils . quote _attr ( message ) + '"><message deleted></a></span>' ) ;
else
out . push ( message ) ;
// Line attributes and classes.
if ( msg . style )
2016-10-20 16:41:04 -04:00
l _el . className += ' ' + msg . style ;
2015-11-19 02:45:56 -05:00
if ( msg . original _sender )
l _el . classList . add ( 'original-sender' ) ;
if ( msg . is _original )
l _el . classList . add ( 'original-msg' ) ;
if ( msg . ffz _has _mention )
2015-11-11 16:03:27 -05:00
l _el . classList . add ( 'ffz-mentioned' ) ;
2015-11-19 02:45:56 -05:00
if ( this . settings . prevent _clear && msg . ffz _deleted )
l _el . classList . add ( 'ffz-deleted' ) ;
l _el . setAttribute ( 'data-room' , msg . room ) ;
l _el . setAttribute ( 'data-sender' , msg . from ) ;
2016-07-13 02:06:50 -04:00
l _el . setAttribute ( 'data-id' , msg . tags && msg . tags . id ) ;
2016-10-05 01:31:10 -04:00
l _el . setAttribute ( 'data-lv-id' , msg . lv _id ) ;
2016-12-23 13:40:05 -05:00
l _el . setAttribute ( 'data-date' , msg . date && msg . date . toLocaleDateString ( ) ) ;
2015-11-19 02:45:56 -05:00
l _el . setAttribute ( 'data-deleted' , msg . deleted || false ) ;
l _el . innerHTML = out . join ( "" ) ;
2015-11-11 16:03:27 -05:00
// Interactivity
2015-11-19 02:45:56 -05:00
jQuery ( 'a.undelete' , l _el ) . click ( function ( e ) { this . parentElement . outerHTML = this . getAttribute ( 'data-message' ) ; } ) ;
2016-07-13 02:31:26 -04:00
jQuery ( '.deleted-word' , l _el ) . click ( function ( e ) { jQuery ( this ) . trigger ( 'mouseout' ) ; this . outerHTML = this . getAttribute ( 'data-text' ) ; } ) ;
2015-11-11 16:03:27 -05:00
jQuery ( 'a.deleted-link' , l _el ) . click ( f . _deleted _link _click ) ;
jQuery ( 'img.emoticon' , l _el ) . click ( function ( e ) { f . _click _emote ( this , e ) } ) ;
2015-11-19 02:45:56 -05:00
if ( modcard ) {
2017-04-21 20:02:27 -04:00
modcard . get ( 'cardInfo.user.id' ) !== msg . from && jQuery ( '.from' , l _el ) . click ( function ( e ) {
2015-11-19 02:45:56 -05:00
var el = modcard . get ( 'element' ) ;
2017-04-21 20:02:27 -04:00
el && f . _roomv && f . _roomv . get ( 'room.id' ) === msg . room && f . _roomv . actions . showModOverlay . call ( f . _roomv , {
2015-11-19 02:45:56 -05:00
sender : msg . from ,
top : parseInt ( el . style . top ) ,
left : parseInt ( el . style . left )
} ) ;
} ) ;
2016-10-05 01:31:10 -04:00
ts _click && l _el . querySelector ( '.timestamp' ) . addEventListener ( 'click' , function ( e ) {
2015-11-19 02:45:56 -05:00
if ( e . button === 0 )
2016-10-05 01:31:10 -04:00
return ts _click . call ( this , e ) ;
2015-11-19 02:45:56 -05:00
} ) ;
}
2015-11-11 16:03:27 -05:00
return l _el ;
}
2015-07-31 17:44:20 -04:00
// ----------------
// Aliases
// ----------------
FFZ . prototype . _update _alias = function ( user ) {
var alias = this . aliases && this . aliases [ user ] ,
2016-09-09 17:34:20 -04:00
results = this . format _display _name ( FFZ . get _capitalization ( user ) , user ) ,
2015-07-31 17:44:20 -04:00
el = this . _roomv && this . _roomv . get ( 'element' ) ,
lines = el && el . querySelectorAll ( '.chat-line[data-sender="' + user + '"]' ) ;
2015-08-04 01:43:08 -04:00
2015-07-31 17:44:20 -04:00
if ( ! lines )
return ;
for ( var i = 0 , l = lines . length ; i < l ; i ++ ) {
var line = lines [ i ] ,
el _from = line . querySelector ( '.from' ) ;
2015-08-04 01:43:08 -04:00
2016-07-13 02:31:26 -04:00
if ( ! el _from )
continue ;
2016-03-23 19:28:22 -04:00
2015-07-31 17:44:20 -04:00
el _from . classList . toggle ( 'ffz-alias' , alias ) ;
2016-09-09 17:34:20 -04:00
el _from . classList . toggle ( 'html-tooltip' , results [ 1 ] || false ) ;
el _from . innerHTML = results [ 0 ] ;
el _from . title = results [ 1 ] || '' ;
2015-07-31 17:44:20 -04:00
}
2015-11-19 02:45:56 -05:00
2016-06-22 14:23:03 -04:00
// Update tab completion.
2016-07-13 02:31:26 -04:00
if ( this . _inputv )
Ember . propertyDidChange ( this . _inputv , 'ffz_name_suggestions' ) ;
2016-06-22 14:23:03 -04:00
2015-11-19 02:45:56 -05:00
// TODO: Update conversations~
2015-07-31 17:44:20 -04:00
}
2015-02-10 01:34:23 -05:00
// ----------------
// Chat Commands
// ----------------
2015-07-04 17:06:36 -04:00
FFZ . chat _commands . purge = function ( room , args ) {
2015-02-10 01:34:23 -05:00
if ( ! args || ! args . length )
2016-10-12 16:42:43 -04:00
return "Usage: /purge <user> [ban reason]" ;
2015-02-10 01:34:23 -05:00
2016-09-09 17:34:20 -04:00
var name = args . shift ( ) ,
reason = args . length ? args . join ( " " ) : "" ;
2015-02-10 01:34:23 -05:00
2016-09-09 17:34:20 -04:00
room . room . send ( "/timeout " + name + " 1 " + reason , true ) ;
2015-02-10 01:34:23 -05:00
}
2016-10-12 16:42:43 -04:00
FFZ . chat _commands . purge . label = '/purge <user> <i>[reason]</i>' ;
FFZ . chat _commands . purge . info = 'Ban User for 1 Second' ;
2015-07-04 17:06:36 -04:00
FFZ . chat _commands . p = function ( room , args ) {
2016-03-23 19:28:22 -04:00
return FFZ . chat _commands . purge . call ( this , room , args ) ;
2015-07-04 17:06:36 -04:00
}
FFZ . chat _commands . p . enabled = function ( ) { return this . settings . short _commands ; }
2016-10-12 16:42:43 -04:00
FFZ . chat _commands . p . short = true ;
FFZ . chat _commands . p . label = '/p <user> <i>[reason]</i>' ;
FFZ . chat _commands . p . info = 'Ban User for 1 Second' ;
2015-02-10 01:34:23 -05:00
FFZ . chat _commands . t = function ( room , args ) {
if ( ! args || ! args . length )
2016-10-12 16:42:43 -04:00
return "Usage: /t <user> [duration=600] [reason]" ;
2015-10-17 18:05:44 -04:00
room . room . send ( "/timeout " + args . join ( " " ) , true ) ;
2015-02-10 01:34:23 -05:00
}
2015-07-04 17:06:36 -04:00
FFZ . chat _commands . t . enabled = function ( ) { return this . settings . short _commands ; }
2016-10-12 16:42:43 -04:00
FFZ . chat _commands . t . short = true ;
FFZ . chat _commands . t . label = '/t <user> <i>[duration=600] [reason]</i>' ;
FFZ . chat _commands . t . info = 'Temporarily Ban User' ;
2015-02-10 01:34:23 -05:00
FFZ . chat _commands . b = function ( room , args ) {
if ( ! args || ! args . length )
2016-10-12 16:42:43 -04:00
return "Usage: /b <user> [reason]" ;
2015-02-10 01:34:23 -05:00
2016-09-09 17:34:20 -04:00
var name = args . shift ( ) ,
reason = args . length ? args . join ( " " ) : "" ;
2015-02-10 01:34:23 -05:00
2016-09-09 17:34:20 -04:00
room . room . send ( "/ban " + name + " " + reason , true ) ;
2015-02-10 01:34:23 -05:00
}
2015-07-04 17:06:36 -04:00
FFZ . chat _commands . b . enabled = function ( ) { return this . settings . short _commands ; }
2016-10-12 16:42:43 -04:00
FFZ . chat _commands . b . short = true ;
FFZ . chat _commands . b . label = '/b <user> <i>[reason]</i>' ;
FFZ . chat _commands . b . info = 'Permanently Ban User' ;
2015-02-10 01:34:23 -05:00
FFZ . chat _commands . u = function ( room , args ) {
if ( ! args || ! args . length )
2016-10-12 16:42:43 -04:00
return "Usage: /u <user> [more usernames separated by spaces]" ;
2015-02-10 01:34:23 -05:00
if ( args . length > 10 )
return "Please only unban up to 10 users at once." ;
for ( var i = 0 ; i < args . length ; i ++ ) {
var name = args [ i ] ;
if ( name )
2015-10-17 18:05:44 -04:00
room . room . send ( "/unban " + name , true ) ;
2015-02-10 01:34:23 -05:00
}
}
2016-10-12 16:42:43 -04:00
FFZ . chat _commands . u . enabled = function ( ) { return this . settings . short _commands ; }
FFZ . chat _commands . u . short = true ;
FFZ . chat _commands . u . label = '/u <user> <i>[<user> ...]' ;
FFZ . chat _commands . u . info = 'Unban User(s)' ;