2015-06-05 03:59:28 -04:00
var FFZ = window . FrankerFaceZ ,
utils = require ( "./utils" ) ,
2015-07-04 17:06:36 -04:00
constants = require ( "./constants" ) ,
2015-06-05 03:59:28 -04:00
helpers ,
2015-11-14 23:52:49 -05:00
conv _helpers ,
2015-06-05 03:59:28 -04:00
2016-03-23 19:28:22 -04:00
EXPLANATION _WARN = '<hr>This link has been sent to you via a whisper rather than standard chat, and has not been checked or approved of by any moderators or staff members. Please treat this link with caution and do not visit it if you do not trust the sender.' ,
2015-07-29 01:03:10 -04:00
2015-06-05 03:59:28 -04:00
reg _escape = function ( str ) {
return str . replace ( /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g , "\\$&" ) ;
} ,
2016-03-23 19:28:22 -04:00
LINK = /(?:https?:\/\/)?(?:[-a-zA-Z0-9@:%_\+~#=]+\.)+[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)/g ,
2015-08-21 19:00:48 -04:00
2015-07-29 01:03:10 -04:00
LINK _SPLIT = /^(?:(https?):\/\/)?(?:(.*?)@)?([^\/:]+)(?::(\d+))?(.*?)(?:\?(.*?))?(?:\#(.*?))?$/ ,
YOUTUBE _CHECK = /^(?:https?:\/\/)?(?:m\.|www\.)?youtu(?:be\.com|\.be)\/(?:v\/|watch\/|.*?(?:embed|watch).*?v=)?([a-zA-Z0-9\-_]+)$/ ,
IMGUR _PATH = /^\/(?:gallery\/)?[A-Za-z0-9]+(?:\.(?:png|jpg|jpeg|gif|gifv|bmp))?$/ ,
IMAGE _EXT = /\.(?:png|jpg|jpeg|gif|bmp)$/i ,
IMAGE _DOMAINS = [ ] ,
2015-08-21 19:00:48 -04:00
2015-07-29 01:03:10 -04:00
is _image = function ( href , any _domain ) {
var match = href . match ( LINK _SPLIT ) ;
if ( ! match )
return ;
var domain = match [ 3 ] . toLowerCase ( ) , port = match [ 4 ] ,
path = match [ 5 ] ;
// Don't allow non-standard ports.
if ( port && port !== '80' && port !== '443' )
return false ;
// imgur-specific checks.
if ( domain === 'i.imgur.com' || domain === 'imgur.com' || domain === 'www.imgur.com' || domain === 'm.imgur.com' )
return IMGUR _PATH . test ( path ) ;
2015-08-21 19:00:48 -04:00
return any _domain ? IMAGE _EXT . test ( path ) : IMAGE _DOMAINS . indexOf ( domain ) !== - 1 ;
2015-11-14 23:52:49 -05:00
} ,
2015-08-21 19:00:48 -04:00
2015-07-29 01:03:10 -04:00
image _iframe = function ( href , extra _class ) {
2015-10-17 18:05:44 -04:00
return '<iframe class="ffz-image-hover' + ( extra _class ? ' ' + extra _class : '' ) + '" allowtransparency="true" src="' + constants . SERVER + 'script/img-proxy.html#' + utils . quote _attr ( href ) + '"></iframe>' ;
2015-07-29 01:03:10 -04:00
} ,
load _link _data = function ( href , success , data ) {
if ( ! success )
return ;
this . _link _data [ href ] = data ;
2015-10-24 22:38:04 -07:00
//data.unsafe = false;
2015-07-29 01:03:10 -04:00
2016-03-23 19:28:22 -04:00
if ( ! this . settings . link _info )
return ;
2015-07-29 01:03:10 -04:00
2016-03-23 19:28:22 -04:00
// If this link is unsafe, add the unsafe-link class to all instances of the link.
if ( data . unsafe )
jQuery ( 'a.chat-link[data-url="' + href + '"]' ) . addClass ( 'unsafe-link' ) ;
2015-07-29 01:03:10 -04:00
} ;
2015-06-05 03:59:28 -04:00
2015-07-04 17:06:36 -04:00
FFZ . SRC _IDS = { } ,
FFZ . src _to _id = function ( src ) {
if ( FFZ . SRC _IDS . hasOwnProperty ( src ) )
return FFZ . SRC _IDS [ src ] ;
var match = /\/emoticons\/v1\/(\d+)\/1\.0/ . exec ( src ) ,
id = match ? parseInt ( match [ 1 ] ) : null ;
2016-03-23 19:28:22 -04:00
if ( Number . isNaN ( id ) )
2015-07-04 17:06:36 -04:00
id = null ;
FFZ . SRC _IDS [ src ] = id ;
return id ;
} ;
2015-10-27 14:25:13 -04:00
FFZ . _emote _mirror _swap = function ( img ) {
var src , attempts = parseInt ( img . getAttribute ( 'data-alt-attempts' ) ) || 0 ;
if ( attempts > 3 )
return ;
img . setAttribute ( 'data-alt-attempts' , attempts + 1 ) ;
2016-03-23 19:28:22 -04:00
var id = img . getAttribute ( 'data-emote' ) ,
src = '//' + img . src . split ( '//' ) [ 1 ] ;
2015-10-27 14:25:13 -04:00
2016-03-23 19:28:22 -04:00
if ( src . substr ( 0 , constants . TWITCH _BASE . length ) === constants . TWITCH _BASE ) {
2015-10-27 14:25:13 -04:00
img . src = constants . EMOTE _MIRROR _BASE + id + ".png" ;
img . srcset = "" ;
} else {
2015-11-14 23:52:49 -05:00
img . src = constants . TWITCH _BASE + id + "/1.0" ;
img . srcset = utils . build _srcset ( id ) ;
2015-10-27 14:25:13 -04:00
}
}
2015-07-13 21:52:44 -04:00
// ---------------------
2015-07-18 21:10:27 -04:00
// Settings
2015-07-13 21:52:44 -04:00
// ---------------------
var ts = new Date ( 0 ) . toLocaleTimeString ( ) . toUpperCase ( ) ;
FFZ . settings _info . twenty _four _timestamps = {
type : "boolean" ,
value : ts . lastIndexOf ( 'PM' ) === - 1 && ts . lastIndexOf ( 'AM' ) === - 1 ,
category : "Chat Appearance" ,
no _bttv : true ,
name : "24hr Timestamps" ,
help : "Display timestamps in chat in the 24 hour format rather than 12 hour."
} ;
2015-08-28 22:54:12 -04:00
FFZ . settings _info . timestamp _seconds = {
type : "boolean" ,
value : false ,
category : "Chat Appearance" ,
no _bttv : true ,
name : "Timestamp Seconds" ,
help : "Display seconds in chat timestamps."
} ;
2015-07-18 21:10:27 -04:00
FFZ . settings _info . show _deleted _links = {
type : "boolean" ,
value : false ,
category : "Chat Moderation" ,
no _bttv : true ,
name : "Show Deleted Links" ,
help : "Do not delete links based on room settings or link length."
} ;
// ---------------------
// Setup
// ---------------------
FFZ . prototype . setup _tokenization = function ( ) {
2015-07-31 21:40:51 -04:00
// Tooltip Data
this . _twitch _emotes = { } ;
this . _twitch _emote _to _set = { } ;
this . _twitch _set _to _channel = { } ;
this . _link _data = { } ;
2015-08-21 19:00:48 -04:00
2015-07-31 21:40:51 -04:00
this . load _twitch _emote _data ( ) ;
2015-08-21 19:00:48 -04:00
2015-07-18 21:10:27 -04:00
helpers = window . require && window . require ( "ember-twitch-chat/helpers/chat-line-helpers" ) ;
if ( ! helpers )
return this . log ( "Unable to get chat helper functions." ) ;
2015-08-21 19:00:48 -04:00
2015-11-14 23:52:49 -05:00
conv _helpers = window . require && window . require ( "ember-twitch-conversations/helpers/conversation-line-helpers" ) ;
if ( ! conv _helpers )
this . log ( "Unable to get conversation helper functions." ) ;
2015-07-18 21:10:27 -04:00
this . log ( "Hooking Ember chat line helpers." ) ;
var f = this ;
2015-08-21 19:00:48 -04:00
2015-07-18 21:10:27 -04:00
// Timestamp Display
2015-07-13 21:52:44 -04:00
helpers . getTime = function ( e ) {
2015-07-18 21:10:27 -04:00
if ( e === undefined || e === null )
return '?:??' ;
2015-07-13 21:52:44 -04:00
var hours = e . getHours ( ) ,
2015-08-28 22:54:12 -04:00
minutes = e . getMinutes ( ) ,
seconds = e . getSeconds ( ) ;
2015-08-21 19:00:48 -04:00
2015-07-18 21:10:27 -04:00
if ( hours > 12 && ! f . settings . twenty _four _timestamps )
2015-07-13 21:52:44 -04:00
hours -= 12 ;
2015-07-18 21:10:27 -04:00
else if ( hours === 0 && ! f . settings . twenty _four _timestamps )
2015-07-13 21:52:44 -04:00
hours = 12 ;
2015-08-21 19:00:48 -04:00
2015-08-28 22:54:12 -04:00
return hours + ':' + ( minutes < 10 ? '0' : '' ) + minutes + ( f . settings . timestamp _seconds ? ':' + ( seconds < 10 ? '0' : '' ) + seconds : '' ) ;
2015-07-13 21:52:44 -04:00
} ;
2015-07-18 21:10:27 -04:00
// Linkify Messages
helpers . linkifyMessage = function ( tokens , delete _links ) {
var show _deleted = f . settings . show _deleted _links ;
2015-08-21 19:00:48 -04:00
2015-07-18 21:10:27 -04:00
return _ . chain ( tokens ) . map ( function ( token ) {
2016-03-23 19:28:22 -04:00
if ( token . type === "text" )
token = token . text ;
2015-07-18 21:10:27 -04:00
if ( ! _ . isString ( token ) )
return token ;
2015-08-21 19:00:48 -04:00
2015-07-18 21:10:27 -04:00
var matches = token . match ( LINK ) ;
if ( ! matches || ! matches . length )
return [ token ] ;
2015-08-21 19:00:48 -04:00
2015-07-18 21:10:27 -04:00
return _ . zip (
token . split ( LINK ) ,
_ . map ( matches , function ( e ) {
2016-03-23 19:28:22 -04:00
var long = e . length > 255 ,
out = {
type : "link" ,
length : e . length ,
isDeleted : ! show _deleted && ( delete _links || long ) ,
isLong : long ,
isMailTo : e . indexOf ( "@" ) > - 1 && ( - 1 === e . indexOf ( "/" ) || e . indexOf ( "@" ) < e . indexOf ( "/" ) ) ,
text : e ,
link : e
} ;
if ( ! out . isMailTo && ! e . match ( /^(?:https?:\/\/)/ ) )
out . link = "http://" + e ;
return out ;
2015-07-18 21:10:27 -04:00
} )
) ;
} ) . flatten ( ) . compact ( ) . value ( ) ;
} ;
}
2015-07-31 21:40:51 -04:00
// ---------------------
// Twitch Emote Data
// ---------------------
FFZ . prototype . load _twitch _emote _data = function ( tries ) {
2015-10-17 18:05:44 -04:00
jQuery . ajax ( constants . SERVER + "script/twitch_emotes.json" , { context : this } )
2015-07-31 21:40:51 -04:00
. done ( function ( data ) {
for ( var set _id in data ) {
var set = data [ set _id ] ;
if ( ! set )
continue ;
this . _twitch _set _to _channel [ set _id ] = set . name ;
for ( var i = 0 , l = set . emotes . length ; i < l ; i ++ )
this . _twitch _emote _to _set [ set . emotes [ i ] ] = set _id ;
}
2015-08-21 19:00:48 -04:00
2015-07-31 21:40:51 -04:00
this . _twitch _set _to _channel [ 0 ] = "--global--" ;
this . _twitch _set _to _channel [ 33 ] = "--turbo-faces--" ;
this . _twitch _set _to _channel [ 42 ] = "--turbo-faces--" ;
} ) . fail ( function ( data ) {
if ( data . status === 404 )
return ;
2015-08-21 19:00:48 -04:00
2015-07-31 21:40:51 -04:00
tries = ( tries || 0 ) + 1 ;
if ( tries < 10 )
setTimeout ( this . load _twitch _emote _data . bind ( this , tries ) , 1000 ) ;
} ) ;
}
2016-03-23 19:28:22 -04:00
// ---------------------
// Tooltip Rendering
// ---------------------
FFZ . prototype . render _tooltip = function ( el ) {
var f = this ,
func = function ( ) {
if ( this . classList . contains ( 'emoticon' ) ) {
var preview _url , width = 0 , height = 0 , image , set _id , emote , emote _set ,
emote _id = this . getAttribute ( 'data-ffz-emote' ) ;
if ( emote _id ) {
set _id = this . getAttribute ( 'data-ffz-set' ) ;
emote _set = f . emote _sets [ set _id ] ;
emote = emote _set && emote _set . emoticons [ emote _id ] ;
if ( emote ) {
var owner = emote . owner ,
title = emote _set . title || "Global" ,
source = emote _set . source || "FFZ" ;
if ( f . settings . emote _image _hover ) {
if ( emote . urls [ 4 ] ) {
height = emote . height * 4 ;
width = emote . width * 4 ;
preview _url = emote . urls [ 4 ] ;
} else if ( emote . urls [ 2 ] ) {
height = emote . height * 2 ;
width = emote . width * 2 ;
}
if ( width > 186 )
height *= 186 / width ;
height = Math . min ( 186 , height ) ;
} else
preview _url = null ;
//image = preview_url ? `<img style="height:${height}px" class="emoticon ffz-image-hover" src="${preview_url}?_=preview">` : '';
image = preview _url ? '<img style="height:' + height + 'px" class="emoticon ffz-image-hover" src="' + preview _url + '"?_=preview">' : '' ;
return image + 'Emoticon: ' + ( emote . hidden ? '???' : emote . name ) + '<br>' + source + ' ' + title + ( owner ? '<br>By: ' + owner . display _name : '' ) ;
//return `${image}Emoticon: ${emote.hidden ? '???' : emote.name}<br>${source} ${title}${owner ? '<br>By: ' + owner.display_name : ""}`;
}
}
emote _id = this . getAttribute ( 'data-emote' ) ;
if ( emote _id ) {
set _id = f . _twitch _emote _to _set [ emote _id ] ;
emote _set = set _id && f . _twitch _set _to _channel [ set _id ] ;
var set _type = "Channel" ;
preview _url = f . settings . emote _image _hover && ( constants . TWITCH _BASE + emote _id + '/3.0' ) ;
//image = preview_url ? `<img style="height:112px" class="emoticon ffz-image-hover" src="${preview_url}?_=preview">` : '';
image = preview _url ? '<img style="height:112px" class="emoticon ffz-image-hover" src="' + preview _url + '"?_=preview">' : '' ;
if ( emote _set === "--global--" ) {
emote _set = "Twitch Global" ;
set _type = null ;
} else if ( emote _set === "--twitch-turbo--" || emote _set === "turbo" || emote _set === "--turbo-faces--" ) {
emote _set = "Twitch Turbo" ;
set _type = null ;
}
if ( this . classList . contains ( 'ffz-tooltip-no-credit' ) )
return image + this . alt ;
else
return image + 'Emoticon: ' + this . alt + '<br>' + ( set _type ? set _type + ': ' : '' ) + emote _set ;
//return `${image}Emoticon: ${this.alt}<br>${set_type ? set_type + ": " : ""}${emote_set}`;
}
emote _id = this . getAttribute ( 'data-ffz-emoji' ) ;
if ( emote _id ) {
emote = f . emoji _data [ emote _id ] ;
var src = emote && ( f . settings . parse _emoji === 3 ? emote . one _src : ( f . settings . parse _emoji === 2 ? emote . noto _src : emote . tw _src ) ) ;
preview _url = f . settings . emote _image _hover && src ;
//image = preview_url ? `<img style="height:72px" class="emoticon ffz-image-hover" src="${preview_url}">` : '';
image = preview _url ? '<img style="height:72px" class="emoticon ffz-image-hover" src="' + preview _url + '"?_=preview">' : '' ;
return image + "Emoji: " + this . alt + '<br>Name: ' + emote . name + ( emote . short _name ? '<br>Short Name :' + emote . short _name + ':' : '' ) + ( emote . cat ? '<br>Category: ' + utils . sanitize ( constants . EMOJI _CATEGORIES [ emote . cat ] || emote . cat ) : '' ) ;
//return `${image}Emoji: ${this.alt}<br>Name: ${emote.name}${emote.short_name ? '<br>Short Name: :' + emote.short_name + ':' : ''}`;
}
} else if ( this . classList . contains ( 'email-link' ) ) {
var url = this . getAttribute ( "data-url" ) ;
return url ? "E-Mail " + url . substr ( 7 ) : '' ;
} else if ( this . classList . contains ( 'chat-link' ) ) {
// TODO: A lot of shit. Lookup data.
var url = this . getAttribute ( "data-url" ) ,
text = '' ;
if ( ! url )
return ;
if ( f . settings . link _image _hover && is _image ( url , f . settings . image _hover _all _domains ) )
preview _url = url ;
else
preview _url = null ;
image = preview _url ? image _iframe ( url ) : '' ;
// If it's not a deleted link, don't waste time showing the URL in the tooltip.
if ( this . classList . contains ( 'deleted-link' ) )
text = url ;
if ( this . classList . contains ( 'warn-link' ) )
text += EXPLANATION _WARN ;
return image + text ; //`${image}${text}`;
}
f . log ( "Unable to Build Tooltip For: " + this . className , this ) ;
return "" ;
} ;
return el ? func ( el ) : func ;
} ;
2015-06-05 03:59:28 -04:00
// ---------------------
// Tokenization
// ---------------------
2015-11-07 22:56:15 -05:00
FFZ . prototype . tokenize _conversation _line = function ( message , prevent _notification ) {
var msg = message . get ( 'body' ) ,
user = this . get _user ( ) ,
from _user = message . get ( 'from.username' ) ,
from _me = user && from _user === user . login ,
emotes = message . get ( 'tags.emotes' ) ,
tokens = [ msg ] ;
2015-11-14 23:52:49 -05:00
if ( conv _helpers && conv _helpers . checkActionMessage )
tokens = conv _helpers . checkActionMessage ( tokens ) ;
2015-11-07 22:56:15 -05:00
// Standard Tokenization
if ( helpers && helpers . linkifyMessage )
tokens = helpers . linkifyMessage ( tokens ) ;
if ( user && user . login && helpers && helpers . mentionizeMessage )
tokens = helpers . mentionizeMessage ( tokens , user . login , from _me ) ;
if ( helpers && helpers . emoticonizeMessage && emotes )
tokens = helpers . emoticonizeMessage ( tokens , emotes ) ;
// FrankerFaceZ Extras
tokens = this . _remove _banned ( tokens ) ;
tokens = this . tokenize _emotes ( from _user , undefined , tokens , from _me ) ;
if ( this . settings . parse _emoji )
tokens = this . tokenize _emoji ( tokens ) ;
// Capitalization
var display _name = message . get ( 'from.displayName' ) ;
if ( display _name && display _name . length )
FFZ . capitalization [ from _user ] = [ display _name . trim ( ) , Date . now ( ) ] ;
// Mentions!
if ( ! from _me )
tokens = this . tokenize _mentions ( tokens ) ;
// TODO: Notifications?
return tokens ;
}
2015-07-29 01:03:10 -04:00
FFZ . prototype . tokenize _chat _line = function ( msgObject , prevent _notification , delete _links ) {
2015-06-05 03:59:28 -04:00
if ( msgObject . cachedTokens )
return msgObject . cachedTokens ;
2016-03-23 19:28:22 -04:00
var msg = msgObject . get && ( msgObject . get ( 'message' ) || msgObject . get ( 'body' ) ) || msgObject . message ,
room _id = msgObject . get && msgObject . get ( 'room' ) || msgObject . room ,
from _user = msgObject . get && msgObject . get ( 'from' ) || msgObject . from ,
user = this . get _user ( ) ,
from _me = user && from _user === user . login ,
emotes = msgObject . get && msgObject . get ( 'tags.emotes' ) || msgObject . tags && msgObject . tags . emotes ,
tokens = [ msg ] ;
2015-06-05 03:59:28 -04:00
// Standard tokenization
2015-07-29 01:03:10 -04:00
if ( helpers && helpers . linkifyMessage ) {
2015-08-21 19:00:48 -04:00
var labels = msgObject . labels || [ ] ,
2015-07-29 01:03:10 -04:00
mod _or _higher = labels . indexOf ( "owner" ) !== - 1 ||
labels . indexOf ( "staff" ) !== - 1 ||
labels . indexOf ( "admin" ) !== - 1 ||
labels . indexOf ( "global_mod" ) !== - 1 ||
labels . indexOf ( "mod" ) !== - 1 ||
2015-08-21 19:00:48 -04:00
msgObject . style === 'admin' ;
2015-07-29 01:03:10 -04:00
tokens = helpers . linkifyMessage ( tokens , delete _links && ! mod _or _higher ) ;
}
2015-08-21 19:00:48 -04:00
2015-07-18 21:10:27 -04:00
if ( user && user . login && helpers && helpers . mentionizeMessage )
2015-06-05 03:59:28 -04:00
tokens = helpers . mentionizeMessage ( tokens , user . login , from _me ) ;
2015-07-06 00:09:21 -04:00
2015-07-18 21:10:27 -04:00
if ( helpers && helpers . emoticonizeMessage )
tokens = helpers . emoticonizeMessage ( tokens , emotes ) ;
2015-06-05 03:59:28 -04:00
// FrankerFaceZ Extras
tokens = this . _remove _banned ( tokens ) ;
2015-11-07 22:56:15 -05:00
tokens = this . tokenize _emotes ( from _user , room _id , tokens , from _me ) ;
2015-06-05 03:59:28 -04:00
2015-07-04 17:06:36 -04:00
if ( this . settings . parse _emoji )
tokens = this . tokenize _emoji ( tokens ) ;
2015-06-05 03:59:28 -04:00
// Capitalization
var display = msgObject . tags && msgObject . tags [ 'display-name' ] ;
if ( display && display . length )
2015-11-07 22:56:15 -05:00
FFZ . capitalization [ from _user ] = [ display . trim ( ) , Date . now ( ) ] ;
2015-06-05 03:59:28 -04:00
// Mentions!
if ( ! from _me ) {
tokens = this . tokenize _mentions ( tokens ) ;
for ( var i = 0 ; i < tokens . length ; i ++ ) {
2016-03-23 19:28:22 -04:00
var token = tokens [ i ] ,
is _mention = token . type === "mention" ;
if ( ! is _mention || token . isOwnMessage )
continue ;
2015-06-05 03:59:28 -04:00
// We have a mention!
msgObject . ffz _has _mention = true ;
2015-12-12 13:28:35 -05:00
// If we have chat tabs/rows, update the status.
if ( room _id && ! this . has _bttv && this . _chatv ) {
var room = this . rooms [ room _id ] && this . rooms [ room _id ] . room ;
if ( room . _ffz _tab && ! room . _ffz _tab . classList . contains ( 'active' ) ) {
room . _ffz _tab . classList . add ( 'tab-mentioned' ) ;
var was _hidden = room . _ffz _tab . classList . contains ( 'hidden' ) ;
if ( was _hidden ) {
room . _ffz _tab . classList . remove ( 'hidden' ) ;
this . _chatv . $ ( '.chat-room' ) . css ( 'top' , this . _chatv . _ffz _tabs . offsetHeight + "px" ) ;
}
}
if ( room . _ffz _row && ! room . _ffz _row . classList . contains ( 'active' ) )
room . _ffz _row . classList . add ( 'row-mentioned' ) ;
2015-06-05 03:59:28 -04:00
}
// Display notifications if that setting is enabled. Also make sure
// that we have a chat view because showing a notification when we
// can't actually go to it is a bad thing.
2015-07-04 17:06:36 -04:00
if ( this . _chatv && this . settings . highlight _notifications && ! this . embed _in _dash && ! document . hasFocus ( ) && ! prevent _notification ) {
2015-06-05 03:59:28 -04:00
var room = this . rooms [ room _id ] && this . rooms [ room _id ] . room ,
room _name ;
2015-07-13 21:52:44 -04:00
// Make sure we have UI for this channel.
if ( ( this . settings . group _tabs && ( this . settings . pinned _rooms . indexOf ( room _id ) !== - 1 || this . _chatv . _ffz _host ) ) || room . get ( 'isGroupRoom' ) || room === this . _chatv . get ( 'controller.currentChannelRoom' ) ) {
if ( room && room . get ( 'isGroupRoom' ) )
room _name = room . get ( 'tmiRoom.displayName' ) ;
else
room _name = FFZ . get _capitalization ( room _id ) ;
2015-08-21 19:00:48 -04:00
2015-11-07 22:56:15 -05:00
display = display || Twitch . display . capitalize ( from _user ) ;
2015-08-21 19:00:48 -04:00
2015-07-13 21:52:44 -04:00
if ( msgObject . style === 'action' )
msg = '* ' + display + ' ' + msg ;
else
msg = display + ': ' + msg ;
2015-08-21 19:00:48 -04:00
2015-07-13 21:52:44 -04:00
var f = this ;
if ( msgObject . style === 'whisper' )
this . show _notification (
msg ,
"Twitch Chat Whisper" ,
"ffz_whisper_notice" ,
2015-07-18 21:10:27 -04:00
( this . settings . notification _timeout * 1000 ) ,
2015-07-13 21:52:44 -04:00
function ( ) {
window . focus ( ) ;
}
2015-07-04 17:06:36 -04:00
) ;
2015-07-13 21:52:44 -04:00
else
this . show _notification (
msg ,
"Twitch Chat Mention in " + room _name ,
room _id ,
2015-07-18 21:10:27 -04:00
( this . settings . notification _timeout * 1000 ) ,
2015-07-13 21:52:44 -04:00
function ( ) {
window . focus ( ) ;
var cont = App . _ _container _ _ . lookup ( 'controller:chat' ) ;
room && cont && cont . focusRoom ( room ) ;
}
2015-07-04 17:06:36 -04:00
) ;
2015-07-13 21:52:44 -04:00
}
2015-06-05 03:59:28 -04:00
}
break ;
}
}
msgObject . cachedTokens = tokens ;
return tokens ;
}
2015-07-06 00:09:21 -04:00
FFZ . prototype . tokenize _line = function ( user , room , message , no _emotes , no _emoji ) {
2015-06-05 03:59:28 -04:00
if ( typeof message === "string" )
message = [ message ] ;
if ( helpers && helpers . linkifyMessage )
message = helpers . linkifyMessage ( message ) ;
if ( helpers && helpers . mentionizeMessage ) {
var u = this . get _user ( ) ;
if ( u && u . login )
message = helpers . mentionizeMessage ( message , u . login , user === u . login ) ;
}
2016-03-23 19:28:22 -04:00
if ( ! no _emotes )
2015-06-05 03:59:28 -04:00
message = this . tokenize _emotes ( user , room , message ) ;
2015-07-06 00:09:21 -04:00
if ( this . settings . parse _emoji && ! no _emoji )
message = this . tokenize _emoji ( message ) ;
2015-06-05 03:59:28 -04:00
return message ;
}
2016-03-23 19:28:22 -04:00
FFZ . prototype . render _tokens = function ( tokens , render _links , warn _links ) {
2015-07-04 17:06:36 -04:00
var f = this ;
2015-06-05 03:59:28 -04:00
return _ . map ( tokens , function ( token ) {
2016-03-23 19:28:22 -04:00
if ( ! token )
return "" ;
2015-11-14 23:52:49 -05:00
if ( token . hidden )
return "" ;
2016-03-23 19:28:22 -04:00
else if ( token . type === "raw" )
2015-11-19 02:45:56 -05:00
return token . html ;
2016-03-23 19:28:22 -04:00
else if ( token . type === "emoticon" ) {
var src = token . imgSrc , srcset , cls , extra ;
2015-07-04 17:06:36 -04:00
if ( token . ffzEmote ) {
var emote _set = f . emote _sets && f . emote _sets [ token . ffzEmoteSet ] ,
emote = emote _set && emote _set . emoticons && emote _set . emoticons [ token . ffzEmote ] ;
2015-07-29 01:03:10 -04:00
srcset = emote ? emote . srcSet : token . srcSet ;
2016-03-23 19:28:22 -04:00
//extra = (emote ? ` data-ffz-emote="${emote.id}"` : '') + (emote_set ? ` data-ffz-set="${emote_set.id}"` : '');
extra = ( emote ? ' data-ffz-emote="' + emote . id + '"' : '' ) + ( emote _set ? ' data-ffz-set="' + emote _set . id + '"' : '' )
2015-07-04 17:06:36 -04:00
} else if ( token . ffzEmoji ) {
2016-03-23 19:28:22 -04:00
var setting = f . settings . parse _emoji ;
if ( setting === 0 || ( setting === 1 && ! token . tw ) || ( setting === 2 && ! token . noto ) || ( setting === 3 && ! token . one ) )
2015-08-28 22:54:12 -04:00
return token . altText ;
2015-07-04 17:06:36 -04:00
2016-03-23 19:28:22 -04:00
src = setting === 3 ? token . one _src : ( setting === 2 ? token . noto _src : token . tw _src ) ;
//extra = ` data-ffz-emoji="${token.ffzEmoji}" height="18px"`;
extra = ' data-ffz-emoji="' + token . ffzEmoji + '" height="18px"' ;
2015-11-14 23:52:49 -05:00
cls = ' emoji' ;
2015-07-04 17:06:36 -04:00
} else {
2016-03-23 19:28:22 -04:00
var id = FFZ . src _to _id ( src ) ,
replacement = f . settings . replace _bad _emotes && constants . EMOTE _REPLACEMENTS [ id ] ;
2015-07-29 01:03:10 -04:00
2016-03-23 19:28:22 -04:00
//extra = ` data-emote="${id}" onerror="FrankerFaceZ._emote_mirror_swap(this)"`;
extra = ' data-emote="' + id + '" onerror="FrankerFaceZ._emote_mirror_swap(this)"' ;
2015-07-29 01:03:10 -04:00
2016-03-23 19:28:22 -04:00
if ( replacement ) {
src = constants . EMOTE _REPLACEMENT _BASE + replacement ;
srcset = '' ;
} else
2015-11-14 23:52:49 -05:00
srcset = utils . build _srcset ( id ) ;
2015-07-04 17:06:36 -04:00
}
2016-03-23 19:28:22 -04:00
//return `<img class="emoticon ffz-tooltip${cls||''}"${extra||''} src="${utils.quote_attr(src)}"${srcset ? ' srcset="' + utils.quote_attr(srcset) + '"' : ''} alt="${utils.quote_attr(token.altText)}">`;
return '<img class="emoticon ffz-tooltip' + ( cls || '' ) + '"' + ( extra || '' ) + ' src="' + utils . quote _attr ( src ) + '"' + ( srcset ? ' srcset="' + utils . quote _attr ( srcset ) + '"' : '' ) + ' alt="' + utils . quote _attr ( token . altText ) + '">' ;
2015-07-04 17:06:36 -04:00
}
2015-06-05 03:59:28 -04:00
2016-03-23 19:28:22 -04:00
else if ( token . type === "link" ) {
var text = token . title || ( token . isLong && '<long link>' ) || ( token . isDeleted && '<deleted link>' ) || ( warn _links && '<whispered link>' ) || token . text ;
2015-08-21 19:00:48 -04:00
2015-06-05 03:59:28 -04:00
if ( ! render _links && render _links !== undefined )
2015-07-29 01:03:10 -04:00
return utils . sanitize ( text ) ;
2015-06-05 03:59:28 -04:00
2016-03-23 19:28:22 -04:00
var href = token . link || token . text ,
cls = '' ;
2015-08-21 19:00:48 -04:00
2016-03-23 19:28:22 -04:00
if ( token . isMailTo ) {
2015-07-29 01:03:10 -04:00
// E-Mail Link
cls = 'email-link' ;
2016-03-23 19:28:22 -04:00
href = 'mailto:' + href ;
2015-08-21 19:00:48 -04:00
2015-07-29 01:03:10 -04:00
} else {
// Web Link
2016-03-23 19:28:22 -04:00
cls = 'chat-link' ;
if ( f . settings . link _info ) {
if ( ! ( f . _link _data && f . _link _data [ href ] ) ) {
f . _link _data = f . _link _data || { } ;
f . _link _data [ href ] = true ;
f . ws _send ( "get_link" , href , load _link _data . bind ( f , href ) ) ;
}
}
2015-07-29 01:03:10 -04:00
}
2015-08-21 19:00:48 -04:00
2015-07-29 01:03:10 -04:00
// Deleted Links
var actual _href = href ;
2016-03-23 19:28:22 -04:00
if ( token . isDeleted ) {
2015-07-29 01:03:10 -04:00
cls = 'deleted-link ' + cls ;
href = '#' ;
2015-08-21 19:00:48 -04:00
2016-03-23 19:28:22 -04:00
} else if ( warn _links ) {
cls = 'warn-link deleted-link ' + cls ;
href = '#' ;
}
//return `<a class="ffz-tooltip ${cls}" data-text="${utils.quote_attr(token.text)}" data-url="${utils.quote_attr(actual_href)}" href="${utils.quote_attr(href||'#')}" target="_blank">${utils.sanitize(text)}</a>`;
return '<a class="ffz-tooltip' + ( cls ? ' ' + cls : '' ) + '" data-text="' + utils . quote _attr ( token . text ) + '" data-url="' + utils . quote _attr ( actual _href ) + '" href="' + utils . quote _attr ( href || '#' ) + '" target="_blank">' + utils . sanitize ( text ) + '</a>' ;
2015-06-05 03:59:28 -04:00
}
2016-03-23 19:28:22 -04:00
else if ( token . type === "deleted" )
return '<span class="deleted-word tooltip" title="' + utils . quote _attr ( token . text ) + '" data-text="' + utils . sanitize ( token . text ) + '">×××</span>' ;
//return `<span class="deleted-word tooltip" title="${utils.quote_attr(token.text)}" data-text="${utils.sanitize(token.text)}">×××</span>`;
2015-06-05 03:59:28 -04:00
2016-03-23 19:28:22 -04:00
else if ( token . type === "mention" )
return '<span class="' + ( token . isOwnMessage ? 'mentioning' : 'mentioned' ) + '">' + utils . sanitize ( token . user ) + '</span>' ;
//return `<span class="${token.isOwnMessage ? 'mentioning' : 'mentioned'}">${utils.sanitize(token.user)}</span>`;
else if ( token . deletedLink || token . text )
2015-06-05 03:59:28 -04:00
return utils . sanitize ( token . text ) ;
2016-03-23 19:28:22 -04:00
else if ( typeof token !== "string" )
return '<b class="html-tooltip" title="<div style="text-align:left">' + utils . quote _attr ( JSON . stringify ( token , null , 2 ) ) + '</div>">[invalid token]</b>' ;
//return `<b class="html-tooltip" title="<div style="text-align:left">${utils.quote_attr(JSON.stringify(token,null,2))}</div>">[invalid token]</b>`;
2015-06-05 03:59:28 -04:00
return utils . sanitize ( token ) ;
} ) . join ( "" ) ;
}
// ---------------------
// Emoticon Processing
// ---------------------
FFZ . prototype . tokenize _emotes = function ( user , room , tokens , do _report ) {
2016-03-23 19:28:22 -04:00
"use strict" ;
var sets = this . getEmotes ( user , room ) ,
emotes = { } ,
emote ,
new _tokens = [ ] ;
if ( ! tokens || ! tokens . length || ! sets || ! sets . length )
return tokens ;
// Build an object with all of our emotes.
for ( var i = 0 ; i < sets . length ; i ++ ) {
var emote _set = this . emote _sets [ sets [ i ] ] ;
if ( emote _set && emote _set . emoticons )
for ( var emote _id in emote _set . emoticons ) {
emote = emote _set . emoticons [ emote _id ] ;
if ( ! emotes [ emote . name ] )
emotes [ emote . name ] = emote ;
}
}
if ( typeof tokens === "string" )
tokens = [ tokens ] ;
for ( var i = 0 , l = tokens . length ; i < l ; i ++ ) {
var token = tokens [ i ] ;
if ( ! token )
continue ;
if ( typeof token !== "string" )
if ( token . type === "text" )
token = token . text ;
else {
new _tokens . push ( token ) ;
continue ;
}
// Split the token!
var segments = token . split ( ' ' ) ,
text = [ ] , segment ;
for ( var x = 0 , y = segments . length ; x < y ; x ++ ) {
segment = segments [ x ] ;
emote = emotes [ segment ] ;
if ( emote ) {
if ( text . length ) {
// We have pending text. Join it together, with an extra space
// on the end for good measure.
new _tokens . push ( { type : "text" , text : text . join ( ' ' ) + ' ' } ) ;
text = [ ]
}
// Push this emote to the tokens.
new _tokens . push ( emote . token ) ;
if ( do _report && room )
this . add _usage ( room , emote ) ;
// Finally, push an empty string to text so that this emote gets spaced.
text . push ( '' ) ;
} else
text . push ( segment ) ;
}
// Add any left over text from this segment.
if ( text . length > 1 || ( text . length === 1 && text [ 0 ] !== '' ) )
new _tokens . push ( { type : "text" , text : text . join ( ' ' ) } ) ;
}
return new _tokens ;
2015-06-05 03:59:28 -04:00
}
2015-07-04 17:06:36 -04:00
// ---------------------
// Emoji Processing
// ---------------------
FFZ . prototype . tokenize _emoji = function ( tokens ) {
2016-03-23 19:28:22 -04:00
"use strict" ;
if ( ! tokens || ! tokens . length || ! this . emoji _data )
return tokens ;
if ( typeof tokens === "string" )
tokens = [ tokens ] ;
var new _tokens = [ ] ;
for ( var i = 0 , l = tokens . length ; i < l ; i ++ ) {
var token = tokens [ i ] ;
if ( ! token )
continue ;
if ( typeof token !== "string" )
if ( token . type === "text" )
token = token . text ;
else {
new _tokens . push ( token ) ;
continue ;
}
var segments = token . split ( constants . EMOJI _REGEX ) ,
text = null ;
while ( segments . length ) {
text = ( text || '' ) + segments . shift ( ) ;
if ( segments . length ) {
var match = segments . shift ( ) ,
eid = utils . emoji _to _codepoint ( match ) ,
data = this . emoji _data [ eid ] ;
if ( data ) {
if ( text && text . length )
new _tokens . push ( { type : "text" , text : text } ) ;
new _tokens . push ( data . token ) ;
text = null ;
} else
text = ( text || '' ) + match ;
}
}
if ( text && text . length )
new _tokens . push ( { type : "text" , text : text } ) ;
}
return new _tokens ;
2015-07-04 17:06:36 -04:00
}
2015-06-05 03:59:28 -04:00
// ---------------------
// Mention Parsing
// ---------------------
FFZ . _regex _cache = { } ;
FFZ . _get _regex = function ( word ) {
return FFZ . _regex _cache [ word ] = FFZ . _regex _cache [ word ] || RegExp ( "\\b" + reg _escape ( word ) + "\\b" , "ig" ) ;
}
FFZ . _words _to _regex = function ( list ) {
var regex = FFZ . _regex _cache [ list ] ;
if ( ! regex ) {
var reg = "" ;
for ( var i = 0 ; i < list . length ; i ++ ) {
if ( ! list [ i ] )
continue ;
reg += ( reg ? "|" : "" ) + reg _escape ( list [ i ] ) ;
}
2016-03-23 19:28:22 -04:00
regex = FFZ . _regex _cache [ list ] = new RegExp ( "(^|.*?" + constants . SEPARATORS + ")(" + reg + ")(?=$|" + constants . SEPARATORS + ")" , "ig" ) ;
2015-06-05 03:59:28 -04:00
}
return regex ;
}
FFZ . prototype . tokenize _mentions = function ( tokens ) {
var mention _words = this . settings . keywords ;
if ( ! mention _words || ! mention _words . length )
return tokens ;
if ( typeof tokens === "string" )
tokens = [ tokens ] ;
var regex = FFZ . _words _to _regex ( mention _words ) ,
new _tokens = [ ] ;
for ( var i = 0 ; i < tokens . length ; i ++ ) {
var token = tokens [ i ] ;
2016-03-23 19:28:22 -04:00
if ( token . type === "text" )
token = token . text ;
2015-06-05 03:59:28 -04:00
2016-03-23 19:28:22 -04:00
if ( ! _ . isString ( token ) || ! token . match ( regex ) ) {
2015-06-05 03:59:28 -04:00
new _tokens . push ( token ) ;
continue ;
}
token = token . replace ( regex , function ( all , prefix , match ) {
new _tokens . push ( prefix ) ;
new _tokens . push ( {
2016-03-23 19:28:22 -04:00
type : "mention" ,
length : match . length ,
user : match ,
isOwnMessage : false ,
2015-06-05 03:59:28 -04:00
} ) ;
return "" ;
} ) ;
if ( token )
new _tokens . push ( token ) ;
}
return new _tokens ;
2015-07-04 17:06:36 -04:00
}
// ---------------------
// Handling Bad Stuff
// ---------------------
FFZ . prototype . _deleted _link _click = function ( e ) {
if ( ! this . classList . contains ( "deleted-link" ) )
return true ;
2016-03-23 19:28:22 -04:00
// Stop from Navigating
e . preventDefault ( ) ;
2015-07-04 17:06:36 -04:00
// Get the URL
2016-03-23 19:28:22 -04:00
var link = this . getAttribute ( 'data-url' ) ,
text = this . getAttribute ( 'data-text' ) || link ,
2015-07-04 17:06:36 -04:00
f = FrankerFaceZ . get ( ) ;
// Delete Old Stuff
this . classList . remove ( 'deleted-link' ) ;
2016-03-23 19:28:22 -04:00
this . classList . remove ( 'warn-link' ) ;
2015-07-04 17:06:36 -04:00
// Set up the Link
2016-03-23 19:28:22 -04:00
this . href = link ;
this . target = "_blank" ;
this . textContent = text ;
2015-07-04 17:06:36 -04:00
2016-03-23 19:28:22 -04:00
// Refresh tipsy.
jQuery ( this ) . trigger ( 'mouseout' ) . trigger ( 'mouseover' ) ;
2015-11-19 02:45:56 -05:00
}
// ---------------------
// History Loading
// ---------------------
FFZ . prototype . parse _history = function ( history , purged , room _id , delete _links , tmiSession , per _line ) {
var i = history . length , was _cleared = false ;
purged = purged || { } ;
while ( i -- ) {
var msg = history [ i ] ,
is _deleted = msg . ffz _deleted = purged [ msg . from ] || false ;
if ( is _deleted && ! this . settings . prevent _clear )
msg . deleted = true ;
if ( ! msg . room && room _id )
msg . room = room _id ;
if ( typeof msg . date === "string" || typeof msg . date === "number" )
msg . date = utils . parse _date ( msg . date ) ;
if ( ! msg . color )
msg . color = msg . tags && msg . tags . color ? msg . tags . color : tmiSession && msg . from ? tmiSession . getColor ( msg . from ) : "#755000" ;
if ( ! msg . labels || ! msg . labels . length ) {
var labels = msg . labels = [ ] ;
if ( msg . room && msg . room === msg . from )
labels . push ( "owner" ) ;
else if ( msg . tags ) {
var ut = msg . tags [ 'user-type' ] ;
if ( ut === 'mod' || ut === 'staff' || ut === 'admin' || ut === 'global_mod' )
labels . push ( ut ) ;
}
if ( msg . tags ) {
if ( msg . tags . turbo )
labels . push ( "turbo" ) ;
if ( msg . tags . subscriber )
labels . push ( "subscriber" ) ;
}
}
if ( ! msg . style ) {
if ( msg . from === "jtv" )
msg . style = "admin" ;
else if ( msg . from === "twitchnotify" )
msg . style = "notification" ;
}
if ( msg . tags && typeof msg . tags . emotes === "string" )
msg . tags . emotes = utils . uncompressEmotes ( msg . tags . emotes ) ;
if ( ! msg . cachedTokens || ! msg . cachedTokens . length )
this . tokenize _chat _line ( msg , true , delete _links ) ;
// CLEARCHAT
if ( msg . tags && msg . tags . target === '@@' )
was _cleared = true ;
else if ( msg . tags && msg . tags . target )
purged [ msg . tags . target ] = true ;
// Per-line
if ( per _line && ! per _line ( msg ) )
break ;
}
return [ history , purged , was _cleared ] ;
2015-06-05 03:59:28 -04:00
}