2015-10-24 22:44:00 -04:00
var FFZ = window . FrankerFaceZ ,
utils = require ( '../utils' ) ,
build _css = function ( emote ) {
if ( ! emote . margins && ! emote . css )
return "" ;
return 'img[src="' + emote . urls [ 1 ] + '"]{' + ( emote . margins ? 'margin:' + emote . margins + ';' : '' ) + ( emote . css || "" ) + '}'
} ;
2016-08-09 20:45:28 -04:00
// ---------------------
// Badware Check
// ---------------------
FFZ . prototype . check _badware = function ( ) {
if ( this . embed _in _dash || ! window . jQuery || ! window . jQuery . noty )
return ;
// Check for the stolen version of BTTV4FFZ.
if ( FFZ . settings _info . bttv _global _emotes && FFZ . settings _info . bttv _global _emotes . category === "BetterTTV" ) {
var shown = localStorage . ffz _warning _bttv4ffz _clone ;
if ( shown !== "true" ) {
localStorage . ffz _warning _bttv4ffz _clone = "true" ;
this . show _message ( "You appear to be using an unofficial version of BTTV4FFZ that was copied without the developer's permission. Please use the official version available at <a href=\"https://lordmau5.com/bttv4ffz/\">https://lordmau5.com/bttv4ffz/</a>" ) ;
}
}
}
2015-10-24 22:44:00 -04:00
// ---------------------
// API Constructor
// ---------------------
2016-08-09 20:45:28 -04:00
var API = FFZ . API = function ( instance , name , icon , version , name _key ) {
2015-10-24 22:44:00 -04:00
this . ffz = instance || FFZ . get ( ) ;
// Check for a known API!
if ( name ) {
for ( var id in this . ffz . _known _apis ) {
if ( this . ffz . _known _apis [ id ] === name ) {
this . id = id ;
break ;
}
}
}
if ( ! this . id ) {
var i = 0 ;
while ( ! this . id ) {
if ( ! this . ffz . _known _apis . hasOwnProperty ( i ) ) {
this . id = i ;
break ;
}
i ++ ;
}
if ( name ) {
this . ffz . _known _apis [ this . id ] = name ;
localStorage . ffz _known _apis = JSON . stringify ( this . ffz . _known _apis ) ;
}
}
this . ffz . _apis [ this . id ] = this ;
this . emote _sets = { } ;
this . global _sets = [ ] ;
this . default _sets = [ ] ;
2016-07-13 02:06:50 -04:00
this . badges = { } ;
2016-05-06 02:23:12 -04:00
this . users = { } ;
this . chat _filters = [ ] ;
2015-10-24 22:44:00 -04:00
this . on _room _callbacks = [ ] ;
this . name = name || ( "Extension#" + this . id ) ;
2016-08-09 20:45:28 -04:00
this . name _key = name _key || this . name . replace ( /[^A-Z0-9_\-]/g , '' ) . toLowerCase ( ) ;
2016-07-13 02:06:50 -04:00
2015-10-24 22:44:00 -04:00
this . icon = icon || null ;
2016-07-13 02:31:26 -04:00
this . version = version || null ;
2015-10-24 22:44:00 -04:00
this . ffz . log ( 'Registered New Extension #' + this . id + ': ' + this . name ) ;
} ;
2016-03-23 19:28:22 -04:00
FFZ . prototype . api = function ( name , icon , version ) {
2015-10-24 22:44:00 -04:00
// Load the known APIs list.
if ( ! this . _known _apis ) {
this . _known _apis = { } ;
if ( localStorage . hasOwnProperty ( 'ffz_known_apis' ) )
try {
this . _known _apis = JSON . parse ( localStorage . ffz _known _apis ) ;
} catch ( err ) {
this . log ( "Error loading Known APIs: " + err ) ;
}
}
2016-03-23 19:28:22 -04:00
return new API ( this , name , icon , version ) ;
2015-10-24 22:44:00 -04:00
}
API . prototype . log = function ( msg , data , to _json , log _json ) {
this . ffz . log ( 'Ext "' + this . name + '": ' + msg , data , to _json , log _json ) ;
}
// ---------------------
// Set Loading
// ---------------------
API . prototype . _load _set = function ( real _id , set _id , data ) {
if ( ! data )
2015-12-12 13:28:35 -05:00
return null ;
2015-10-24 22:44:00 -04:00
// Check for an existing set to copy the users.
var users = [ ] ;
if ( this . emote _sets [ real _id ] && this . emote _sets [ real _id ] . users )
users = this . emote _sets [ real _id ] . users ;
2015-12-12 13:28:35 -05:00
var emote _set = {
2015-10-24 22:44:00 -04:00
source : this . name ,
source _ext : this . id ,
source _id : set _id ,
users : users ,
count : 0 ,
emoticons : { } ,
_type : data . _type || 0 ,
css : data . css || null ,
description : data . description || null ,
icon : data . icon || this . icon || null ,
id : real _id ,
title : data . title || "Global Emoticons" ,
} ;
2015-12-12 13:28:35 -05:00
this . emote _sets [ real _id ] = emote _set ;
2015-10-24 22:44:00 -04:00
if ( this . ffz . emote _sets )
2015-12-12 13:28:35 -05:00
this . ffz . emote _sets [ real _id ] = emote _set ;
2015-10-24 22:44:00 -04:00
var output _css = "" ,
ems = data . emoticons ,
2015-12-12 13:28:35 -05:00
emoticons = emote _set . emoticons ;
2015-10-24 22:44:00 -04:00
for ( var i = 0 ; i < ems . length ; i ++ ) {
var emote = ems [ i ] ,
new _emote = { urls : { } } ,
id = emote . id || ( this . name + '-' + set _id + '-' + i ) ;
if ( ! emote . name )
continue ;
new _emote . id = id ;
new _emote . set _id = real _id ;
new _emote . name = emote . name ;
new _emote . width = emote . width ;
new _emote . height = emote . height ;
new _emote . hidden = emote . hidden ;
new _emote . owner = emote . owner ;
new _emote . css = emote . css ;
new _emote . margins = emote . margins ;
new _emote . srcSet = emote . urls [ 1 ] + ' 1x' ;
new _emote . urls [ 1 ] = emote . urls [ 1 ] ;
if ( emote . urls [ 2 ] ) {
new _emote . urls [ 2 ] = emote . urls [ 2 ] ;
new _emote . srcSet += ', ' + emote . urls [ 2 ] + ' 2x' ;
}
if ( emote . urls [ 3 ] ) {
new _emote . urls [ 3 ] = emote . urls [ 3 ] ;
new _emote . srcSet += ', ' + emote . urls [ 3 ] + ' 3x' ;
}
if ( emote . urls [ 4 ] ) {
new _emote . urls [ 4 ] = emote . urls [ 4 ] ;
new _emote . srcSet += ', ' + emote . urls [ 4 ] + ' 4x' ;
}
if ( emote . regex )
new _emote . regex = emote . regex ;
else if ( typeof emote . name !== "string" )
new _emote . regex = emote . name ;
2015-12-12 13:28:35 -05:00
else if ( emote _set . require _spaces || emote . require _spaces )
new _emote . regex = new RegExp ( "(^| )(" + utils . escape _regex ( emote . name ) + ")(?= |$)" , "g" ) ;
2015-10-24 22:44:00 -04:00
else
2015-10-27 14:25:13 -04:00
new _emote . regex = new RegExp ( "(^|\\W|\\b)(" + utils . escape _regex ( emote . name ) + ")(?=\\W|$)" , "g" ) ;
2015-10-24 22:44:00 -04:00
2016-07-13 02:31:26 -04:00
new _emote . token = {
type : "emoticon" ,
srcSet : new _emote . srcSet ,
imgSrc : new _emote . urls [ 1 ] ,
ffzEmote : id ,
ffzEmoteSet : real _id ,
altText : new _emote . hidden ? '???' : new _emote . name
} ;
2016-03-23 19:28:22 -04:00
2015-10-24 22:44:00 -04:00
output _css += build _css ( new _emote ) ;
2015-12-12 13:28:35 -05:00
emote _set . count ++ ;
2015-10-24 22:44:00 -04:00
emoticons [ id ] = new _emote ;
}
2015-12-12 13:28:35 -05:00
utils . update _css ( this . ffz . _emote _style , real _id , output _css + ( emote _set . css || "" ) ) ;
2015-10-24 22:44:00 -04:00
if ( this . ffz . _cindex )
this . ffz . _cindex . ffzFixTitle ( ) ;
try {
this . ffz . update _ui _link ( ) ;
} catch ( err ) { }
2015-12-12 13:28:35 -05:00
return emote _set ;
2015-10-24 22:44:00 -04:00
}
2015-10-27 14:25:13 -04:00
// -------------------------
// Loading / Unloading Sets
// -------------------------
API . prototype . load _set = function ( id , emote _set ) {
var exact _id = this . id + '-' + id ;
emote _set . title = emote _set . title || "Global Emoticons" ;
emote _set . _type = emote _set . _type || 0 ;
emote _set = this . _load _set ( exact _id , id , emote _set ) ;
this . log ( "Loaded Emoticon Set #" + id + ": " + emote _set . title + " (" + emote _set . count + " emotes)" , emote _set ) ;
return emote _set ;
}
API . prototype . unload _set = function ( id ) {
var exact _id = this . id + '-' + id ,
emote _set = this . emote _sets [ exact _id ] ;
if ( ! emote _set )
return ;
// First, let's unregister it as a global.
this . unregister _global _set ( id ) ;
// Now, remove the set data.
utils . update _css ( this . ffz . _emote _style , exact _id , null ) ;
this . emote _sets [ exact _id ] = undefined ;
if ( this . ffz . emote _sets )
this . ffz . emote _sets [ exact _id ] = undefined ;
// Remove from all its Rooms
if ( emote _set . users ) {
for ( var i = 0 ; i < emote _set . users . length ; i ++ ) {
var room _id = emote _set . users [ i ] ,
room = this . ffz . rooms && this . ffz . rooms [ room _id ] ;
if ( ! room )
continue ;
2016-03-25 18:32:35 -04:00
var ind = room . ext _sets ? room . ext _sets . indexOf ( exact _id ) : - 1 ;
2015-10-27 14:25:13 -04:00
if ( ind !== - 1 )
room . ext _sets . splice ( ind , 1 ) ;
}
emote _set . users = [ ] ;
}
2015-12-12 13:28:35 -05:00
return emote _set ;
2015-10-27 14:25:13 -04:00
}
API . prototype . get _set = function ( id ) {
var exact _id = this . id + '-' + id ;
return this . emote _sets [ exact _id ] ;
}
2015-10-24 22:44:00 -04:00
// ---------------------
// Global Emote Sets
// ---------------------
2015-10-27 14:25:13 -04:00
API . prototype . register _global _set = function ( id , emote _set ) {
2015-10-24 22:44:00 -04:00
var exact _id = this . id + '-' + id ;
2015-10-27 14:25:13 -04:00
if ( emote _set ) {
// If a set was provided, load it.
emote _set = this . load _set ( id , emote _set ) ;
} else
emote _set = this . emote _sets [ exact _id ] ;
2015-10-24 22:44:00 -04:00
2015-10-27 14:25:13 -04:00
if ( ! emote _set )
throw new Error ( "Invalid set ID" ) ;
2015-10-24 22:44:00 -04:00
2015-10-27 14:25:13 -04:00
2015-10-27 21:22:54 -04:00
// Make sure the set is still available with FFZ.
if ( ! this . ffz . emote _sets [ exact _id ] )
this . ffz . emote _sets [ exact _id ] = emote _set ;
2015-10-27 14:25:13 -04:00
// It's a valid set if we get here, so make it global.
2015-10-24 22:44:00 -04:00
if ( this . global _sets . indexOf ( exact _id ) === - 1 )
this . global _sets . push ( exact _id ) ;
if ( this . default _sets . indexOf ( exact _id ) === - 1 )
this . default _sets . push ( exact _id ) ;
if ( this . ffz . global _sets && this . ffz . global _sets . indexOf ( exact _id ) === - 1 )
this . ffz . global _sets . push ( exact _id ) ;
if ( this . ffz . default _sets && this . ffz . default _sets . indexOf ( exact _id ) === - 1 )
this . ffz . default _sets . push ( exact _id ) ;
2016-03-26 16:09:36 -04:00
2016-07-13 02:31:26 -04:00
// Update tab completion.
if ( this . ffz . _inputv )
Ember . propertyDidChange ( this . ffz . _inputv , 'ffz_emoticons' ) ;
2015-10-24 22:44:00 -04:00
} ;
API . prototype . unregister _global _set = function ( id ) {
var exact _id = this . id + '-' + id ,
2015-10-27 14:25:13 -04:00
emote _set = this . emote _sets [ exact _id ] ;
2015-10-24 22:44:00 -04:00
2015-10-27 14:25:13 -04:00
if ( ! emote _set )
2015-10-24 22:44:00 -04:00
return ;
2015-10-27 14:25:13 -04:00
// Remove the set from global sets.
2015-10-24 22:44:00 -04:00
var ind = this . global _sets . indexOf ( exact _id ) ;
if ( ind !== - 1 )
this . global _sets . splice ( ind , 1 ) ;
ind = this . default _sets . indexOf ( exact _id ) ;
if ( ind !== - 1 )
this . default _sets . splice ( ind , 1 ) ;
ind = this . ffz . global _sets ? this . ffz . global _sets . indexOf ( exact _id ) : - 1 ;
if ( ind !== - 1 )
this . ffz . global _sets . splice ( ind , 1 ) ;
ind = this . ffz . default _sets ? this . ffz . default _sets . indexOf ( exact _id ) : - 1 ;
if ( ind !== - 1 )
this . ffz . default _sets . splice ( ind , 1 ) ;
2016-03-26 16:09:36 -04:00
2016-07-13 02:31:26 -04:00
// Update tab completion.
if ( this . ffz . _inputv )
Ember . propertyDidChange ( this . ffz . _inputv , 'ffz_emoticons' ) ;
2015-10-24 22:44:00 -04:00
} ;
// -----------------------
// Per-Channel Emote Sets
// -----------------------
2015-10-27 14:25:13 -04:00
API . prototype . register _room _set = function ( room _id , id , emote _set ) {
var exact _id = this . id + '-' + id ,
room = this . ffz . rooms && this . ffz . rooms [ room _id ] ;
2015-10-24 22:44:00 -04:00
2015-10-27 14:25:13 -04:00
if ( ! room )
throw new Error ( "Room not loaded" ) ;
2015-10-24 22:44:00 -04:00
2015-10-27 14:25:13 -04:00
if ( emote _set ) {
// If a set was provided, load it.
emote _set . title = emote _set . title || "Channel: " + ( room . display _name || room _id ) ;
emote _set . _type = emote _set . _type || 1 ;
2015-10-24 22:44:00 -04:00
2015-10-27 14:25:13 -04:00
emote _set = this . load _set ( id , emote _set ) ;
} else
emote _set = this . emote _sets [ exact _id ] ;
2015-10-24 22:44:00 -04:00
2015-10-27 14:25:13 -04:00
if ( ! emote _set )
throw new Error ( "Invalid set ID" ) ;
2015-10-27 21:22:54 -04:00
// Make sure the set is still available with FFZ.
if ( ! this . ffz . emote _sets [ exact _id ] )
this . ffz . emote _sets [ exact _id ] = emote _set ;
2015-10-27 14:25:13 -04:00
// Register it on the room.
2016-03-25 18:32:35 -04:00
room . ext _sets && room . ext _sets . push ( exact _id ) ;
2015-10-27 14:25:13 -04:00
emote _set . users . push ( room _id ) ;
2016-03-26 16:09:36 -04:00
2016-07-13 02:31:26 -04:00
// Update tab completion.
if ( this . ffz . _inputv )
Ember . propertyDidChange ( this . ffz . _inputv , 'ffz_emoticons' ) ;
2015-10-27 14:25:13 -04:00
}
API . prototype . unregister _room _set = function ( room _id , id ) {
var exact _id = this . id + '-' + id ,
emote _set = this . emote _sets [ exact _id ] ,
room = this . ffz . rooms && this . ffz . rooms [ room _id ] ;
if ( ! emote _set || ! room )
return ;
2016-03-25 18:32:35 -04:00
var ind = room . ext _sets ? room . ext _sets . indexOf ( exact _id ) : - 1 ;
2015-10-27 14:25:13 -04:00
if ( ind !== - 1 )
room . ext _sets . splice ( ind , 1 ) ;
ind = emote _set . users . indexOf ( room _id ) ;
if ( ind !== - 1 )
emote _set . users . splice ( ind , 1 ) ;
2016-03-26 16:09:36 -04:00
2016-07-13 02:31:26 -04:00
// Update tab completion.
if ( this . ffz . _inputv )
Ember . propertyDidChange ( this . ffz . _inputv , 'ffz_emoticons' ) ;
2015-10-27 14:25:13 -04:00
}
2016-07-13 02:06:50 -04:00
// -----------------------
// Badge APIs
// -----------------------
API . prototype . add _badge = function ( badge _id , badge ) {
var exact _id = this . id + '-' + badge _id ,
real _badge = {
id : exact _id ,
source _ext : this . id ,
source _id : badge _id ,
alpha _image : badge . alpha _image ,
color : badge . color || "transparent" ,
no _invert : badge . no _invert ,
invert _invert : badge . invert _invert ,
css : badge . css ,
image : badge . image ,
name : badge . name ,
title : badge . title ,
slot : badge . slot ,
visible : badge . visible ,
replaces : badge . replaces ,
replaces _type : badge . replaces _type
} ;
this . ffz . badges [ exact _id ] = this . badges [ badge _id ] = real _badge ;
utils . update _css ( this . ffz . _badge _style , exact _id , utils . badge _css ( real _badge ) ) ;
}
API . prototype . remove _badge = function ( badge _id ) {
var exact _id = this . id + '-' + badge _id ;
this . ffz . badges [ exact _id ] = this . badges [ badge _id ] = undefined ;
utils . update _css ( this . ffz . _badge _style , exact _id ) ;
}
2016-05-06 02:23:12 -04:00
// -----------------------
// User Modifications
// -----------------------
2016-07-13 02:06:50 -04:00
API . prototype . user _add _badge = function ( username , slot , badge _id ) {
var user = this . users [ username ] = this . users [ username ] || { } ,
ffz _user = this . ffz . users [ username ] = this . ffz . users [ username ] || { } ,
badges = user . badges = user . badges || { } ,
ffz _badges = ffz _user . badges = ffz _user . badges || { } ,
exact _id = this . id + '-' + badge _id ,
badge = { id : exact _id } ;
badges [ slot ] = ffz _badges [ slot ] = badge ;
}
API . prototype . user _remove _badge = function ( username , slot ) {
var user = this . users [ username ] = this . users [ username ] || { } ,
ffz _user = this . ffz . users [ username ] = this . ffz . users [ username ] || { } ,
badges = user . badges = user . badges || { } ,
ffz _badges = ffz _user . badges = ffz _user . badges || { } ;
badges [ slot ] = ffz _badges [ slot ] = null ;
}
API . prototype . user _add _set = function ( username , set _id ) {
var user = this . users [ username ] = this . users [ username ] || { } ,
ffz _user = this . ffz . users [ username ] = this . ffz . users [ username ] || { } ,
2016-05-06 02:23:12 -04:00
emote _sets = user . sets = user . sets || [ ] ,
ffz _sets = ffz _user . sets = ffz _user . sets || [ ] ,
exact _id = this . id + '-' + set _id ;
if ( emote _sets . indexOf ( exact _id ) === - 1 )
emote _sets . push ( exact _id ) ;
if ( ffz _sets . indexOf ( exact _id ) === - 1 )
ffz _sets . push ( exact _id ) ;
// Update tab completion.
var user = this . ffz . get _user ( ) ;
2016-07-13 02:06:50 -04:00
if ( this . ffz . _inputv && user && user . login === username )
2016-07-13 02:31:26 -04:00
Ember . propertyDidChange ( this . ffz . _inputv , 'ffz_emoticons' ) ;
2016-05-06 02:23:12 -04:00
}
2016-07-13 02:06:50 -04:00
API . prototype . user _remove _set = function ( username , set _id ) {
var user = this . users [ username ] ,
ffz _user = this . ffz . users [ username ] ,
2016-05-06 02:23:12 -04:00
emote _sets = user && user . sets ,
ffz _sets = ffz _user && ffz _user . sets ,
exact _id = this . id + '-' + set _id ;
var ind = emote _sets ? emote _sets . indexOf ( exact _id ) : - 1 ;
if ( ind !== - 1 )
emote _sets . splice ( ind , 1 ) ;
ind = ffz _sets ? ffz _sets . indexOf ( exact _id ) : - 1 ;
if ( ind !== - 1 )
ffz _sets . splice ( ind , 1 ) ;
// Update tab completion.
var user = this . ffz . get _user ( ) ;
2016-07-13 02:06:50 -04:00
if ( this . ffz . _inputv && user && user . login === username )
2016-07-13 02:31:26 -04:00
Ember . propertyDidChange ( this . ffz . _inputv , 'ffz_emoticons' ) ;
2016-05-06 02:23:12 -04:00
}
// -----------------------
// Chat Callback
// -----------------------
API . prototype . register _chat _filter = function ( filter ) {
this . chat _filters . push ( filter ) ;
this . ffz . _chat _filters . push ( filter ) ;
}
API . prototype . unregister _chat _filter = function ( filter ) {
var ind = this . chat _filters . indexOf ( filter ) ;
if ( ind !== - 1 )
this . chat _filters . splice ( ind , 1 ) ;
ind = this . ffz . _chat _filters . indexOf ( filter ) ;
if ( ind !== - 1 )
this . ffz . _chat _filters . splice ( ind , 1 ) ;
}
2015-10-27 14:25:13 -04:00
// -----------------------
// Channel Callbacks
// -----------------------
API . prototype . _room _callbacks = function ( room _id , room , specific _func ) {
var callback = this . register _room _set . bind ( this , room _id ) ;
2015-10-24 22:44:00 -04:00
if ( specific _func ) {
try {
specific _func ( room _id , callback ) ;
} catch ( err ) {
this . log ( "Error in On-Room Callback: " + err ) ;
}
} else {
for ( var i = 0 ; i < this . on _room _callbacks . length ; i ++ ) {
var cb = this . on _room _callbacks [ i ] ;
try {
cb ( room _id , callback ) ;
} catch ( err ) {
this . log ( "Error in On-Room Callback: " + err ) ;
}
}
}
}
2015-10-27 14:25:13 -04:00
API . prototype . register _on _room _callback = function ( callback , dont _iterate ) {
2015-10-24 22:44:00 -04:00
this . on _room _callbacks . push ( callback ) ;
// Call this for all current rooms.
2015-10-27 14:25:13 -04:00
if ( ! dont _iterate && this . ffz . rooms ) {
2015-10-24 22:44:00 -04:00
for ( var room _id in this . ffz . rooms )
this . _room _callbacks ( room _id , this . ffz . rooms [ room _id ] , callback ) ;
}
}
API . prototype . unregister _on _room _callback = function ( callback ) {
var ind = this . on _room _callbacks . indexOf ( callback ) ;
if ( ind !== - 1 )
this . on _room _callbacks . splice ( ind , 1 ) ;
}