2015-01-12 17:58:07 -05:00
( function ( window ) { ( function e ( t , n , r ) { function s ( o , u ) { if ( ! n [ o ] ) { if ( ! t [ o ] ) { var a = typeof require == "function" && require ; if ( ! u && a ) return a ( o , ! 0 ) ; if ( i ) return i ( o , ! 0 ) ; throw new Error ( "Cannot find module '" + o + "'" ) } var f = n [ o ] = { exports : { } } ; t [ o ] [ 0 ] . call ( f . exports , function ( e ) { var n = t [ o ] [ 1 ] [ e ] ; return s ( n ? n : e ) } , f , f . exports , e , t , n , r ) } return n [ o ] . exports } var i = typeof require == "function" && require ; for ( var o = 0 ; o < r . length ; o ++ ) s ( r [ o ] ) ; return s } ) ( { 1 : [ function ( require , module , exports ) {
var FFZ = window . FrankerFaceZ ,
constants = require ( './constants' ) ,
utils = require ( './utils' ) ;
// --------------------
// Initialization
// --------------------
FFZ . prototype . setup _badges = function ( ) {
this . log ( "Preparing badge system." ) ;
this . badges = { } ;
this . log ( "Creating badge style element." ) ;
var s = this . _badge _style = document . createElement ( 'style' ) ;
s . id = "ffz-badge-css" ;
document . head . appendChild ( s ) ;
this . log ( "Adding legacy donor badges." ) ;
this . _legacy _add _donors ( ) ;
}
// --------------------
// Badge CSS
// --------------------
var badge _css = function ( badge ) {
return ".badges .ffz-badge-" + badge . id + " { background-color: " + badge . color + '; background-image: url("' + badge . image + '"); ' + ( badge . extra _css || "" ) + '}' ;
}
// --------------------
// Render Badge
// --------------------
2015-01-15 13:58:42 -05:00
FFZ . prototype . bttv _badges = function ( data ) {
var user _id = data . sender ,
user = this . users [ user _id ] ,
badges _out = [ ] ,
insert _at = - 1 ;
if ( ! user || ! user . badges )
return ;
// Determine where in the list to insert these badges.
for ( var i = 0 ; i < data . badges . length ; i ++ ) {
var badge = data . badges [ i ] ;
if ( badge . type == "subscriber" || badge . type == "turbo" ) {
insert _at = i ;
break ;
}
}
for ( var slot in user . badges ) {
if ( ! user . badges . hasOwnProperty ( slot ) )
continue ;
var badge = user . badges [ slot ] ,
full _badge = this . badges [ badge . id ] || { } ,
desc = badge . title || full _badge . title ,
style = "" ,
alpha = BetterTTV . settings . get ( 'alphaTags' ) ;
if ( badge . image )
style += 'background-image: url(\\"' + badge . image + '\\"); ' ;
if ( badge . color && ! alpha )
style += 'background-color: ' + badge . color + '; ' ;
if ( badge . extra _css )
style += badge . extra _css ;
if ( style )
desc += '" style="' + style ;
badges _out . push ( [ ( insert _at == - 1 ? 1 : - 1 ) * slot , { type : "ffz-badge-" + badge . id + ( alpha ? " alpha" : "" ) , name : "" , description : desc } ] ) ;
}
badges _out . sort ( function ( a , b ) { return a [ 0 ] - b [ 0 ] } ) ;
if ( insert _at == - 1 ) {
while ( badges _out . length )
data . badges . push ( badges _out . shift ( ) [ 1 ] ) ;
} else {
while ( badges _out . length )
data . badges . insertAt ( insert _at , badges _out . shift ( ) [ 1 ] ) ;
}
}
2015-01-12 17:58:07 -05:00
FFZ . prototype . render _badge = function ( view ) {
var user = view . get ( 'context.model.from' ) ,
room _id = view . get ( 'context.parentController.content.id' ) ,
badges = view . $ ( '.badges' ) ;
var data = this . users [ user ] ;
if ( ! data || ! data . badges )
return ;
// Figure out where to place our badge(s).
var before = badges . find ( '.badge' ) . filter ( function ( i ) {
var t = this . title . toLowerCase ( ) ;
return t == "subscriber" || t == "turbo" ;
} ) . first ( ) ;
var badges _out = [ ] , reverse = ! ( ! before . length ) ;
for ( var slot in data . badges ) {
if ( ! data . badges . hasOwnProperty ( slot ) )
continue ;
var badge = data . badges [ slot ] ,
full _badge = this . badges [ badge . id ] || { } ;
var el = document . createElement ( 'div' ) ;
el . className = 'badge float-left tooltip ffz-badge-' + badge . id ;
el . setAttribute ( 'title' , badge . title || full _badge . title ) ;
if ( badge . image )
el . style . backgroundImage = 'url("' + badge . image + '")' ;
if ( badge . color )
el . style . backgroundColor = badge . color ;
if ( badge . extra _css )
el . style . cssText += badge . extra _css ;
badges _out . push ( [ ( ( reverse ? 1 : - 1 ) * slot ) , el ] ) ;
}
badges _out . sort ( function ( a , b ) { return a [ 0 ] - b [ 0 ] } ) ;
if ( reverse ) {
while ( badges _out . length )
before . before ( badges _out . shift ( ) [ 1 ] ) ;
} else {
while ( badges _out . length )
badges . append ( badges _out . shift ( ) [ 1 ] ) ;
}
}
// --------------------
// Legacy Support
// --------------------
FFZ . prototype . _legacy _add _donors = function ( tries ) {
this . badges [ 1 ] = { id : 1 , title : "FFZ Donor" , color : "#755000" , image : "http://cdn.frankerfacez.com/channel/global/donoricon.png" } ;
utils . update _css ( this . _badge _style , 1 , badge _css ( this . badges [ 1 ] ) ) ;
// Developer Badges
// TODO: Upload the badge to the proper CDN.
this . badges [ 0 ] = { id : 0 , title : "FFZ Developer" , color : "#FAAF19" , image : "http://sir.stendec.me/devicon.png" } ;
utils . update _css ( this . _badge _style , 0 , badge _css ( this . badges [ 0 ] ) ) ;
this . users . sirstendec = { badges : { 0 : { id : 0 } } } ;
jQuery . ajax ( constants . SERVER + "script/donors.txt" , { cache : false , context : this } )
. done ( function ( data ) {
this . _legacy _parse _donors ( data ) ;
} ) . fail ( function ( data ) {
if ( data . status == 404 )
return ;
tries = ( tries || 0 ) + 1 ;
if ( tries < 10 )
return this . _legacy _add _donors ( tries ) ;
} ) ;
}
FFZ . prototype . _legacy _parse _donors = function ( data ) {
var count = 0 ;
if ( data != null ) {
var lines = data . trim ( ) . split ( /\W+/ ) ;
for ( var i = 0 ; i < lines . length ; i ++ ) {
var user _id = lines [ i ] ,
user = this . users [ user _id ] = this . users [ user _id ] || { } ,
badges = user . badges = user . badges || { } ;
if ( badges [ 0 ] )
continue ;
badges [ 0 ] = { id : 1 } ;
count += 1 ;
}
}
this . log ( "Added donor badge to " + utils . number _commas ( count ) + " users." ) ;
2014-03-27 23:24:57 -04:00
}
2015-01-15 22:19:05 -05:00
} , { "./constants" : 3 , "./utils" : 19 } ] , 2 : [ function ( require , module , exports ) {
2015-01-15 13:58:42 -05:00
var FFZ = window . FrankerFaceZ ,
SENDER _REGEX = /(\sdata-sender="[^"]*"(?=>))/ ;
// --------------------
// Initialization
// --------------------
FFZ . prototype . find _bttv = function ( increment , delay ) {
this . has _bttv = false ;
if ( window . BTTVLOADED )
return this . setup _bttv ( ) ;
if ( delay >= 60000 )
this . log ( "BetterTTV was not detected after 60 seconds." ) ;
else
setTimeout ( this . find _bttv . bind ( this , increment , ( delay || 0 ) + increment ) ,
increment ) ;
}
FFZ . prototype . setup _bttv = function ( ) {
this . log ( "BetterTTV was detected. Hooking." ) ;
this . has _bttv = true ;
// Send Message Behavior
var original _send = BetterTTV . chat . helpers . sendMessage , f = this ;
BetterTTV . chat . helpers . sendMessage = function ( message ) {
var cmd = message . split ( ' ' , 1 ) [ 0 ] . toLowerCase ( ) ;
if ( cmd === "/ffz" )
f . run _command ( message . substr ( 5 ) , BetterTTV . chat . store . currentRoom ) ;
else
return original _send ( message ) ;
}
// Ugly Hack for Current Room
var original _handler = BetterTTV . chat . handlers . privmsg ,
received _room ;
BetterTTV . chat . handlers . privmsg = function ( room , data ) {
received _room = room ;
var output = original _handler ( room , data ) ;
received _room = null ;
return output ;
}
// Message Display Behavior
var original _privmsg = BetterTTV . chat . templates . privmsg ;
BetterTTV . chat . templates . privmsg = function ( highlight , action , server , isMod , data ) {
// Handle badges.
f . bttv _badges ( data ) ;
var output = original _privmsg ( highlight , action , server , isMod , data ) ;
return output . replace ( SENDER _REGEX , '$1 data-room="' + received _room + '"' ) ;
}
// Ugly Hack for Current Sender
var original _template = BetterTTV . chat . templates . message ,
received _sender ;
BetterTTV . chat . templates . message = function ( sender , message , emotes , colored ) {
received _sender = sender ;
var output = original _template ( sender , message , emotes , colored ) ;
received _sender = null ;
return output ;
}
// Emoticonize
var original _emoticonize = BetterTTV . chat . templates . emoticonize ;
BetterTTV . chat . templates . emoticonize = function ( message , emotes ) {
var tokens = original _emoticonize ( message , emotes ) ,
user = f . users [ received _sender ] ,
room = f . rooms [ received _room ] ;
// Get our sets.
var sets = _ . union ( user && user . sets || [ ] , room && room . sets || [ ] , f . global _sets ) ,
emotes = [ ] ;
// Build a list of emotes that match.
_ . each ( sets , function ( set _id ) {
var set = f . emote _sets [ set _id ] ;
if ( ! set )
return ;
_ . each ( set . emotes , function ( emote ) {
_ . any ( tokens , function ( token ) {
return _ . isString ( token ) && token . match ( emote . regex ) ;
} ) && emotes . push ( emote ) ;
} ) ;
} ) ;
// Don't bother proceeding if we have no emotes.
if ( ! emotes . length )
return tokens ;
// Why is emote parsing so bad? ;_;
_ . each ( emotes , function ( emote ) {
2015-01-15 22:19:05 -05:00
var eo = [ '<img class="emoticon" src="' + emote . url + ( emote . hidden ? "" : '" alt="' + emote . name + '" title="' + emote . name ) + '" />' ] ,
2015-01-15 13:58:42 -05:00
old _tokens = tokens ;
tokens = [ ] ;
if ( ! old _tokens || ! old _tokens . length )
return tokens ;
for ( var i = 0 ; i < old _tokens . length ; i ++ ) {
var token = old _tokens [ i ] ;
if ( typeof token != "string" ) {
tokens . push ( token ) ;
continue ;
}
var tbits = token . split ( emote . regex ) ;
tbits . forEach ( function ( val , ind ) {
if ( val && val . length )
tokens . push ( val ) ;
if ( ind !== tbits . length - 1 )
tokens . push ( eo ) ;
} ) ;
}
} ) ;
return tokens ;
}
this . update _ui _link ( ) ;
}
} , { } ] , 3 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
var SVGPATH = '<path d="m120.95 1.74c4.08-0.09 8.33-0.84 12.21 0.82 3.61 1.8 7 4.16 11.01 5.05 2.08 3.61 6.12 5.46 8.19 9.07 3.6 5.67 7.09 11.66 8.28 18.36 1.61 9.51 7.07 17.72 12.69 25.35 3.43 7.74 1.97 16.49 3.6 24.62 2.23 5.11 4.09 10.39 6.76 15.31 1.16 2 4.38 0.63 4.77-1.32 1.2-7.1-2.39-13.94-1.97-21.03 0.38-3.64-0.91-7.48 0.25-10.99 2.74-3.74 4.57-8.05 7.47-11.67 3.55-5.47 10.31-8.34 16.73-7.64 2.26 2.89 5.13 5.21 7.58 7.92 2.88 4.3 6.52 8.01 9.83 11.97 1.89 2.61 3.06 5.64 4.48 8.52 2.81 4.9 4 10.5 6.63 15.49 2.16 6.04 5.56 11.92 5.37 18.5 0.65 1.95 0.78 4 0.98 6.03 1.01 3.95 2.84 8.55 0.63 12.42-2.4 5.23-7.03 8.97-11.55 12.33-6.06 4.66-11.62 10.05-18.37 13.75-4.06 2.65-8.24 5.17-12.71 7.08-3.59 1.57-6.06 4.94-9.85 6.09-2.29 1.71-3.98 4.51-6.97 5.02-4.56 1.35-8.98-3.72-13.5-1.25-2.99 1.83-6.19 3.21-9.39 4.6-8.5 5.61-18.13 9.48-28.06 11.62-8.36-0.2-16.69 0.62-25.05 0.47-3.5-1.87-7.67-1.08-11.22-2.83-6.19-1.52-10.93-6.01-16.62-8.61-2.87-1.39-5.53-3.16-8.11-4.99-2.58-1.88-4.17-4.85-6.98-6.44-3.83-0.11-6.54 3.42-10.24 3.92-2.31 0.28-4.64 0.32-6.96 0.31-3.5-3.65-5.69-8.74-10.59-10.77-5.01-3.68-10.57-6.67-14.84-11.25-2.52-2.55-5.22-4.87-8.24-6.8-4.73-4.07-7.93-9.51-11.41-14.62-3.08-4.41-5.22-9.73-4.6-15.19 0.65-8.01 0.62-16.18 2.55-24.02 4.06-10.46 11.15-19.34 18.05-28.06 3.71-5.31 9.91-10.21 16.8-8.39 3.25 1.61 5.74 4.56 7.14 7.89 1.19 2.7 3.49 4.93 3.87 7.96 0.97 5.85 1.6 11.86 0.74 17.77-1.7 6.12-2.98 12.53-2.32 18.9 0.01 2.92 2.9 5.36 5.78 4.57 3.06-0.68 3.99-4.07 5.32-6.48 1.67-4.06 4.18-7.66 6.69-11.23 3.61-5.28 5.09-11.57 7.63-17.37 2.07-4.56 1.7-9.64 2.56-14.46 0.78-7.65-0.62-15.44 0.7-23.04 1.32-3.78 1.79-7.89 3.8-11.4 3.01-3.66 6.78-6.63 9.85-10.26 1.72-2.12 4.21-3.32 6.55-4.6 7.89-2.71 15.56-6.75 24.06-7z"/>' ,
2015-01-15 13:58:42 -05:00
DEBUG = localStorage . ffzDebugMode == "true" && document . body . classList . contains ( 'ffz-dev' ) ;
2015-01-12 17:58:07 -05:00
module . exports = {
DEBUG : DEBUG ,
SERVER : DEBUG ? "//localhost:8000/" : "//cdn.frankerfacez.com/" ,
SVGPATH : SVGPATH ,
ZREKNARF : '<svg style="padding:1.75px 0" class="svg-glyph_views" width="16px" viewBox="0 0 249 195" version="1.1" height="12.5px">' + SVGPATH + '</svg>' ,
CHAT _BUTTON : '<svg class="svg-emoticons ffz-svg" height="18px" width="24px" viewBox="0 0 249 195" version="1.1">' + SVGPATH + '</svg>'
2014-03-27 22:44:11 -04:00
}
2015-01-15 13:58:42 -05:00
} , { } ] , 4 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
var FFZ = window . FrankerFaceZ ;
2015-01-15 13:58:42 -05:00
// -----------------------
// Developer Mode Command
// -----------------------
2015-01-12 17:58:07 -05:00
2015-01-15 13:58:42 -05:00
FFZ . chat _commands . developer _mode = function ( room , args ) {
2015-01-12 17:58:07 -05:00
var enabled , args = args && args . length ? args [ 0 ] . toLowerCase ( ) : null ;
if ( args == "y" || args == "yes" || args == "true" || args == "on" )
enabled = true ;
else if ( args == "n" || args == "no" || args == "false" || args == "off" )
enabled = false ;
if ( enabled === undefined )
2015-01-15 13:58:42 -05:00
return "Developer Mode is currently " + ( localStorage . ffzDebugMode == "true" ? "enabled." : "disabled." ) ;
2015-01-12 17:58:07 -05:00
localStorage . ffzDebugMode = enabled ;
2015-01-15 13:58:42 -05:00
return "Developer Mode is now " + ( enabled ? "enabled" : "disabled" ) + ". Please refresh your browser." ;
2015-01-12 17:58:07 -05:00
}
2015-01-15 13:58:42 -05:00
FFZ . chat _commands . developer _mode . help = "Usage: /ffz developer_mode <on|off>\nEnable or disable Developer Mode. When Developer Mode is enabled, the script will be reloaded from //localhost:8000/script.js instead of from the CDN." ;
} , { } ] , 5 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
var FFZ = window . FrankerFaceZ ;
// --------------------
// Initialization
// --------------------
FFZ . prototype . setup _chatview = function ( ) {
this . log ( "Hooking the Ember Chat view." ) ;
var Chat = App . _ _container _ _ . resolve ( 'view:chat' ) ;
this . _modify _cview ( Chat ) ;
// For some reason, this doesn't work unless we create an instance of the
// chat view and then destroy it immediately.
Chat . create ( ) . destroy ( ) ;
// Modify all existing Chat views.
for ( var key in Ember . View . views ) {
if ( ! Ember . View . views . hasOwnProperty ( key ) )
continue ;
var view = Ember . View . views [ key ] ;
if ( ! ( view instanceof Chat ) )
continue ;
this . log ( "Adding UI link manually to Chat view." , view ) ;
view . $ ( '.textarea-contain' ) . append ( this . build _ui _link ( view ) ) ;
}
}
// --------------------
// Modify Chat View
// --------------------
FFZ . prototype . _modify _cview = function ( view ) {
var f = this ;
view . reopen ( {
didInsertElement : function ( ) {
this . _super ( ) ;
this . $ ( ) && this . $ ( '.textarea-contain' ) . append ( f . build _ui _link ( this ) ) ;
} ,
willClearRender : function ( ) {
this . _super ( ) ;
this . $ ( ".ffz-ui-toggle" ) . remove ( ) ;
} ,
ffzUpdateLink : Ember . observer ( 'controller.currentRoom' , function ( ) {
f . update _ui _link ( ) ;
} )
} ) ;
2014-03-27 23:24:57 -04:00
}
2015-01-15 13:58:42 -05:00
} , { } ] , 6 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
var FFZ = window . FrankerFaceZ ;
// ---------------------
// Initialization
// ---------------------
FFZ . prototype . setup _line = function ( ) {
this . log ( "Hooking the Ember Line controller." ) ;
var Line = App . _ _container _ _ . resolve ( 'controller:line' ) ,
f = this ;
Line . reopen ( {
tokenizedMessage : function ( ) {
// Add our own step to the tokenization procedure.
return f . _emoticonize ( this , this . _super ( ) ) ;
} . property ( "model.message" , "isModeratorOrHigher" , "controllers.emoticons.emoticons.[]" )
// TODO: Copy the new properties from the new Twitch!
} ) ;
this . log ( "Hooking the Ember Line view." ) ;
var Line = App . _ _container _ _ . resolve ( 'view:line' ) ;
Line . reopen ( {
didInsertElement : function ( ) {
this . _super ( ) ;
2015-01-15 13:58:42 -05:00
var el = this . get ( 'element' ) ,
user = this . get ( 'context.model.from' ) ;
2015-01-12 17:58:07 -05:00
el . setAttribute ( 'data-room' , this . get ( 'context.parentController.content.id' ) ) ;
2015-01-15 13:58:42 -05:00
el . setAttribute ( 'data-sender' , user ) ;
2015-01-12 17:58:07 -05:00
f . render _badge ( this ) ;
2015-01-15 22:19:05 -05:00
if ( localStorage [ 'ffzCapitalize' ] != 'false' )
f . capitalize ( this , user ) ;
2015-01-15 13:58:42 -05:00
2015-01-12 17:58:07 -05:00
}
} ) ;
}
2015-01-15 13:58:42 -05:00
// ---------------------
// Capitalization
// ---------------------
FFZ . capitalization = { } ;
2015-01-15 16:42:21 -05:00
FFZ . _cap _fetching = 0 ;
FFZ . get _capitalization = function ( name , callback ) {
name = name . toLowerCase ( ) ;
var old _data = FFZ . capitalization [ name ] ;
if ( old _data ) {
if ( Date . now ( ) - old _data [ 1 ] < 3600000 )
return old _data [ 0 ] ;
}
2015-01-15 13:58:42 -05:00
2015-01-15 16:42:21 -05:00
if ( FFZ . _cap _fetching < 5 ) {
FFZ . _cap _fetching ++ ;
Twitch . api . get ( "users/" + name )
. always ( function ( data ) {
var cap _name = data . display _name || name ;
FFZ . capitalization [ name ] = [ cap _name , Date . now ( ) ] ;
FFZ . _cap _fetching -- ;
callback && callback ( cap _name ) ;
} ) ;
}
2015-01-15 13:58:42 -05:00
2015-01-15 16:42:21 -05:00
return old _data ? old _data [ 0 ] : name ;
2015-01-15 13:58:42 -05:00
}
2015-01-15 16:42:21 -05:00
FFZ . prototype . capitalize = function ( view , user ) {
var name = FFZ . get _capitalization ( user , this . capitalize . bind ( this , view ) ) ;
if ( name )
view . $ ( '.from' ) . text ( name ) ;
}
2015-01-15 13:58:42 -05:00
2015-01-15 22:19:05 -05:00
FFZ . chat _commands . capitalization = function ( room , args ) {
var enabled , args = args && args . length ? args [ 0 ] . toLowerCase ( ) : null ;
if ( args == "y" || args == "yes" || args == "true" || args == "on" )
enabled = true ;
else if ( args == "n" || args == "no" || args == "false" || args == "off" )
enabled = false ;
if ( enabled === undefined )
return "Chat Name Capitalization is currently " + ( localStorage . ffzCapitalize != "false" ? "enabled." : "disabled." ) ;
localStorage . ffzCapitalize = enabled ;
return "Chat Name Capitalization is now " + ( enabled ? "enabled." : "disabled." ) ;
}
FFZ . chat _commands . capitalization . help = "Usage: /ffz capitalization <on|off>\nEnable or disable Chat Name Capitalization. This setting does not work with BetterTTV." ;
2015-01-12 17:58:07 -05:00
// ---------------------
// Emoticon Replacement
// ---------------------
FFZ . prototype . _emoticonize = function ( controller , tokens ) {
var room _id = controller . get ( "parentController.model.id" ) ,
user _id = controller . get ( "model.from" ) ,
user = this . users [ user _id ] ,
room = this . rooms [ room _id ] ,
f = this ;
// Get our sets.
var sets = _ . union ( user && user . sets || [ ] , room && room . sets || [ ] , f . global _sets ) ,
emotes = [ ] ;
// Build a list of emotes that match.
_ . each ( sets , function ( set _id ) {
var set = f . emote _sets [ set _id ] ;
if ( ! set )
return ;
_ . each ( set . emotes , function ( emote ) {
_ . any ( tokens , function ( token ) {
return _ . isString ( token ) && token . match ( emote . regex ) ;
} ) && emotes . push ( emote ) ;
} ) ;
} ) ;
// Don't bother proceeding if we have no emotes.
if ( ! emotes . length )
return tokens ;
// Now that we have all the matching tokens, do crazy stuff.
if ( typeof tokens == "string" )
tokens = [ tokens ] ;
// This is weird stuff I basically copied from the old Twitch code.
// Here, for each emote, we split apart every text token and we
// put it back together with the matching bits of text replaced
// with an object telling Twitch's line template how to render the
// emoticon.
_ . each ( emotes , function ( emote ) {
2015-01-12 18:18:23 -05:00
//var eo = {isEmoticon:true, cls: emote.klass};
2015-01-15 22:19:05 -05:00
var eo = { isEmoticon : true , cls : emote . klass , emoticonSrc : emote . url , altText : ( emote . hidden ? "???" : emote . name ) } ;
2015-01-12 17:58:07 -05:00
tokens = _ . compact ( _ . flatten ( _ . map ( tokens , function ( token ) {
if ( _ . isObject ( token ) )
return token ;
var tbits = token . split ( emote . regex ) , bits = [ ] ;
tbits . forEach ( function ( val , ind ) {
bits . push ( val ) ;
if ( ind !== tbits . length - 1 )
bits . push ( eo ) ;
} ) ;
return bits ;
} ) ) ) ;
} ) ;
return tokens ;
2014-03-27 23:24:57 -04:00
}
2015-01-15 13:58:42 -05:00
} , { } ] , 7 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
var FFZ = window . FrankerFaceZ ,
CSS = /\.([\w\-_]+)\s*?\{content:\s*?"([^"]+)";\s*?background-image:\s*?url\("([^"]+)"\);\s*?height:\s*?(\d+)px;\s*?width:\s*?(\d+)px;\s*?margin:([^;}]+);?([^}]*)\}/mg ,
MOD _CSS = /[^\n}]*\.badges\s+\.moderator\s*{\s*background-image:\s*url\(\s*['"]([^'"]+)['"][^}]+(?:}|$)/ ,
GROUP _CHAT = /^_([^_]+)_\d+$/ ,
constants = require ( '../constants' ) ,
utils = require ( '../utils' ) ,
moderator _css = function ( room ) {
if ( ! room . moderator _badge )
return "" ;
return '.chat-line[data-room="' + room . id + '"] .badges .moderator { background-image:url("' + room . moderator _badge + '") !important; }' ;
}
// --------------------
// Initialization
// --------------------
FFZ . prototype . setup _room = function ( ) {
this . rooms = { } ;
this . log ( "Creating room style element." ) ;
var s = this . _room _style = document . createElement ( "style" ) ;
s . id = "ffz-room-css" ;
document . head . appendChild ( s ) ;
this . log ( "Hooking the Ember Room model." ) ;
var Room = App . _ _container _ _ . resolve ( 'model:room' ) ;
this . _modify _room ( Room ) ;
// Modify all current instances of Room, as the changes to the base
// class won't be inherited automatically.
var instances = Room . instances ;
for ( var key in instances ) {
if ( ! instances . hasOwnProperty ( key ) )
continue ;
var inst = instances [ key ] ;
this . add _room ( inst . id , inst ) ;
this . _modify _room ( inst ) ;
}
}
// --------------------
// Command System
// --------------------
FFZ . chat _commands = { } ;
FFZ . prototype . room _message = function ( room , text ) {
var lines = text . split ( "\n" ) ;
2015-01-15 13:58:42 -05:00
if ( this . has _bttv ) {
for ( var i = 0 ; i < lines . length ; i ++ )
BetterTTV . chat . handlers . onPrivmsg ( room . id , { style : 'admin' , date : new Date ( ) , from : 'jtv' , message : lines [ i ] } ) ;
} else {
for ( var i = 0 ; i < lines . length ; i ++ )
room . room . addMessage ( { style : 'ffz admin' , date : new Date ( ) , from : 'FFZ' , message : lines [ i ] } ) ;
}
2015-01-12 17:58:07 -05:00
}
FFZ . prototype . run _command = function ( text , room _id ) {
var room = this . rooms [ room _id ] ;
if ( ! room || ! room . room )
return ;
if ( ! text )
text = "help" ;
var args = text . split ( " " ) ,
cmd = args . shift ( ) . toLowerCase ( ) ;
this . log ( "Received Command: " + cmd , args , true ) ;
var command = FFZ . chat _commands [ cmd ] , output ;
if ( command ) {
try {
output = command . bind ( this ) ( room , args ) ;
} catch ( err ) {
this . log ( "Error Running Command - " + cmd + ": " + err , room ) ;
output = "There was an error running the command." ;
}
} else
output = 'There is no "' + cmd + '" command.' ;
if ( output )
this . room _message ( room , output ) ;
}
FFZ . chat _commands . help = function ( room , args ) {
if ( args && args . length ) {
var command = FFZ . chat _commands [ args [ 0 ] . toLowerCase ( ) ] ;
if ( ! command )
return 'There is no "' + args [ 0 ] + '" command.' ;
else if ( ! command . help )
return 'No help is available for the command "' + args [ 0 ] + '".' ;
else
return command . help ;
}
var cmds = [ ] ;
for ( var c in FFZ . chat _commands )
FFZ . chat _commands . hasOwnProperty ( c ) && cmds . push ( c ) ;
return "The available commands are: " + cmds . join ( ", " ) ;
}
FFZ . chat _commands . help . help = "Usage: /ffz help [command]\nList available commands, or show help for a specific command." ;
// --------------------
// Room Management
// --------------------
FFZ . prototype . add _room = function ( id , room ) {
if ( this . rooms [ id ] )
return this . log ( "Tried to add existing room: " + id ) ;
this . log ( "Adding Room: " + id ) ;
// Create a basic data table for this room.
this . rooms [ id ] = { id : id , room : room , menu _sets : [ ] , sets : [ ] , css : null } ;
// Let the server know where we are.
this . ws _send ( "sub" , id ) ;
// For now, we use the legacy function to grab the .css file.
this . _legacy _add _room ( id ) ;
}
FFZ . prototype . remove _room = function ( id ) {
var room = this . rooms [ id ] ;
if ( ! room )
return ;
this . log ( "Removing Room: " + id ) ;
// Remove the CSS
if ( room . css || room . moderator _badge )
utils . update _css ( this . _room _style , id , null ) ;
// Let the server know we're gone and delete our data for this room.
this . ws _send ( "unsub" , id ) ;
delete this . rooms [ id ] ;
// Clean up sets we aren't using any longer.
for ( var i = 0 ; i < room . sets . length ; i ++ ) {
var set _id = room . sets [ i ] , set = this . emote _sets [ set _id ] ;
if ( ! set )
continue ;
set . users . removeObject ( id ) ;
if ( ! set . global && ! set . users . length )
this . unload _set ( set _id ) ;
}
}
// --------------------
// Receiving Set Info
// --------------------
FFZ . prototype . load _room = function ( room _id , callback ) {
return this . _legacy _load _room ( room _id , callback ) ;
}
FFZ . prototype . _load _room _json = function ( room _id , callback , data ) {
// Preserve the pointer to the Room instance.
if ( this . rooms [ room _id ] )
data . room = this . rooms [ room _id ] . room ;
this . rooms [ room _id ] = data ;
if ( data . css || data . moderator _badge )
utils . update _css ( this . _room _style , room _id , moderator _css ( data ) + ( data . css || "" ) ) ;
for ( var i = 0 ; i < data . sets . length ; i ++ ) {
var set _id = data . sets [ i ] ;
if ( ! this . emote _sets . hasOwnProperty ( set _id ) )
this . load _set ( set _id ) ;
}
this . update _ui _link ( ) ;
if ( callback )
callback ( true , data ) ;
}
// --------------------
// Ember Modifications
// --------------------
FFZ . prototype . _modify _room = function ( room ) {
var f = this ;
room . reopen ( {
init : function ( ) {
this . _super ( ) ;
f . add _room ( this . id , this ) ;
} ,
willDestroy : function ( ) {
this . _super ( ) ;
f . remove _room ( this . id ) ;
} ,
send : function ( text ) {
var cmd = text . split ( ' ' , 1 ) [ 0 ] . toLowerCase ( ) ;
if ( cmd === "/ffz" ) {
this . set ( "messageToSend" , "" ) ;
f . run _command ( text . substr ( 5 ) , this . get ( 'id' ) ) ;
} else
return this . _super ( text ) ;
}
} ) ;
}
// --------------------
// Legacy Data Support
// --------------------
FFZ . prototype . _legacy _add _room = function ( room _id , callback , tries ) {
jQuery . ajax ( constants . SERVER + "channel/" + room _id + ".css" , { cache : false , context : this } )
. done ( function ( data ) {
this . _legacy _load _room _css ( room _id , callback , data ) ;
} ) . fail ( function ( data ) {
if ( data . status == 404 )
return this . _legacy _load _room _css ( room _id , callback , null ) ;
tries = tries || 0 ;
tries ++ ;
if ( tries < 10 )
return this . _legacy _add _room ( room _id , callback , tries ) ;
} ) ;
}
FFZ . prototype . _legacy _load _room _css = function ( room _id , callback , data ) {
var set _id = room _id ,
match = set _id . match ( GROUP _CHAT ) ;
if ( match && match [ 1 ] )
set _id = match [ 1 ] ;
var output = { id : room _id , menu _sets : [ set _id ] , sets : [ set _id ] , moderator _badge : null , css : null } ;
if ( data )
data = data . replace ( CSS , "" ) . trim ( ) ;
if ( data ) {
data = data . replace ( MOD _CSS , function ( match , url ) {
if ( output . moderator _badge || url . substr ( - 11 ) !== 'modicon.png' )
return match ;
output . moderator _badge = url ;
return "" ;
} ) ;
}
output . css = data || null ;
return this . _load _room _json ( room _id , callback , output ) ;
2014-03-27 22:44:11 -04:00
}
2015-01-15 22:19:05 -05:00
} , { "../constants" : 3 , "../utils" : 19 } ] , 8 : [ function ( require , module , exports ) {
2015-01-15 16:42:21 -05:00
var FFZ = window . FrankerFaceZ ;
// --------------------
// Initialization
// --------------------
FFZ . prototype . setup _viewers = function ( ) {
this . log ( "Hooking the Ember Viewers controller." ) ;
var Viewers = App . _ _container _ _ . resolve ( 'controller:viewers' ) ;
this . _modify _viewers ( Viewers ) ;
}
FFZ . prototype . _modify _viewers = function ( controller ) {
var f = this ;
controller . reopen ( {
lines : function ( ) {
var viewers = this . _super ( ) ,
categories = [ ] ,
data = { } ,
last _category = null ;
// Get the broadcaster name.
var Channel = App . _ _container _ _ . lookup ( 'controller:channel' ) ,
room _id = this . get ( 'parentController.model.id' ) ,
broadcaster = Channel && Channel . get ( 'id' ) ;
// We can get capitalization for the broadcaster from the channel.
if ( broadcaster ) {
var display _name = Channel . get ( 'display_name' ) ;
if ( display _name )
FFZ . capitalization [ broadcaster ] = [ display _name , Date . now ( ) ] ;
}
// If the current room isn't the channel's chat, then we shouldn't
// display them as the broadcaster.
if ( room _id != broadcaster )
broadcaster = null ;
// Now, break the viewer array down into something we can use.
for ( var i = 0 ; i < viewers . length ; i ++ ) {
var entry = viewers [ i ] ;
if ( entry . category ) {
last _category = entry . category ;
categories . push ( last _category ) ;
data [ last _category ] = [ ] ;
} else {
var viewer = entry . chatter . toLowerCase ( ) ;
if ( ! viewer )
continue ;
// If the viewer is the broadcaster, give them their own
// group. Don't put them with normal mods!
if ( viewer == broadcaster ) {
categories . unshift ( "Broadcaster" ) ;
data [ "Broadcaster" ] = [ viewer ] ;
} else if ( data . hasOwnProperty ( last _category ) )
data [ last _category ] . push ( viewer ) ;
}
}
// Now, rebuild the viewer list. However, we're going to actually
// sort it this time.
viewers = [ ] ;
for ( var i = 0 ; i < categories . length ; i ++ ) {
var category = categories [ i ] ,
chatters = data [ category ] ;
if ( ! chatters || ! chatters . length )
continue ;
viewers . push ( { category : category } ) ;
viewers . push ( { chatter : "" } ) ;
// Push the chatters, capitalizing them as we go.
chatters . sort ( ) ;
2015-01-15 16:54:28 -05:00
while ( chatters . length ) {
var viewer = chatters . shift ( ) ;
viewer = f . has _bttv ? BetterTTV . chat . helpers . lookupDisplayName ( viewer ) : FFZ . get _capitalization ( viewer ) ;
viewers . push ( { chatter : viewer } ) ;
}
2015-01-15 16:42:21 -05:00
}
return viewers ;
} . property ( "content.chatters" )
} ) ;
}
} , { } ] , 9 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
var FFZ = window . FrankerFaceZ ,
CSS = /\.([\w\-_]+)\s*?\{content:\s*?"([^"]+)";\s*?background-image:\s*?url\("([^"]+)"\);\s*?height:\s*?(\d+)px;\s*?width:\s*?(\d+)px;\s*?margin:([^;}]+);?([^}]*)\}/mg ,
constants = require ( './constants' ) ,
utils = require ( './utils' ) ;
var loaded _global = function ( set _id , success , data ) {
if ( ! success )
return ;
data . global = true ;
this . global _sets . push ( set _id ) ;
}
var check _margins = function ( margins , height ) {
var mlist = margins . split ( / +/ ) ;
if ( mlist . length != 2 )
return margins ;
mlist [ 0 ] = parseFloat ( mlist [ 0 ] ) ;
mlist [ 1 ] = parseFloat ( mlist [ 1 ] ) ;
if ( mlist [ 0 ] == ( height - 18 ) / - 2 && mlist [ 1 ] == 0 )
return null ;
return margins ;
}
FFZ . prototype . setup _emoticons = function ( ) {
this . log ( "Preparing emoticon system." ) ;
this . emote _sets = { } ;
this . global _sets = [ ] ;
this . _last _emote _id = 0 ;
this . log ( "Creating emoticon style element." ) ;
var s = this . _emote _style = document . createElement ( 'style' ) ;
s . id = "ffz-emoticon-css" ;
document . head . appendChild ( s ) ;
this . log ( "Loading global emote set." ) ;
this . load _set ( "global" , loaded _global . bind ( this , "global" ) ) ;
}
FFZ . ws _commands . reload _set = function ( set _id ) {
this . load _set ( set _id ) ;
}
FFZ . prototype . load _set = function ( set _id , callback ) {
return this . _legacy _load _set ( set _id , callback ) ;
}
FFZ . prototype . unload _set = function ( set _id ) {
var set = this . emote _sets [ set _id ] ;
if ( ! set )
return ;
this . log ( "Unloading emoticons for set: " + set _id ) ;
utils . update _css ( this . _emote _style , set _id , null ) ;
delete this . emote _sets [ set _id ] ;
for ( var i = 0 ; i < set . users . length ; i ++ ) {
var room = this . rooms [ set . users [ i ] ] ;
if ( room )
room . sets . removeObject ( set _id ) ;
}
}
2015-01-12 18:18:23 -05:00
var build _legacy _css = function ( emote ) {
2015-01-12 17:58:07 -05:00
var margin = emote . margins ;
if ( ! margin )
margin = ( ( emote . height - 18 ) / - 2 ) + "px 0" ;
return ".ffz-emote-" + emote . id + ' { background-image: url("' + emote . url + '"); height: ' + emote . height + "px; width: " + emote . width + "px; margin: " + margin + ( emote . extra _css ? "; " + emote . extra _css : "" ) + "}\n" ;
}
2015-01-15 13:58:42 -05:00
var build _new _css = function ( emote ) {
2015-01-12 18:18:23 -05:00
if ( ! emote . margins && ! emote . extra _css )
2015-01-15 13:58:42 -05:00
return build _legacy _css ( emote ) ;
2015-01-12 18:18:23 -05:00
2015-01-15 13:58:42 -05:00
return build _legacy _css ( emote ) + 'img[src="' + emote . url + '"] { ' + ( emote . margins ? "margin: " + emote . margins + ";" : "" ) + ( emote . extra _css || "" ) + " }\n" ;
2015-01-12 18:18:23 -05:00
}
2015-01-15 13:58:42 -05:00
var build _css = build _new _css ;
2015-01-12 18:18:23 -05:00
2015-01-12 17:58:07 -05:00
FFZ . prototype . _load _set _json = function ( set _id , callback , data ) {
// Store our set.
this . emote _sets [ set _id ] = data ;
data . users = [ ] ;
data . global = false ;
2015-01-15 13:58:42 -05:00
data . count = 0 ;
2015-01-12 17:58:07 -05:00
// Iterate through all the emoticons, building CSS and regex objects as appropriate.
var output _css = "" ;
for ( var key in data . emotes ) {
if ( ! data . emotes . hasOwnProperty ( key ) )
continue ;
var emote = data . emotes [ key ] ;
emote . klass = "ffz-emote-" + emote . id ;
if ( emote . name [ emote . name . length - 1 ] === "!" )
emote . regex = new RegExp ( "\\b" + emote . name + "(?=\\W|$)" , "g" ) ;
else
emote . regex = new RegExp ( "\\b" + emote . name + "\\b" , "g" ) ;
output _css += build _css ( emote ) ;
2015-01-15 13:58:42 -05:00
data . count ++ ;
2015-01-12 17:58:07 -05:00
}
utils . update _css ( this . _emote _style , set _id , output _css + ( data . extra _css || "" ) ) ;
this . log ( "Updated emoticons for set: " + set _id , data ) ;
2015-01-15 13:58:42 -05:00
this . update _ui _link ( ) ;
2015-01-12 17:58:07 -05:00
if ( callback )
callback ( true , data ) ;
}
FFZ . prototype . _legacy _load _set = function ( set _id , callback , tries ) {
jQuery . ajax ( constants . SERVER + "channel/" + set _id + ".css" , { cache : false , context : this } )
. done ( function ( data ) {
this . _legacy _load _css ( set _id , callback , data ) ;
} ) . fail ( function ( data ) {
if ( data . status == 404 )
return callback && callback ( false ) ;
tries = tries || 0 ;
tries ++ ;
if ( tries < 10 )
return this . _legacy _load _set ( set _id , callback , tries ) ;
return callback && callback ( false ) ;
} ) ;
}
FFZ . prototype . _legacy _load _css = function ( set _id , callback , data ) {
var emotes = { } , output = { id : set _id , emotes : emotes , extra _css : null } , f = this ;
data . replace ( CSS , function ( match , klass , name , path , height , width , margins , extra ) {
height = parseInt ( height ) ; width = parseInt ( width ) ;
margins = check _margins ( margins , height ) ;
var hidden = path . substr ( path . lastIndexOf ( "/" ) + 1 , 1 ) === "." ,
id = ++ f . _last _emote _id ,
emote = { id : id , hidden : hidden , name : name , height : height , width : width , url : path , margins : margins , extra _css : extra } ;
emotes [ id ] = emote ;
return "" ;
} ) ;
this . _load _set _json ( set _id , callback , output ) ;
2014-03-27 22:44:11 -04:00
}
2015-01-15 22:19:05 -05:00
} , { "./constants" : 3 , "./utils" : 19 } ] , 10 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
// Modify Array and others.
require ( './shims' ) ;
// ----------------
// The Constructor
// ----------------
var FFZ = window . FrankerFaceZ = function ( ) {
FFZ . instance = this ;
// Get things started.
this . initialize ( ) ;
}
FFZ . get = function ( ) { return FFZ . instance ; }
// Version
var VER = FFZ . version _info = {
major : 3 , minor : 0 , revision : 0 ,
toString : function ( ) {
return [ VER . major , VER . minor , VER . revision ] . join ( "." ) + ( VER . extra || "" ) ;
}
}
// Logging
FFZ . prototype . log = function ( msg , data , to _json ) {
msg = "FFZ: " + msg + ( to _json ? " -- " + JSON . stringify ( data ) : "" ) ;
if ( data !== undefined && console . groupCollapsed && console . dir ) {
console . groupCollapsed ( msg ) ;
2015-01-15 16:42:21 -05:00
if ( navigator . userAgent . indexOf ( "Firefox/" ) !== - 1 )
console . log ( data ) ;
else
console . dir ( data ) ;
2015-01-12 17:58:07 -05:00
console . groupEnd ( msg ) ;
} else
console . log ( msg ) ;
}
// -------------------
// User Data
// -------------------
FFZ . prototype . get _user = function ( ) {
if ( window . PP && PP . login ) {
return PP ;
} else if ( window . App ) {
var nc = App . _ _container _ _ . lookup ( "controller:navigation" ) ;
return nc ? nc . get ( "userData" ) : undefined ;
}
}
// -------------------
// Import Everything!
// -------------------
require ( './socket' ) ;
require ( './emoticons' ) ;
require ( './badges' ) ;
require ( './ember/room' ) ;
require ( './ember/line' ) ;
require ( './ember/chatview' ) ;
2015-01-15 16:42:21 -05:00
require ( './ember/viewers' ) ;
2015-01-12 17:58:07 -05:00
require ( './debug' ) ;
2015-01-15 13:58:42 -05:00
require ( './betterttv' ) ;
2015-01-15 22:19:05 -05:00
require ( './featurefriday' ) ;
2015-01-12 17:58:07 -05:00
require ( './ui/styles' ) ;
require ( './ui/notifications' ) ;
require ( './ui/viewer_count' ) ;
require ( './ui/menu_button' ) ;
require ( './ui/menu' ) ;
2015-01-15 13:58:42 -05:00
2015-01-12 17:58:07 -05:00
// ---------------
// Initialization
// ---------------
FFZ . prototype . initialize = function ( increment , delay ) {
// Make sure that FrankerFaceZ doesn't start setting itself up until the
// Twitch ember application is ready.
// TODO: Special Dashboard check.
var loaded = window . App != undefined &&
App . _ _container _ _ != undefined &&
App . _ _container _ _ . resolve ( 'model:room' ) != undefined ;
if ( ! loaded ) {
increment = increment || 10 ;
if ( delay >= 60000 )
this . log ( "Twitch application not detected in \"" + location . toString ( ) + "\". Aborting." ) ;
else
setTimeout ( this . initialize . bind ( this , increment , ( delay || 0 ) + increment ) ,
increment ) ;
return ;
}
this . setup ( delay ) ;
}
FFZ . prototype . setup = function ( delay ) {
2015-01-15 16:42:21 -05:00
var start = ( window . performance && performance . now ) ? performance . now ( ) : Date . now ( ) ;
2015-01-12 17:58:07 -05:00
this . log ( "Found Twitch application after " + ( delay || 0 ) + " ms in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ . version _info ) ;
this . users = { } ;
2015-01-15 16:42:21 -05:00
// Cleanup localStorage
for ( var key in localStorage ) {
if ( key . substr ( 0 , 4 ) == "ffz_" )
localStorage . removeItem ( key ) ;
}
// Store the capitalization of our own name.
var user = this . get _user ( ) ;
if ( user && user . name )
FFZ . capitalization [ user . login ] = [ user . name , Date . now ( ) ] ;
// Initialize all the modules.
2015-01-12 17:58:07 -05:00
try {
this . ws _create ( ) ;
this . setup _emoticons ( ) ;
this . setup _badges ( ) ;
this . setup _room ( ) ;
this . setup _line ( ) ;
this . setup _chatview ( ) ;
2015-01-15 16:42:21 -05:00
this . setup _viewers ( ) ;
2015-01-12 17:58:07 -05:00
this . setup _css ( ) ;
this . setup _menu ( ) ;
2015-01-15 13:58:42 -05:00
this . find _bttv ( 10 ) ;
2015-01-15 22:19:05 -05:00
this . check _ff ( ) ;
2015-01-15 13:58:42 -05:00
2015-01-12 17:58:07 -05:00
} catch ( err ) {
this . log ( "An error occurred while starting FrankerFaceZ: " + err ) ;
return ;
}
2015-01-15 16:42:21 -05:00
if ( window . console && console . time )
console . timeEnd ( "FrankerFaceZ Initialization" ) ;
var end = ( window . performance && performance . now ) ? performance . now ( ) : Date . now ( ) ,
duration = end - start ;
this . log ( "Initialization complete in " + duration + "ms" ) ;
2014-03-27 22:44:11 -04:00
}
2015-01-15 22:19:05 -05:00
} , { "./badges" : 1 , "./betterttv" : 2 , "./debug" : 4 , "./ember/chatview" : 5 , "./ember/line" : 6 , "./ember/room" : 7 , "./ember/viewers" : 8 , "./emoticons" : 9 , "./featurefriday" : 11 , "./shims" : 12 , "./socket" : 13 , "./ui/menu" : 14 , "./ui/menu_button" : 15 , "./ui/notifications" : 16 , "./ui/styles" : 17 , "./ui/viewer_count" : 18 } ] , 11 : [ function ( require , module , exports ) {
var FFZ = window . FrankerFaceZ ,
constants = require ( './constants' ) ;
// --------------------
// Initialization
// --------------------
FFZ . prototype . feature _friday = null ;
// --------------------
// Check FF
// --------------------
FFZ . prototype . check _ff = function ( tries ) {
if ( ! tries )
this . log ( "Checking for Feature Friday data..." ) ;
var f = this ;
jQuery . getJSON ( constants . SERVER + "script/event.json" )
. done ( function ( data ) {
return f . _load _ff ( data ) ;
} ) . fail ( function ( data ) {
if ( data . status == 404 )
return f . _load _ff ( null ) ;
tries = tries || 0 ;
tries ++ ;
if ( tries < 10 )
return setTimeout ( f . check _ff . bind ( this , tries ) , 250 ) ;
return f . _load _ff ( null ) ;
} ) ;
}
FFZ . ws _commands . reload _ff = function ( ) {
this . check _ff ( ) ;
}
// --------------------
// Rendering UI
// --------------------
FFZ . prototype . _feature _friday _ui = function ( room _id , parent , view ) {
if ( ! this . feature _friday || this . feature _friday . channel == room _id )
return ;
this . _emotes _for _sets ( parent , view , [ this . feature _friday . set ] , "Feature Friday" ) ;
// Before we add the button, make sure the channel isn't the
// current channel.
var Channel = App . _ _container _ _ . lookup ( 'controller:channel' ) ;
if ( Channel && Channel . get ( 'id' ) == this . feature _friday . channel )
return ;
var ff = this . feature _friday ,
btnc = document . createElement ( 'div' ) ,
btn = document . createElement ( 'a' ) ;
btnc . className = 'chat-menu-content' ;
btnc . style . textAlign = 'center' ;
var message = ff . display _name + ( ff . live ? " is live now!" : "" ) ;
btn . className = 'button primary' ;
btn . classList . toggle ( 'live' , ff . live ) ;
btn . classList . toggle ( 'blue' , this . has _bttv && BetterTTV . settings . get ( 'showBlueButtons' ) ) ;
btn . href = "http://www.twitch.tv/" + ff . channel ;
btn . title = message ;
btn . target = "_new" ;
btn . innerHTML = "<span>" + message + "</span>" ;
btnc . appendChild ( btn ) ;
parent . appendChild ( btnc ) ;
}
// --------------------
// Loading Data
// --------------------
FFZ . prototype . _load _ff = function ( data ) {
// Check for previous Feature Friday data and remove it.
if ( this . feature _friday ) {
// Remove the global set, delete the data, and reset the UI link.
this . global _sets . removeObject ( this . feature _friday . set ) ;
var set = this . emote _sets [ this . feature _friday . set ] ;
if ( set )
set . global = false ;
this . feature _friday = null ;
this . update _ui _link ( ) ;
}
// If there's no data, just leave.
if ( ! data || ! data . set || ! data . channel )
return ;
// We have our data! Set it up.
this . feature _friday = { set : data . set , channel : data . channel , live : false ,
display _name : FFZ . get _capitalization ( data . channel , this . _update _ff _name . bind ( this ) ) } ;
// Add the set.
this . global _sets . push ( data . set ) ;
this . load _set ( data . set , this . _update _ff _set . bind ( this ) ) ;
// Check to see if the channel is live.
this . _update _ff _live ( ) ;
}
FFZ . prototype . _update _ff _live = function ( ) {
if ( ! this . feature _friday )
return ;
var f = this ;
Twitch . api . get ( "streams/" + this . feature _friday . channel )
. done ( function ( data ) {
f . feature _friday . live = data . stream != null ;
f . update _ui _link ( ) ;
} )
. always ( function ( ) {
f . feature _friday . timer = setTimeout ( f . _update _ff _live . bind ( f ) , 120000 ) ;
} ) ;
}
FFZ . prototype . _update _ff _set = function ( success , set ) {
// Prevent the set from being unloaded.
if ( set )
set . global = true ;
}
FFZ . prototype . _update _ff _name = function ( name ) {
if ( this . feature _friday )
this . feature _friday . display _name = name ;
}
} , { "./constants" : 3 } ] , 12 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
Array . prototype . equals = function ( array ) {
// if the other array is a falsy value, return
if ( ! array )
return false ;
// compare lengths - can save a lot of time
if ( this . length != array . length )
return false ;
for ( var i = 0 , l = this . length ; i < l ; i ++ ) {
// Check if we have nested arrays
if ( this [ i ] instanceof Array && array [ i ] instanceof Array ) {
// recurse into the nested arrays
if ( ! this [ i ] . equals ( array [ i ] ) )
return false ;
}
else if ( this [ i ] != array [ i ] ) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false ;
}
}
return true ;
}
2015-01-15 22:19:05 -05:00
} , { } ] , 13 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
var FFZ = window . FrankerFaceZ ;
FFZ . prototype . _ws _open = false ;
FFZ . prototype . _ws _delay = 0 ;
FFZ . ws _commands = { } ;
// ----------------
// Socket Creation
// ----------------
FFZ . prototype . ws _create = function ( ) {
var f = this ;
this . _ws _last _req = 0 ;
this . _ws _callbacks = { } ;
var ws = this . _ws _sock = new WebSocket ( "ws://ffz.stendec.me/" ) ;
ws . onopen = function ( e ) {
f . _ws _open = true ;
f . _ws _delay = 0 ;
f . log ( "Socket connected." ) ;
var user = f . get _user ( ) ;
if ( user )
f . ws _send ( "setuser" , user . login ) ;
// Send the current rooms.
for ( var room _id in f . rooms )
f . ws _send ( "sub" , room _id ) ;
}
ws . onclose = function ( e ) {
f . log ( "Socket closed." ) ;
f . _ws _open = false ;
// We never ever want to not have a socket.
if ( f . _ws _delay < 30000 )
f . _ws _delay += 5000 ;
setTimeout ( f . ws _create . bind ( f ) , f . _ws _delay ) ;
}
ws . onmessage = function ( e ) {
// Messages are formatted as REQUEST_ID SUCCESS/FUNCTION_NAME[ JSON_DATA]
var cmd , data , ind = e . data . indexOf ( " " ) ,
msg = e . data . substr ( ind + 1 ) ,
request = parseInt ( e . data . slice ( 0 , ind ) ) ;
ind = msg . indexOf ( " " ) ;
if ( ind === - 1 )
ind = msg . length ;
cmd = msg . slice ( 0 , ind ) ;
msg = msg . substr ( ind + 1 ) ;
if ( msg )
data = JSON . parse ( msg ) ;
if ( request === - 1 ) {
// It's a command from the server.
var command = FFZ . ws _commands [ cmd ] ;
if ( command )
command . bind ( f ) ( data ) ;
else
f . log ( "Invalid command: " + cmd , data ) ;
} else {
var success = cmd === 'True' ,
callback = f . _ws _callbacks [ request ] ;
f . log ( "Socket Reply to " + request + " - " + ( success ? "SUCCESS" : "FAIL" ) , data ) ;
if ( callback ) {
delete f . _ws _callbacks [ request ] ;
callback ( success , data ) ;
}
}
}
}
FFZ . prototype . ws _send = function ( func , data , callback ) {
if ( ! this . _ws _open ) return false ;
var request = ++ this . _ws _last _req ;
data = data !== undefined ? " " + JSON . stringify ( data ) : "" ;
if ( callback )
this . _ws _callbacks [ request ] = callback ;
this . _ws _sock . send ( request + " " + func + data ) ;
return request ;
2014-03-27 22:44:11 -04:00
}
2015-01-15 22:19:05 -05:00
} , { } ] , 14 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
var FFZ = window . FrankerFaceZ ;
// --------------------
// Initializer
// --------------------
FFZ . prototype . setup _menu = function ( ) {
this . log ( "Installing mouse-up event to auto-close menus." ) ;
var f = this ;
jQuery ( document ) . mouseup ( function ( e ) {
var popup = f . _popup , parent ;
if ( ! popup ) return ;
popup = jQuery ( popup ) ;
parent = popup . parent ( ) ;
if ( ! parent . is ( e . target ) && parent . has ( e . target ) . length === 0 ) {
popup . remove ( ) ;
delete f . _popup ;
}
} ) ;
}
// --------------------
// Create Menu
// --------------------
FFZ . prototype . build _ui _popup = function ( view ) {
var popup = this . _popup ;
if ( popup ) {
popup . parentElement . removeChild ( popup ) ;
delete this . _popup ;
return ;
}
// Start building the DOM.
var container = document . createElement ( 'div' ) ,
inner = document . createElement ( 'div' ) ;
container . className = 'emoticon-selector chat-menu ffz-ui-popup' ;
inner . className = 'emoticon-selector-box dropmenu' ;
container . appendChild ( inner ) ;
// TODO: Modularize for multiple menu pages!
// Get the current room.
var room _id = view . get ( 'controller.currentRoom.id' ) ,
room = this . rooms [ room _id ] ;
this . log ( "Menu for Room: " + room _id , room ) ;
// Add the header and ad button.
var btn = document . createElement ( 'a' ) ;
btn . className = 'button glyph-only ffz-button' ;
btn . title = 'Advertise for FrankerFaceZ in chat!' ;
btn . href = '#' ;
btn . innerHTML = '<svg class="svg-followers" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M8,13.5L1.5,7V4l2-2h3L8,3.5L9.5,2h3l2,2v3L8,13.5z" fill-rule="evenodd"></path></svg>' ;
var hdr = document . createElement ( 'div' ) ;
hdr . className = 'list-header first' ;
hdr . appendChild ( btn ) ;
hdr . appendChild ( document . createTextNode ( 'FrankerFaceZ' ) ) ;
inner . appendChild ( hdr ) ;
var c = this . _emotes _for _sets ( inner , view , room && room . menu _sets || [ ] ) ;
if ( c === 0 )
btn . addEventListener ( 'click' , this . _add _emote . bind ( this , view , "To use custom emoticons in tons of channels, get FrankerFaceZ from http://www.frankerfacez.com" ) ) ;
else
btn . addEventListener ( 'click' , this . _add _emote . bind ( this , view , "To view this channel's emoticons, get FrankerFaceZ from http://www.frankerfacez.com" ) ) ;
2015-01-15 22:19:05 -05:00
// Feature Friday!
this . _feature _friday _ui ( room _id , inner , view ) ;
2015-01-12 17:58:07 -05:00
// Add the menu to the DOM.
this . _popup = container ;
inner . style . maxHeight = Math . max ( 300 , view . $ ( ) . height ( ) - 171 ) + "px" ;
view . $ ( '.chat-interface' ) . append ( container ) ;
}
// --------------------
// Emotes for Sets
// --------------------
FFZ . prototype . _emotes _for _sets = function ( parent , view , sets , header , btn ) {
if ( header != null ) {
var el _header = document . createElement ( 'div' ) ;
el _header . className = 'list-header' ;
el _header . appendChild ( document . createTextNode ( header ) ) ;
if ( btn )
el _header . appendChild ( btn ) ;
parent . appendChild ( el _header ) ;
}
var grid = document . createElement ( 'div' ) , c = 0 ;
grid . className = 'emoticon-grid' ;
for ( var i = 0 ; i < sets . length ; i ++ ) {
var set = this . emote _sets [ sets [ i ] ] ;
2015-01-15 13:58:42 -05:00
if ( ! set || ! set . emotes )
continue ;
2015-01-12 17:58:07 -05:00
for ( var eid in set . emotes ) {
var emote = set . emotes [ eid ] ;
if ( ! set . emotes . hasOwnProperty ( eid ) || emote . hidden )
continue ;
c ++ ;
2015-01-15 13:58:42 -05:00
var s = document . createElement ( 'span' ) ;
s . className = 'emoticon tooltip' ;
s . style . backgroundImage = 'url("' + emote . url + '")' ;
s . style . width = emote . width + "px" ;
s . style . height = emote . height + "px" ;
2015-01-12 17:58:07 -05:00
s . title = emote . name ;
s . addEventListener ( 'click' , this . _add _emote . bind ( this , view , emote . name ) ) ;
grid . appendChild ( s ) ;
}
}
if ( ! c ) {
grid . innerHTML = "This channel has no emoticons." ;
grid . className = "chat-menu-content ffz-no-emotes center" ;
}
parent . appendChild ( grid ) ;
}
FFZ . prototype . _add _emote = function ( view , emote ) {
var room = view . get ( 'controller.currentRoom' ) ,
current _text = room . get ( 'messageToSend' ) || '' ;
if ( current _text && current _text . substr ( - 1 ) !== " " )
current _text += ' ' ;
room . set ( 'messageToSend' , current _text + ( emote . name || emote ) ) ;
2014-03-27 22:44:11 -04:00
}
2015-01-15 22:19:05 -05:00
} , { } ] , 15 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
var FFZ = window . FrankerFaceZ ,
constants = require ( '../constants' ) ;
// --------------------
// Initialization
// --------------------
FFZ . prototype . build _ui _link = function ( view ) {
var link = document . createElement ( 'a' ) ;
link . className = 'ffz-ui-toggle' ;
link . innerHTML = constants . CHAT _BUTTON ;
link . addEventListener ( 'click' , this . build _ui _popup . bind ( this , view ) ) ;
this . update _ui _link ( link ) ;
return link ;
}
FFZ . prototype . update _ui _link = function ( link ) {
var controller = App . _ _container _ _ . lookup ( 'controller:chat' ) ;
link = link || document . querySelector ( 'a.ffz-ui-toggle' ) ;
2015-01-15 13:58:42 -05:00
if ( ! link || ! controller ) return this . log ( "No button." ) ;
2015-01-12 17:58:07 -05:00
var room _id = controller . get ( 'currentRoom.id' ) ,
room = this . rooms [ room _id ] ,
2015-01-15 13:58:42 -05:00
has _emotes = false ,
2015-01-12 17:58:07 -05:00
2015-01-15 13:58:42 -05:00
dark = ( this . has _bttv ? BetterTTV . settings . get ( 'darkenedMode' ) : false ) ,
2015-01-15 22:19:05 -05:00
blue = ( this . has _bttv ? BetterTTV . settings . get ( 'showBlueButtons' ) : false ) ,
live = ( this . feature _friday && this . feature _friday . live ) ;
2015-01-15 13:58:42 -05:00
// Check for emoticons.
if ( room && room . sets . length ) {
for ( var i = 0 ; i < room . sets . length ; i ++ ) {
var set = this . emote _sets [ room . sets [ i ] ] ;
if ( set && set . count > 0 ) {
has _emotes = true ;
break ;
}
}
}
link . classList . toggle ( 'no-emotes' , ! has _emotes ) ;
2015-01-15 22:19:05 -05:00
link . classList . toggle ( 'live' , live ) ;
2015-01-15 13:58:42 -05:00
link . classList . toggle ( 'dark' , dark ) ;
link . classList . toggle ( 'blue' , blue ) ;
2014-03-27 22:44:11 -04:00
}
2015-01-15 22:19:05 -05:00
} , { "../constants" : 3 } ] , 16 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
var FFZ = window . FrankerFaceZ ;
FFZ . prototype . show _notification = function ( message ) {
window . noty ( {
text : message ,
theme : "ffzTheme" ,
layout : "bottomCenter" ,
closeWith : [ "button" ]
} ) . show ( ) ;
}
FFZ . ws _commands . message = function ( message ) {
this . show _notification ( message ) ;
2014-03-27 22:44:11 -04:00
}
2015-01-15 22:19:05 -05:00
} , { } ] , 17 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
var FFZ = window . FrankerFaceZ ,
constants = require ( '../constants' ) ;
FFZ . prototype . setup _css = function ( ) {
this . log ( "Injecting main FrankerFaceZ CSS." ) ;
var s = this . _main _style = document . createElement ( 'link' ) ;
s . id = "ffz-ui-css" ;
s . setAttribute ( 'rel' , 'stylesheet' ) ;
s . setAttribute ( 'href' , constants . SERVER + "script/style.css" ) ;
document . head . appendChild ( s ) ;
jQuery . noty . themes . ffzTheme = {
name : "ffzTheme" ,
style : function ( ) {
this . $bar . removeClass ( ) . addClass ( "noty_bar" ) . addClass ( "ffz-noty" ) . addClass ( this . options . type ) ;
} ,
callback : {
onShow : function ( ) { } ,
onClose : function ( ) { }
}
} ;
2014-03-27 22:44:11 -04:00
}
2015-01-15 22:19:05 -05:00
} , { "../constants" : 3 } ] , 18 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
var FFZ = window . FrankerFaceZ ,
constants = require ( '../constants' ) ,
utils = require ( '../utils' ) ;
// ------------
// Set Viewers
// ------------
FFZ . ws _commands . viewers = function ( data ) {
var channel = data [ 0 ] , count = data [ 1 ] ;
var controller = App . _ _container _ _ . lookup ( 'controller:channel' ) ,
id = controller && controller . get && controller . get ( 'id' ) ;
if ( id !== channel )
return ;
var view _count = document . querySelector ( '.channel-stats .ffz.stat' ) ,
content = constants . ZREKNARF + ' ' + utils . number _commas ( count ) ;
if ( view _count )
view _count . innerHTML = content ;
else {
var parent = document . querySelector ( '.channel-stats' ) ;
if ( ! parent )
return ;
view _count = document . createElement ( 'span' ) ;
view _count . className = 'ffz stat' ;
view _count . title = 'Viewers with FrankerFaceZ' ;
view _count . innerHTML = content ;
parent . appendChild ( view _count ) ;
jQuery ( view _count ) . tipsy ( ) ;
}
2014-03-27 22:44:11 -04:00
}
2015-01-15 22:19:05 -05:00
} , { "../constants" : 3 , "../utils" : 19 } ] , 19 : [ function ( require , module , exports ) {
2015-01-12 17:58:07 -05:00
var FFZ = window . FrankerFaceZ ,
constants = require ( './constants' ) ;
module . exports = {
update _css : function ( element , id , css ) {
var all = element . innerHTML ,
start = "/*BEGIN " + id + "*/" ,
end = "/*END " + id + "*/" ,
s _ind = all . indexOf ( start ) ,
e _ind = all . indexOf ( end ) ,
found = s _ind !== - 1 && e _ind !== - 1 && e _ind > s _ind ;
if ( ! found && ! css )
return ;
if ( found )
all = all . substr ( 0 , s _ind ) + all . substr ( e _ind + end . length ) ;
if ( css )
all += start + css + end ;
element . innerHTML = all ;
} ,
number _commas : function ( x ) {
var parts = x . toString ( ) . split ( "." ) ;
parts [ 0 ] = parts [ 0 ] . replace ( /\B(?=(\d{3})+(?!\d))/g , "," ) ;
return parts . join ( "." ) ;
}
2014-03-27 22:44:11 -04:00
}
2015-01-15 16:42:21 -05:00
} , { "./constants" : 3 } ] } , { } , [ 10 ] ) ; window . ffz = new FrankerFaceZ ( ) } ( window ) ) ;