diff --git a/script.js b/script.js
index d5e8e85b..433c67b7 100644
--- a/script.js
+++ b/script.js
@@ -55,6 +55,7 @@ var ffz = function() {
ffz.prototype.last_set = 0;
ffz.prototype.last_emote = 0;
ffz.prototype.manger = null;
+ffz.prototype.has_bttv = false;
ffz.commands = {};
@@ -162,6 +163,9 @@ ffz.prototype.setup = function() {
document.addEventListener("DOMContentLoaded", this.listen_dom, false);
}
+ // Detect BetterTTV
+ this.find_bttv(10);
+
this.log("Initialization complete.");
};
@@ -198,7 +202,10 @@ ffz.prototype.listen_dom = function() {
// Commands
// -----------------
-var msg = function(room, out) {
+ffz.prototype._msg = function(room, out) {
+ if ( this.has_bttv )
+ return BetterTTV.chat.helpers.serverMessage(out.replace(/\n/g, "
"));
+
out = out.split("\n");
for(var i=0; i < out.length; i++)
room.addMessage({style: 'ffz admin', from: 'FFZ', message: out[i]});
@@ -216,7 +223,7 @@ ffz.prototype.run_command = function(room, m) {
else
out = "No such sub-command.";
- if ( out ) msg(room, out);
+ if ( out ) this._msg(room, out);
}
ffz.commands['help'] = function(room, args) {
@@ -335,11 +342,13 @@ ffz.commands['log'] = function(room, args) {
ffz.commands['log'].help = "Usage: /ffz log\nOpen a window with FFZ's debugging output.";
ffz.commands['list'] = function(room, args) {
- var output = '', filter;
+ var output = '', filter, html = this.has_bttv;
if ( args && args.length > 0 )
filter = args.join(" ").toLowerCase();
+ if ( html ) output += "
";
+
for(var name in this.collections) {
if ( ! this.collections.hasOwnProperty(name) )
return;
@@ -353,19 +362,30 @@ ffz.commands['list'] = function(room, args) {
if ( !include )
continue;
+ if ( html )
+ output += "" + name + " | ";
+ else
+ output += name + "\n";
+
var em = this.collections[name];
- output += name + "\n";
for(var e in em) {
if ( em.hasOwnProperty(e) ) {
var emote = em[e], t = emote.text;
- t = t[0] + "\u200B" + t.substr(1);
- output += " " + t + " = " + emote.text + "\n";
+ if ( html )
+ output += "" + t + " | " + emote.image.html + " |
";
+ else {
+ t = t[0] + "\u200B" + t.substr(1);
+ output += " " + t + " = " + emote.text + "\n";
+ }
}
}
+ if ( html ) output += "";
}
+ if ( html ) output += "
";
+
// Make sure we actually have output.
- if ( output.indexOf('\u200B') === -1 )
+ if ( output.indexOf(html ? '' : '\u200B') === -1 )
return "There are no available FFZ channel emoticons. If this is in error, please try the /ffz reload command.";
else
return "The following emotes are available:\n" + output;
@@ -393,7 +413,7 @@ ffz.commands['inject'] = function(room, args) {
return "/ffz inject requires exactly 1 argument.";
var album = args[0].split('/').pop().split('?').shift().split('#').shift();
- room.addMessage({style: 'ffz admin', message: "Attempting to load test emoticons from imgur album \"" + album + "\"..."});
+ this._msg(room, "Attempting to load test emoticons from imgur album \"" + album + "\"...");
// Make sure there's no cache hits.
var res = "https://api.imgur.com/3/album/" + album;
@@ -404,13 +424,13 @@ ffz.commands['inject'] = function(room, args) {
{'Accept': 'application/json', 'Authorization': 'Client-ID ' + IMGUR_KEY},
5);
}
-ffz.commands['inject'].help = "Usage: /ffz inject \nLoads emoticons from an imgur album for testing. album-id can simply be the album URL. Ex: /ffz inject http://imgur.com/a/v4aZr";
+ffz.commands['inject'].help = "Usage: /ffz inject [album-id]\nLoads emoticons from an imgur album for testing. album-id can simply be the album URL. Ex: /ffz inject http://imgur.com/a/v4aZr";
ffz.prototype.do_imgur = function(room, album, data) {
if ( data === undefined )
- return msg(room, "An error occurred communicating with Imgur.");
+ return this._msg(room, "An error occurred communicating with Imgur.");
else if ( !data )
- return msg(room, "The named album does not exist or is private.");
+ return this._msg(room, "The named album does not exist or is private.");
// Get our data structure.
data = JSON.parse(data).data;
@@ -438,8 +458,56 @@ ffz.prototype.do_imgur = function(room, album, data) {
}
var count = this.process_css('imgur-' + album, 'FFZ Global Emotes - Imgur Album: ' + album, css);
- msg(room, "Loaded " + count + " emoticons from Imgur.");
- msg(room, ffz.commands['list'].bind(this)(room, [album]));
+ this._msg(room, "Loaded " + count + " emoticons from Imgur.");
+ this._msg(room, ffz.commands['list'].bind(this)(room, [album]));
+}
+
+
+// -----------------
+// BetterTTV Hooks
+// -----------------
+
+ffz.prototype.find_bttv = function(increment, delay) {
+ if ( !this.alive ) return;
+
+ if ( window.BTTVLOADED )
+ return this.setup_bttv();
+
+ else if ( delay === undefined )
+ this.log("BetterTTV not yet loaded. Waiting...");
+
+ if ( delay >= 60000 )
+ this.log("BetterTTV not detected in \"" + location.toString() + "\". Giving up.");
+ else
+ setTimeout(this.find_bttv.bind(this, increment, (delay||0) + increment),
+ increment);
+}
+
+var donor_badge = {type: 'ffz-donor', name: '', description: 'FFZ Donor'};
+
+ffz.prototype.setup_bttv = function() {
+ this.log("BetterTTV was detected. Installing hook.");
+ this.has_bttv = true;
+
+ // Add badge handling to BetterTTV chat.
+ var privmsg = BetterTTV.chat.templates.privmsg, f = this;
+ BetterTTV.chat.templates.privmsg = function(highlight, action, server, isMod, data) {
+ if ( f.check_donor(data.sender) ) {
+ var inserted = false;
+ for(var i=0; i < data.badges.length; i++) {
+ var t = data.badges[i].type;
+ if ( t != 'turbo' && t != 'subscriber' )
+ continue;
+ data.badges.insertAt(i, donor_badge);
+ inserted = true;
+ break;
+ }
+ if ( ! inserted )
+ data.badges.push(donor_badge);
+ }
+
+ return privmsg(highlight, action, server, isMod, data);
+ }
}
@@ -600,8 +668,9 @@ ffz.prototype.add_channel = function(id, room) {
// Do we have log messages?
if ( this._log2.length > 0 ) {
+ var func = this.has_bttv ? BetterTTV.chat.helpers.serverMessage : room.addTmiMessage;
while ( this._log2.length )
- room.addTmiMessage(this._log2.shift());
+ func(this._log2.shift());
}
// Load the emotes for this channel.
@@ -730,8 +799,9 @@ ffz.prototype.process_css = function(group, channel, data) {
this.log("Loaded " + count + " emotes from collection: " + group);
// Notify the manager that we've added emotes.
- if ( this.manager )
- this.manager.notifyPropertyChange('emoticons');
+ // Don't notify the manager for now because of BTTV.
+ //if ( this.manager && ! this.has_bttv )
+ // this.manager.notifyPropertyChange('emoticons');
return count;
}
@@ -791,8 +861,9 @@ ffz.prototype.unload_emotes = function(group) {
this.emoticons = this.emoticons.filter(filt);
// Update the emoticons with the manager.
- if ( this.manager )
- this.manager.notifyPropertyChange('emoticons');
+ // Don't notify the manager for now for BTTV.
+ //if ( this.manager && ! this.has_bttv )
+ // this.manager.notifyPropertyChange('emoticons');
}
diff --git a/script.min.js b/script.min.js
new file mode 100644
index 00000000..3b0bfcb5
--- /dev/null
+++ b/script.min.js
@@ -0,0 +1,31 @@
+(function wrapper(l,t){if(t){var p=document.createElement("script");p.textContent="("+wrapper+")(window, false)";document.body.appendChild(p);document.body.removeChild(p)}else{var u=/\.([\w\-_]+) \{content:.*?"([^"]+)";.*?background-image: url\("([^"]+)"\);.*?height:.*?(\d+).+?width:.*?(\d+)[^}]*\}/mg,r=-1!==location.search.indexOf("frankerfacez"),d=function(){this.alive=!0;this.donors={};this.getting={};this.emoticons=[];this.emotesets={};this.channels={};this.collections={};this.globals={};this.global_sets=
+[];this.styles={};this.pending_styles=[];this._log=[];this._log2=[];this.init(10)};d.prototype.last_set=0;d.prototype.last_emote=0;d.prototype.manger=null;d.prototype.has_bttv=!1;d.commands={};d.prototype.log=function(b){this._log.push(b);b="FFZ"+(this.alive?": ":" (Dead): ")+b;console.log(b);if(r){var c,a;for(a in this.channels)if(this.channels[a]&&this.channels[a].room){c=this.channels[a];break}c?c.room.addTmiMessage(b):this._log2.push(b)}};d.prototype.init=function(b,c){if(this.alive){if(l.CurrentChat&&
+l.CurrentChat.emoticons)return this.log("Detected old chat. Injecting old FFZ."),this.inject_old();if(void 0==l.Ember||void 0==l.App||void 0==App.EmoticonsController||void 0==App.Room)6E4<=c?this.log('Twitch API not detected in "'+location.toString()+'". Aborting.'):setTimeout(this.init.bind(this,b,(c||0)+b),b);else{if(App.hasOwnProperty("useNewChat")&&!App.useNewChat)return this.log("Detected old chat. Injecting old FFZ."),this.inject_old();this.setup()}}};d.prototype.inject_old=function(){this._dom&&
+document.removeEventListener("DOMContentLoaded",this._dom,!1);if(document.body){var b=document.createElement("script");b.src="//commondatastorage.googleapis.com/frankerfacez/script/old-frankerfacez.js";document.body.appendChild(b);document.body.removeChild(b)}else this._dom=this.inject_old.bind(this),document.addEventListener("DOMContentLoaded",this._dom,!1)};d.prototype.setup=function(){this.alive&&(this.log("Hooking Ember application."),this.modify_room(),this.modify_viewers(),this.modify_emotes(),
+this.modify_lines(),this.log("Loading data."),this.load_donors(),this.load_emotes("global"),document.body||(this.listen_dom=this.listen_dom.bind(this),document.addEventListener("DOMContentLoaded",this.listen_dom,!1)),this.find_bttv(10),this.log("Initialization complete."))};d.prototype.destroy=function(){this.alive&&(alive=!1,l.ffz===this&&(l.ffz=void 0),delete this._log,delete this._log2)};d.prototype.listen_dom=function(){for(document.removeEventListener("DOMContentLoaded",this.listen_dom,!1);this.pending_styles.length;)document.body.appendChild(this.pending_styles.pop())};
+d.prototype._msg=function(b,c){if(this.has_bttv)return BetterTTV.chat.helpers.serverMessage(c.replace(/\n/g," "));c=c.split("\n");for(var a=0;a');for(var f in this.collections){if(!this.collections.hasOwnProperty(f))return;if(d?-1!==f.toLowerCase().indexOf(d):"FFZ Global Emotes"!==f){var a=g?a+(''+f+" | "):a+(f+"\n"),e=this.collections[f],n;for(n in e)if(e.hasOwnProperty(n)){var k=e[n],
+m=k.text;g?a+=''+m+" | "+k.image.html+" | ":(m=m[0]+"\u200b"+m.substr(1),a+=" "+m+" = "+k.text+"\n")}g&&(a+="")}}g&&(a+="");return-1===a.indexOf(g?"":"\u200b")?"There are no available FFZ channel emoticons. If this is in error, please try the /ffz reload command.":"The following emotes are available:\n"+a};d.commands.list.help="Usage: /ffz list [global]\nList available FFZ emoticons. Use the global parameter to list ALL FFZ emoticons, or filter for a specific set.";
+d.commands.global=function(b,c){return d.commands.list.bind(this)(b,["global"])};d.commands.global.help="Usage: /ffz global\nShorthand for /ffz list global. List ALL FFZ emoticons, including FFZ global emoticons.";d.commands.reload=function(b,c){for(var a in this.channels)this.channels.hasOwnProperty(a)&&this.channels[a]&&this.load_emotes(a,!0);this.load_emotes("global");this.load_donors();return"Attempting to reload FFZ data from the server."};d.commands.reload.help="Usage: /ffz reload\nAttempt to reload FFZ emoticons and donors.";
+d.commands.inject=function(b,c){if(!c||1!==c.length)return"/ffz inject requires exactly 1 argument.";var a=c[0].split("/").pop().split("?").shift().split("#").shift();this._msg(b,'Attempting to load test emoticons from imgur album "'+a+'"...');var d="https://api.imgur.com/3/album/"+a;l.localStorage&&localStorage.removeItem("ffz_"+d);this.get(d,this.do_imgur.bind(this,b,a),1,{Accept:"application/json",Authorization:"Client-ID e48d122e3437051"},5)};d.commands.inject.help="Usage: /ffz inject [album-id]\nLoads emoticons from an imgur album for testing. album-id can simply be the album URL. Ex: /ffz inject http://imgur.com/a/v4aZr";
+d.prototype.do_imgur=function(b,c,a){if(void 0===a)return this._msg(b,"An error occurred communicating with Imgur.");if(!a)return this._msg(b,"The named album does not exist or is private.");a=JSON.parse(a).data;a=a.images;for(var h="",g=0;g',id:--k.last_emote};a="!"===e[e.length-1]?RegExp("\\b"+e+"(?=\\W|$)","g"):RegExp("\\b"+e+"\\b","g");e={image:h,images:[h],text:e,channel:c,hidden:!1,regex:a,ffzset:b};m.push(e);k.emoticons.push(e);f.push({isEmoticon:!0,cls:d,regex:a});l++});this.log("Loaded "+l+" emotes from collection: "+
+b);return l}};d.prototype.unload_emotes=function(b){if(this.alive){var c=this.channels[b],a,d,g;if(!1!==c){c?(a=c.set_id,d=c.style,g="FFZ Channel Emotes: "+b,delete c.set,delete c.set_id,delete c.style):(a=this.globals[b],d=this.styles[b],g="FFZ Global Emotes"+("global"!=b?": "+b:""),delete this.globals[b],delete this.styles[b],c=this.global_sets.indexOf(a),-1!==c&&this.global_sets.splice(c,1));this.collections[g]&&delete this.collections[g];if(d)try{d.parentNode.removeChild(d)}catch(f){}delete this.emotesets[a];
+this.manager&&delete this.manager.emoticonSets[a];this.emoticons=this.emoticons.filter(function(a){return a.ffzgroup!==b})}}};d.prototype.check_donor=function(b){return this.donors[b]||!1};d.prototype.load_donors=function(b){this.get("//commondatastorage.googleapis.com/frankerfacez/donors.txt",this.process_donors.bind(this),b?1:108E5)};d.prototype.process_donors=function(b){if(this.alive){this.donors={};var c=0;if(null!=b){b=b.trim().split(/\W+/);for(var a=0;aa?(this.log("Resource expired. Fetching: "+
+b),this.do_get(b,c,0,d,g)):this.getting[b]=!1}};d.prototype.do_get=function(b,c,a,d,g){function f(){var e=(a||0)+1;if(!g||e<=g)return setTimeout(k.do_get.bind(k,b,c,e,d,g),1E3),!0}if(this.alive){var e=new XMLHttpRequest;e.open("GET",b);if(d)for(var n in d)d.hasOwnProperty(n)&&e.setRequestHeader(n,d[n]);var k=this;e.addEventListener("error",function(a){if(!f()){k.getting[b]=!1;try{c(void 0)}catch(d){k.log("Error in callback: "+d)}}},!1);e.addEventListener("load",function(a){if(200===e.status){if(a=
+e.responseText,l.localStorage){var d=localStorage.getItem("ffz_last_"+b),g=e.getResponseHeader("Last-Modified");if(d&&d==g){k.log("Resource not modified: "+b);localStorage.setItem("ffz_age_"+b,(new Date).getTime());k.getting[b]=!1;return}localStorage.setItem("ffz_last_"+b,g)}}else{if(304===e.status){k.log("Resource not modified: "+b);l.localStorage&&localStorage.setItem("ffz_age_"+b,(new Date).getTime());k.getting[b]=!1;return}if(404===e.status)a=null;else{if(f())return;a=void 0}}l.localStorage&&
+void 0!==a&&(localStorage.setItem("ffz_"+b,JSON.stringify(a)),localStorage.setItem("ffz_age_"+b,(new Date).getTime()));k.getting[b]=!1;try{c(a)}catch(h){k.log("Error in callback: "+h)}},!1);e.send()}else this.getting[b]=!1};l.ffz=new d}})(this.unsafeWindow||window,window.chrome?!0:!1);
\ No newline at end of file
| |