1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-10-20 09:52:01 +00:00

Bunch o changes. I'm a bad person.

This commit is contained in:
SirStendec 2015-06-05 03:59:28 -04:00
parent 576c9569b2
commit 6264da62fc
25 changed files with 4610 additions and 1860 deletions

View file

@ -8,35 +8,277 @@ var FFZ = window.FrankerFaceZ,
// --------------------
FFZ.prototype.setup_channel = function() {
this.channels = {};
// Settings stuff!
document.body.classList.toggle("ffz-hide-view-count", !this.settings.channel_views);
this.log("Creating channel style element.");
var s = this._channel_style = document.createElement('style');
s.id = "ffz-channel-css";
document.head.appendChild(s);
this.log("Hooking the Ember Channel view.");
var Channel = App.__container__.lookup('controller:channel'),
this.log("Hooking the Ember Channel Index view.");
var Channel = App.__container__.resolve('view:channel/index'),
f = this;
if ( ! Channel )
return;
this._modify_cindex(Channel);
// The Stupid View Fix. Is this necessary still?
try {
Channel.create().destroy();
} catch(err) { }
// Update Existing
for(var key in Ember.View.views) {
if ( ! Ember.View.views.hasOwnProperty(key) )
continue;
var view = Ember.View.views[key];
if ( !(view instanceof Channel) )
continue;
this.log("Manually updating Channel Index view.", view);
this._modify_cindex(view);
view.ffzInit();
};
this.log("Hooking the Ember Channel controller.");
Channel = App.__container__.lookup('controller:channel');
if ( ! Channel )
return;
Channel.reopen({
ffzUpdateUptime: function() {
f.update_uptime();
}.observes("isLive", "content.id").on("init")
if ( f._cindex )
f._cindex.ffzUpdateUptime();
/*ffzUpdateInfo: function() {
f.log("Updated! ID: " + this.get("content.id"));
f.update_stream_info(true);
}.observes("content.id").on("init")*/
}.observes("isLive", "content.id"),
ffzUpdateTitle: function() {
var name = this.get('content.name'),
display_name = this.get('content.display_name');
if ( display_name )
FFZ.capitalization[name] = [display_name, Date.now()];
if ( f._cindex )
f._cindex.ffzFixTitle();
}.observes("content.status", "content.id")
/*ffzHostTarget: function() {
var target = this.get('content.hostModeTarget'),
name = target && target.get('name'),
display_name = target && target.get('display_name');
if ( display_name )
FFZ.capitalization[name] = [display_name, Date.now()];
if ( f.settings.group_tabs && f._chatv )
f._chatv.ffzRebuildTabs();
}.observes("content.hostModeTarget")*/
});
}
// Do uptime the first time.
this.update_uptime();
//this.update_stream_info(true);
FFZ.prototype._modify_cindex = function(view) {
var f = this;
view.reopen({
didInsertElement: function() {
this._super();
try {
this.ffzInit();
} catch(err) {
f.error("CIndex didInsertElement: " + err);
}
},
willClearRender: function() {
try {
this.ffzTeardown();
} catch(err) {
f.error("CIndex willClearRender: " + err);
}
return this._super();
},
ffzInit: function() {
f._cindex = this;
this.get('element').setAttribute('data-channel', this.get('controller.id'));
this.ffzFixTitle();
this.ffzUpdateUptime();
this.ffzUpdateChatters();
var el = this.get('element').querySelector('.svg-glyph_views:not(.ffz-svg)')
if ( el )
el.parentNode.classList.add('twitch-channel-views');
},
ffzFixTitle: function() {
if ( f.has_bttv || ! f.settings.stream_title )
return;
var status = this.get("controller.status"),
channel = this.get("controller.id");
status = f.render_tokens(f.tokenize_line(channel, channel, status, true));
this.$(".title span").each(function(i, el) {
var scripts = el.querySelectorAll("script");
el.innerHTML = scripts[0].outerHTML + status + scripts[1].outerHTML;
});
},
ffzUpdateChatters: function() {
// Get the counts.
var room_id = this.get('controller.id'),
room = f.rooms && f.rooms[room_id];
if ( ! room || ! f.settings.chatter_count ) {
var el = this.get('element').querySelector('#ffz-chatter-display');
el && el.parentElement.removeChild(el);
el = this.get('element').querySelector('#ffz-ffzchatter-display');
el && el.parentElement.removeChild(el);
return;
}
var chatter_count = Object.keys(room.room.get('ffz_chatters') || {}).length,
ffz_chatters = room.ffz_chatters || 0;
var el = this.get('element').querySelector('#ffz-chatter-display span');
if ( ! el ) {
var cont = this.get('element').querySelector('.stats-and-actions .channel-stats');
if ( ! cont )
return;
var stat = document.createElement('span');
stat.className = 'ffz stat';
stat.id = 'ffz-chatter-display';
stat.title = "Current Chatters";
stat.innerHTML = constants.ROOMS + " ";
el = document.createElement("span");
stat.appendChild(el);
var other = cont.querySelector("#ffz-ffzchatter-display");
if ( other )
cont.insertBefore(stat, other);
else
cont.appendChild(stat);
jQuery(stat).tipsy();
}
el.innerHTML = utils.number_commas(chatter_count);
if ( ! ffz_chatters ) {
el = this.get('element').querySelector('#ffz-ffzchatter-display');
el && el.parentNode.removeChild(el);
return;
}
el = this.get('element').querySelector('#ffz-ffzchatter-display span');
if ( ! el ) {
var cont = this.get('element').querySelector('.stats-and-actions .channel-stats');
if ( ! cont )
return;
var stat = document.createElement('span');
stat.className = 'ffz stat';
stat.id = 'ffz-ffzchatter-display';
stat.title = "Chatters with FrankerFaceZ";
stat.innerHTML = constants.ZREKNARF + " ";
el = document.createElement("span");
stat.appendChild(el);
var other = cont.querySelector("#ffz-chatter-display");
if ( other )
cont.insertBefore(stat, other.nextSibling);
else
cont.appendChild(stat);
jQuery(stat).tipsy();
}
el.innerHTML = utils.number_commas(ffz_chatters);
},
ffzUpdateUptime: function() {
if ( this._ffz_update_uptime ) {
clearTimeout(this._ffz_update_uptime);
delete this._ffz_update_uptime;
}
if ( ! f.settings.stream_uptime || ! this.get("controller.isLiveAccordingToKraken") ) {
var el = this.get('element').querySelector('#ffz-uptime-display');
if ( el )
el.parentElement.removeChild(el);
return;
}
// Schedule an update.
this._ffz_update_uptime = setTimeout(this.ffzUpdateUptime.bind(this), 1000);
// Determine when the channel last went live.
var online = this.get("controller.content.stream.created_at");
if ( ! online )
return;
online = utils.parse_date(online);
if ( ! online )
return;
var uptime = Math.floor((Date.now() - online.getTime()) / 1000);
if ( uptime < 0 )
return;
var el = this.get('element').querySelector('#ffz-uptime-display span');
if ( ! el ) {
var cont = this.get('element').querySelector('.stats-and-actions .channel-stats');
if ( ! cont )
return;
var stat = document.createElement('span');
stat.className = 'ffz stat';
stat.id = 'ffz-uptime-display';
stat.title = "Stream Uptime <nobr>(since " + online.toLocaleString() + ")</nobr>";
stat.innerHTML = constants.CLOCK + " ";
el = document.createElement("span");
stat.appendChild(el);
var viewers = cont.querySelector(".live-count");
if ( viewers )
cont.insertBefore(stat, viewers.nextSibling);
else {
try {
viewers = cont.querySelector("script:nth-child(0n+2)");
cont.insertBefore(stat, viewers.nextSibling);
} catch(err) {
cont.insertBefore(stat, cont.childNodes[0]);
}
}
jQuery(stat).tipsy({html: true});
}
el.innerHTML = utils.time_to_string(uptime);
},
ffzTeardown: function() {
this.get('element').setAttribute('data-channel', '');
f._cindex = undefined;
if ( this._ffz_update_uptime )
clearTimeout(this._ffz_update_uptime);
}
});
}
@ -44,6 +286,42 @@ FFZ.prototype.setup_channel = function() {
// Settings
// ---------------
FFZ.settings_info.chatter_count = {
type: "boolean",
value: false,
category: "Channel Metadata",
name: "Chatter Count",
help: "Display the current number of users connected to chat beneath the channel.",
on_update: function(val) {
if ( this._cindex )
this._cindex.ffzUpdateChatters();
if ( ! val || ! this.rooms )
return;
// Refresh the data.
for(var room_id in this.rooms)
this.rooms.hasOwnProperty(room_id) && this.rooms[room_id].room && this.rooms[room_id].room.ffzInitChatterCount();
}
};
FFZ.settings_info.channel_views = {
type: "boolean",
value: true,
category: "Channel Metadata",
name: "Channel Views",
help: 'Display the number of times the channel has been viewed beneath the stream.',
on_update: function(val) {
document.body.classList.toggle("ffz-hide-view-count", !val);
}
};
FFZ.settings_info.stream_uptime = {
type: "boolean",
value: false,
@ -52,120 +330,22 @@ FFZ.settings_info.stream_uptime = {
name: "Stream Uptime",
help: 'Display the stream uptime under a channel by the viewer count.',
on_update: function(val) {
this.update_uptime();
if ( this._cindex )
this._cindex.ffzUpdateUptime();
}
};
// --------------------
// Stream Data Update
// --------------------
FFZ.settings_info.stream_title = {
type: "boolean",
value: true,
no_bttv: true,
/*FFZ.prototype.update_stream_info = function(just_schedule) {
if ( this._stream_info_update ) {
clearTimeout(this._stream_info_update);
delete this._stream_info_update;
}
this._stream_info_update = setTimeout(this.update_stream_info.bind(this), 90000);
if ( just_schedule )
return;
var Channel = App.__container__.lookup('controller:channel'),
channel_id = Channel ? Channel.get('content.id') : undefined,
f = this;
if ( ! channel_id )
return;
Twitch.api.get("streams/" + channel_id, {}, {version: 3})
.done(function(data) {
var channel_id = Channel.get('content.id'), d = data.stream;
if ( ! data.stream || d.channel.name != channel_id )
return;
// Override the data in Twitch. We can't just .load() the stream
// because that resets the whole channel layout, resetting the
// video player. Twitch pls fix
var old_created = Channel.get('content.stream.created_at');
Channel.set('content.stream.created_at', d.created_at);
Channel.set('content.stream.average_fps', d.average_fps);
Channel.set('content.stream.viewers', d.viewers);
Channel.set('content.stream.video_height', d.video_height);
Channel.set('content.stream.csGoSkill', Twitch.uri.csGoSkillImg(("0" + d.skill).slice(-2)));
Channel.set('content.stream.game', d.game);
Channel.set('content.stream.gameUrl', Twitch.uri.game(d.game));
Channel.set('content.stream.gameBoxart', Twitch.uri.gameBoxArtJpg(d.game));
// Update the uptime display.
if ( f.settings.stream_uptime && old_created != d.created_at )
f.update_uptime(true) && f.update_uptime();
});
}*/
// --------------------
// Uptime Display
// --------------------
FFZ.prototype.update_uptime = function(destroy) {
if ( this._uptime_update ) {
clearTimeout(this._uptime_update);
delete this._uptime_update;
}
var Channel = App.__container__.lookup('controller:channel');
if ( destroy || ! this.settings.stream_uptime || ! Channel || ! Channel.get('isLiveAccordingToKraken') ) {
var el = document.querySelector("#ffz-uptime-display");
if ( el )
el.parentElement.removeChild(el);
return;
}
// Schedule an update.
this._update_uptime = setTimeout(this.update_uptime.bind(this), 1000);
// Determine when the channel last went live.
var online = Channel.get('content.stream.created_at');
if ( ! online ) return;
online = utils.parse_date(online);
if ( ! online ) return;
var uptime = Math.floor((Date.now() - online.getTime()) / 1000);
if ( uptime < 0 ) return;
var el = document.querySelector("#ffz-uptime-display span");
if ( ! el ) {
var cont = document.querySelector("#channel .stats-and-actions .channel-stats");
if ( ! cont ) return;
var stat = document.createElement("span");
stat.className = "ffz stat";
stat.id = "ffz-uptime-display";
stat.title = "Stream Uptime <nobr>(since " + online.toLocaleString() + ")</nobr>";
stat.innerHTML = constants.CLOCK + " ";
el = document.createElement("span");
stat.appendChild(el);
var viewers = cont.querySelector(".live-count");
if ( viewers )
cont.insertBefore(stat, viewers.nextSibling);
else {
try {
viewers = cont.querySelector("script:nth-child(0n+2)");
cont.insertBefore(stat, viewers.nextSibling);
} catch(err) {
cont.insertBefore(stat, cont.childNodes[0]);
}
category: "Channel Metadata",
name: "Title Links",
help: "Make links in stream titles clickable.",
on_update: function(val) {
if ( this._cindex )
this._cindex.ffzFixTitle();
}
jQuery(stat).tipsy({html:true});
}
el.innerHTML = utils.time_to_string(uptime);
}
};

View file

@ -1,4 +1,55 @@
var FFZ = window.FrankerFaceZ;
var FFZ = window.FrankerFaceZ,
utils = require('../utils'),
constants = require('../constants'),
format_unread = function(count) {
if ( count < 1 )
return "";
else if ( count >= 99 )
return "99+";
return "" + count;
};
// --------------------
// Settings
// --------------------
FFZ.settings_info.group_tabs = {
type: "boolean",
value: false,
no_bttv: true,
category: "Chat",
name: "Chat Room Tabs <span>Beta</span>",
help: "Enhanced UI for switching the current chat room and noticing new messages.",
on_update: function(val) {
var enabled = !this.has_bttv && val;
if ( ! this._chatv || enabled === this._group_tabs_state )
return;
if ( enabled )
this._chatv.ffzEnableTabs();
else
this._chatv.ffzDisableTabs();
}
};
FFZ.settings_info.pinned_rooms = {
type: "button",
value: [],
category: "Chat",
visible: false,
name: "Pinned Chat Rooms",
help: "Set a list of channels that should always be available in chat."
};
// --------------------
@ -6,6 +57,21 @@ var FFZ = window.FrankerFaceZ;
// --------------------
FFZ.prototype.setup_chatview = function() {
this.log("Hooking the Ember Chat controller.");
var Chat = App.__container__.lookup('controller:chat'),
f = this;
if ( Chat ) {
Chat.reopen({
ffzUpdateChannels: function() {
if ( f.settings.group_tabs && f._chatv )
f._chatv.ffzRebuildTabs();
}.observes("currentChannelRoom", "connectedPrivateGroupRooms")
});
}
this.log("Hooking the Ember Chat view.");
var Chat = App.__container__.resolve('view:chat');
@ -17,7 +83,6 @@ FFZ.prototype.setup_chatview = function() {
Chat.create().destroy();
} catch(err) { }
// Modify all existing Chat views.
for(var key in Ember.View.views) {
if ( ! Ember.View.views.hasOwnProperty(key) )
@ -27,13 +92,45 @@ FFZ.prototype.setup_chatview = function() {
if ( !(view instanceof Chat) )
continue;
this.log("Adding UI link manually to Chat view.", view);
this.log("Manually updating existing Chat view.", view);
try {
view.$('.textarea-contain').append(this.build_ui_link(view));
view.ffzInit();
} catch(err) {
this.error("setup: build_ui_link: " + err);
}
}
this.log("Hooking the Ember Layout controller.");
var Layout = App.__container__.lookup('controller:layout');
if ( ! Layout )
return;
Layout.reopen({
ffzFixTabs: function() {
if ( f.settings.group_tabs && f._chatv && f._chatv._ffz_tabs ) {
setTimeout(function() {
f._chatv && f._chatv.$('.chat-room').css('top', f._chatv._ffz_tabs.offsetHeight + "px");
},0);
}
}.observes("isRightColumnClosed")
});
this.log("Hooking the Ember 'Right Column' controller. Seriously...");
var Column = App.__container__.lookup('controller:right-column');
if ( ! Column )
return;
Column.reopen({
ffzFixTabs: function() {
if ( f.settings.group_tabs && f._chatv && f._chatv._ffz_tabs ) {
setTimeout(function() {
f._chatv && f._chatv.$('.chat-room').css('top', f._chatv._ffz_tabs.offsetHeight + "px");
},0);
}
}.observes("firstTabSelected")
});
}
@ -47,28 +144,391 @@ FFZ.prototype._modify_cview = function(view) {
view.reopen({
didInsertElement: function() {
this._super();
try {
this.$() && this.$('.textarea-contain').append(f.build_ui_link(this));
this.ffzInit();
} catch(err) {
f.error("didInsertElement: build_ui_link: " + err);
f.error("ChatView didInsertElement: " + err);
}
},
willClearRender: function() {
this._super();
try {
this.$(".ffz-ui-toggle").remove();
this.ffzTeardown();
} catch(err) {
f.error("willClearRender: remove ui link: " + err);
f.error("ChatView willClearRender: " + err);
}
this._super();
},
ffzUpdateLink: Ember.observer('controller.currentRoom', function() {
ffzInit: function() {
f._chatv = this;
this.$('.textarea-contain').append(f.build_ui_link(this));
if ( !f.has_bttv && f.settings.group_tabs )
this.ffzEnableTabs();
setTimeout(function() {
if ( f.settings.group_tabs && f._chatv._ffz_tabs )
f._chatv.$('.chat-room').css('top', f._chatv._ffz_tabs.offsetHeight + "px");
var controller = f._chatv.get('controller');
controller && controller.set('showList', false);
}, 1000);
},
ffzTeardown: function() {
if ( f._chatv === this )
f._chatv = null;
this.$('.textarea-contain .ffz-ui-toggle').remove();
if ( f.settings.group_tabs )
this.ffzDisableTabs();
},
ffzChangeRoom: Ember.observer('controller.currentRoom', function() {
try {
f.update_ui_link();
if ( !f.has_bttv && f.settings.group_tabs && this._ffz_tabs ) {
var room = this.get('controller.currentRoom');
room && room.resetUnreadCount();
var tabs = jQuery(this._ffz_tabs);
tabs.children('.ffz-chat-tab').removeClass('active');
if ( room )
tabs.children('.ffz-chat-tab[data-room="' + room.get('id') + '"]').removeClass('tab-mentioned').addClass('active').children('span').text('');
// Invite Link
var can_invite = room && room.get('canInvite');
this._ffz_invite && this._ffz_invite.classList.toggle('hidden', !can_invite);
this.set('controller.showInviteUser', can_invite && this.get('controller.showInviteUser'))
// Now, adjust the chat-room.
this.$('.chat-room').css('top', this._ffz_tabs.offsetHeight + "px");
}
} catch(err) {
f.error("ffzUpdateLink: update_ui_link: " + err);
f.error("ChatView ffzUpdateLink: " + err);
}
})
}),
// Group Tabs~!
ffzEnableTabs: function() {
if ( f.has_bttv || ! f.settings.group_tabs )
return;
// Hide the existing chat UI.
this.$(".chat-header").addClass("hidden");
// Create our own UI.
var tabs = this._ffz_tabs = document.createElement("div");
tabs.id = "ffz-group-tabs";
this.$(".chat-header").after(tabs);
// List the Rooms
this.ffzRebuildTabs();
},
ffzRebuildTabs: function() {
if ( f.has_bttv || ! f.settings.group_tabs )
return;
var tabs = this._ffz_tabs || this.get('element').querySelector('#ffz-group-tabs');
if ( ! tabs )
return;
tabs.innerHTML = "";
var link = document.createElement('a'),
view = this;
link.className = 'button glyph-only tooltip';
link.title = "Chat Room Management";
link.innerHTML = constants.ROOMS;
link.addEventListener('click', function() {
var controller = view.get('controller');
controller && controller.set('showList', !controller.get('showList'));
});
tabs.appendChild(link);
link = document.createElement('a'),
link.className = 'button glyph-only tooltip invite';
link.title = "Invite a User";
link.innerHTML = constants.INVITE;
link.addEventListener('click', function() {
var controller = view.get('controller');
controller && controller.set('showInviteUser', controller.get('currentRoom.canInvite') && !controller.get('showInviteUser'));
});
link.classList.toggle('hidden', !this.get("controller.currentRoom.canInvite"));
view._ffz_invite = link;
tabs.appendChild(link);
var room = this.get('controller.currentChannelRoom'), tab;
if ( room ) {
tab = this.ffzBuildTab(view, room, true);
tab && tabs.appendChild(tab);
}
// Check Host Target
var Channel = App.__container__.lookup('controller:channel'),
Room = App.__container__.resolve('model:room');
target = Channel && Channel.get('hostModeTarget');
if ( target && Room ) {
var target_id = target.get('id');
if ( this._ffz_host !== target_id ) {
if ( this._ffz_host_room ) {
if ( this.get('controller.currentRoom') === this._ffz_host_room )
this.get('controller').blurRoom();
this._ffz_host_room.destroy();
}
this._ffz_host = target_id;
this._ffz_host_room = Room.findOne(target_id);
}
} else if ( this._ffz_host ) {
if ( this._ffz_host_room ) {
if ( this.get('controller.currentRoom') === this._ffz_host_room )
this.get('controller').blurRoom();
this._ffz_host_room.destroy();
}
delete this._ffz_host;
delete this._ffz_host_room;
}
if ( this._ffz_host_room ) {
tab = view.ffzBuildTab(view, this._ffz_host_room, false, true);
tab && tabs.appendChild(tab);
}
// Pinned Rooms
for(var i=0; i < f.settings.pinned_rooms.length; i++) {
var room_id = f.settings.pinned_rooms[i];
if ( room && room.get('id') !== room_id && this._ffz_host !== room_id && f.rooms[room_id] && f.rooms[room_id].room ) {
var tab = view.ffzBuildTab(view, f.rooms[room_id].room, false, false);
tab && tabs.appendChild(tab);
}
}
_.each(this.get('controller.connectedPrivateGroupRooms'), function(room) {
var tab = view.ffzBuildTab(view, room);
tab && tabs.appendChild(tab);
});
// Now, adjust the chat-room.
this.$('.chat-room').css('top', tabs.offsetHeight + "px");
},
ffzTabUnread: function(room_id) {
if ( f.has_bttv || ! f.settings.group_tabs )
return;
var tabs = this._ffz_tabs || this.get('element').querySelector('#ffz-group-tabs'),
current_id = this.get('controller.currentRoom.id');
if ( ! tabs )
return;
if ( room_id ) {
var tab = tabs.querySelector('.ffz-chat-tab[data-room="' + room_id + '"]'),
room = f.rooms && f.rooms[room_id];
if ( tab && room ) {
var unread = format_unread(room_id === current_id ? 0 : room.room.get('unreadCount'));
tab.querySelector('span').innerHTML = unread;
}
// Now, adjust the chat-room.
return this.$('.chat-room').css('top', tabs.offsetHeight + "px");
}
var children = tabs.querySelectorAll('.ffz-chat-tab');
for(var i=0; i < children.length; i++) {
var tab = children[i],
room_id = tab.getAttribute('data-room'),
room = f.rooms && f.rooms[room_id];
if ( ! room )
continue;
var unread = format_unread(room_id === current_id ? 0 : room.room.get('unreadCount'));
tab.querySelector('span').innerHTML = unread;
}
// Now, adjust the chat-room.
this.$('.chat-room').css('top', tabs.offsetHeight + "px");
},
ffzBuildTab: function(view, room, current_channel, host_channel) {
var tab = document.createElement('span'), name, unread,
group = room.get('isGroupRoom'),
current = room === view.get('controller.currentRoom');
tab.setAttribute('data-room', room.id);
tab.className = 'ffz-chat-tab tooltip';
tab.classList.toggle('current-channel', current_channel);
tab.classList.toggle('host-channel', host_channel);
tab.classList.toggle('group-chat', group);
tab.classList.toggle('active', current);
name = room.get('tmiRoom.displayName') || (group ? room.get('tmiRoom.name') : FFZ.get_capitalization(room.get('id')));
unread = format_unread(current ? 0 : room.get('unreadCount'));
if ( current_channel ) {
tab.innerHTML = constants.CAMERA;
tab.title = "Current Channel";
} else if ( host_channel ) {
tab.innerHTML = constants.EYE;
tab.title = "Hosted Channel";
} else if ( group )
tab.title = "Group Chat";
else
tab.title = "Pinned Channel";
tab.innerHTML += utils.sanitize(name) + '<span>' + unread + '</span>';
tab.addEventListener('click', function() {
view.get('controller').focusRoom(room);
});
return tab;
},
ffzDisableTabs: function() {
if ( this._ffz_tabs ) {
this._ffz_tabs.parentElement.removeChild(this._ffz_tabs);
delete this._ffz_tabs;
delete this._ffz_invite;
}
if ( this._ffz_host ) {
if ( this._ffz_host_room ) {
if ( this.get('controller.currentRoom') === this._ffz_host_room )
this.get('controller').blurRoom();
this._ffz_host_room.destroy();
}
delete this._ffz_host;
delete this._ffz_host_room;
}
// Show the old chat UI.
this.$('.chat-room').css('top', '');
this.$(".chat-header").removeClass("hidden");
},
});
}
// ----------------------
// Chat Room Connections
// ----------------------
FFZ.prototype.connect_extra_chat = function() {
if ( this.has_bttv )
return;
for(var i=0; i < this.settings.pinned_rooms.length; i++)
this._join_room(this.settings.pinned_rooms[i], true);
if ( ! this.has_bttv && this._chatv && this.settings.group_tabs )
this._chatv.ffzRebuildTabs();
}
FFZ.prototype._join_room = function(room_id, no_rebuild) {
var did_join = false;
if ( this.settings.pinned_rooms.indexOf(room_id) === -1 ) {
this.settings.pinned_rooms.push(room_id);
this.settings.set("pinned_rooms", this.settings.pinned_rooms);
did_join = true;
}
// Make sure we're not already there.
if ( this.rooms[room_id] && this.rooms[room_id].room )
return did_join;
// Okay, fine. Get it.
var Room = App.__container__.resolve('model:room'),
r = Room && Room.findOne(room_id);
// Finally, rebuild the chat UI.
if ( ! no_rebuild && ! this.has_bttv && this._chatv && this.settings.group_tabs )
this._chatv.ffzRebuildTabs();
return did_join;
}
FFZ.prototype._leave_room = function(room_id, no_rebuild) {
var did_leave = false;
if ( this.settings.pinned_rooms.indexOf(room_id) !== -1 ) {
this.settings.pinned_rooms.removeObject(room_id);
this.settings.set("pinned_rooms", this.settings.pinned_rooms);
did_leave = true;
}
if ( ! this.rooms[room_id] || ! this.rooms[room_id].room )
return did_leave;
var Chat = App.__container__.lookup('controller:chat'),
r = this.rooms[room_id].room;
if ( ! Chat || Chat.get('currentChannelRoom.id') === room_id || (this._chatv && this._chatv._ffz_host === room_id) )
return did_leave;
if ( Chat.get('currentRoom.id') === room_id )
Chat.blurRoom();
r.destroy();
if ( ! no_rebuild && ! this.has_bttv && this._chatv && this.settings.group_tabs )
this._chatv.ffzRebuildTabs();
return did_leave;
}
// ----------------------
// Commands
// ----------------------
FFZ.chat_commands.join = function(room, args) {
if ( ! args || ! args.length || args.length > 1 )
return "Join Usage: /join <channel>";
var room_id = args[0].toLowerCase();
if ( room_id.charAt(0) === "#" )
room_id = room_id.substr(1);
if ( this._join_room(room_id) )
return "Joining " + room_id + ". You will always connect to this channel's chat unless you later /part from it.";
else
return "You have already joined " + room_id + ". Please use \"/part " + room_id + "\" to leave it.";
}
FFZ.chat_commands.part = function(room, args) {
if ( ! args || ! args.length || args.length > 1 )
return "Part Usage: /part <channel>";
var room_id = args[0].toLowerCase();
if ( room_id.charAt(0) === "#" )
room_id = room_id.substr(1);
if ( this._leave_room(room_id) )
return "Leaving " + room_id + ".";
else if ( this.rooms[room_id] )
return "You do not have " + room_id + " pinned and you cannot leave the current channel or hosted channels via /part.";
else
return "You are not in " + room_id + ".";
}

View file

@ -1,10 +1,6 @@
var FFZ = window.FrankerFaceZ,
utils = require("../utils"),
reg_escape = function(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
SEPARATORS = "[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]",
SPLITTER = new RegExp(SEPARATORS + "*," + SEPARATORS + "*"),
@ -26,7 +22,8 @@
data_to_tooltip = function(data) {
var set = data.set,
set_type = data.set_type;
set_type = data.set_type,
owner = data.owner;
if ( set_type === undefined )
set_type = "Channel";
@ -39,7 +36,7 @@
set_type = null;
}
return "Emoticon: " + data.code + "\n" + (set_type ? set_type + ": " : "") + set;
return "Emoticon: " + data.code + "\n" + (set_type ? set_type + ": " : "") + set + (owner ? "\nBy: " + owner.display_name : "");
},
build_tooltip = function(id) {
@ -112,6 +109,12 @@
tooltip += "<nobr>Views: " + utils.number_commas(link_data.views) + "</nobr> | <nobr>Followers: " + utils.number_commas(link_data.followers) + "</nobr>";
} else if ( link_data.type == "twitch_vod" ) {
tooltip = "<b>Twitch " + (link_data.broadcast_type == "highlight" ? "Highlight" : "Broadcast") + ": " + utils.sanitize(link_data.title) + "</b><hr>";
tooltip += "By: " + utils.sanitize(link_data.display_name) + (link_data.game ? " | Playing: " + utils.sanitize(link_data.game) : " | Not Playing") + "<br>";
tooltip += "Views: " + utils.number_commas(link_data.views) + " | " + utils.time_to_string(link_data.length);
} else if ( link_data.type == "twitter" ) {
tooltip = "<b>Tweet By: " + utils.sanitize(link_data.user) + "</b><hr>";
tooltip += utils.sanitize(link_data.tweet);
@ -321,27 +324,41 @@ FFZ.prototype.setup_line = function() {
this.log("Hooking the Ember Line controller.");
var Line = App.__container__.resolve('controller:line'),
var Line = App.__container__.resolve('component:message-line'),
f = this;
Line.reopen({
tokenizedMessage: function() {
// Add our own step to the tokenization procedure.
var tokens = this._super();
var tokens = this.get("msgObject.cachedTokens");
if ( tokens )
return tokens;
tokens = this._super();
try {
var start = performance.now();
var start = performance.now(),
user = f.get_user(),
from_me = user && this.get("msgObject.from") === user.login;
tokens = f._remove_banned(tokens);
tokens = f._emoticonize(this, tokens);
var user = f.get_user();
if ( ! user || this.get("model.from") != user.login )
tokens = f._mentionize(this, tokens);
// Store the capitalization.
var display = this.get("model.tags.display-name");
var display = this.get("msgObject.tags.display-name");
if ( display && display.length )
FFZ.capitalization[this.get("model.from")] = [display.trim(), Date.now()];
FFZ.capitalization[this.get("msgObject.from")] = [display.trim(), Date.now()];
if ( ! from_me )
tokens = f.tokenize_mentions(tokens);
for(var i = 0; i < tokens.length; i++) {
var token = tokens[i];
if ( ! _.isString(token) && token.mentionedUser && ! token.own ) {
this.set('msgObject.ffz_has_mention', true);
break;
}
}
var end = performance.now();
if ( end - start > 5 )
@ -353,38 +370,30 @@ FFZ.prototype.setup_line = function() {
} catch(err) { }
}
this.set("msgObject.cachedTokens", tokens);
return tokens;
}.property("model.message", "isModeratorOrHigher")
});
}.property("msgObject.message", "isChannelLinksDisabled", "currentUserNick", "msgObject.from", "msgObject.tags.emotes"),
this.log("Hooking the Ember Line view.");
var Line = App.__container__.resolve('view:line');
Line.reopen({
didInsertElement: function() {
this._super();
try {
var start = performance.now();
var el = this.get('element'),
controller = this.get('context'),
user = controller.get('model.from'),
room = controller.get('parentController.content.id'),
color = controller.get('model.color'),
row_type = controller.get('model.ffz_alternate');
user = this.get('msgObject.from'),
room = this.get('msgObject.room') || App.__container__.lookup('controller:chat').get('currentRoom.id'),
color = this.get('msgObject.color'),
row_type = this.get('msgObject.ffz_alternate');
// Color Processing
if ( color )
f._handle_color(color);
// Row Alternation
if ( row_type === undefined ) {
row_type = f._last_row[room] = f._last_row.hasOwnProperty(room) ? !f._last_row[room] : false;
this.set("context.model.ffz_alternate", row_type);
this.set("msgObject.ffz_alternate", row_type);
}
el.classList.toggle('ffz-alternate', row_type);
@ -393,7 +402,7 @@ FFZ.prototype.setup_line = function() {
// Basic Data
el.setAttribute('data-room', room);
el.setAttribute('data-sender', user);
el.setAttribute('data-deleted', controller.get('model.deleted'));
el.setAttribute('data-deleted', this.get('deleted')||false);
// Badge
@ -401,36 +410,9 @@ FFZ.prototype.setup_line = function() {
// Mention Highlighting
var mentioned = el.querySelector('span.mentioned');
if ( mentioned ) {
if ( this.get("msgObject.ffz_has_mention") )
el.classList.add("ffz-mentioned");
if ( f.settings.highlight_notifications && !document.hasFocus() && !this.get('context.model.ffz_notified') ) {
var cap_room = FFZ.get_capitalization(room),
cap_user = FFZ.get_capitalization(user),
room_name = cap_room,
msg = this.get("context.model.message");
if ( this.get("context.parentController.content.isGroupRoom") )
room_name = this.get("context.parentController.content.tmiRoom.displayName");
if ( this.get("context.model.style") == "action" )
msg = "* " + cap_user + " " + msg;
else
msg = cap_user + ": " + msg;
f.show_notification(
msg,
"Twitch Chat Mention in " + room_name,
cap_room,
60000,
window.focus.bind(window)
);
}
}
// Mark that we've checked this message for mentions.
this.set('context.model.ffz_notified', true);
// Banned Links
var bad_links = el.querySelectorAll('a.deleted-link');
@ -541,19 +523,16 @@ FFZ.prototype.setup_line = function() {
set_id = data && data[1] || null,
set = f.emote_sets[set_id],
emote = set ? set.emotes[id] : null,
emote = set ? set.emoticons[id] : null;
set_name = set ? (set.title || set.id) : "Unknown FFZ Set",
set_type = (set && set.title) ? "FrankerFaceZ" : "FFZ Channel";
// High-DPI!
if ( emote && emote.srcSet )
img.setAttribute('srcset', emote.srcSet);
if ( set && f.feature_friday && set.id == f.feature_friday.set )
set_name = f.feature_friday.title + " - " + f.feature_friday.display_name;
img.title = data_to_tooltip({
code: emote ? (emote.hidden ? "???" : emote.name) : name,
set: set_name,
set_type: set_type
});
img.title = f._emote_tooltip(emote);
}
}
@ -681,75 +660,6 @@ FFZ.get_capitalization = function(name, callback) {
}
// ---------------------
// Extra Mentions
// ---------------------
FFZ._regex_cache = {};
FFZ._get_regex = function(word) {
return FFZ._regex_cache[word] = FFZ._regex_cache[word] || RegExp("\\b" + reg_escape(word) + "\\b", "ig");
}
FFZ._words_to_regex = function(list) {
var regex = FFZ._regex_cache[list];
if ( ! regex ) {
var reg = "";
for(var i=0; i < list.length; i++) {
if ( ! list[i] )
continue;
reg += (reg ? "|" : "") + reg_escape(list[i]);
}
regex = FFZ._regex_cache[list] = new RegExp("(^|.*?" + SEPARATORS + ")(" + reg + ")(?=$|" + SEPARATORS + ")", "ig");
}
return regex;
}
FFZ.prototype._mentionize = function(controller, tokens) {
var mention_words = this.settings.keywords;
if ( ! mention_words || ! mention_words.length )
return tokens;
if ( typeof tokens == "string" )
tokens = [tokens];
var regex = FFZ._words_to_regex(mention_words),
new_tokens = [];
for(var i=0; i < tokens.length; i++) {
var token = tokens[i];
if ( ! _.isString(token) ) {
new_tokens.push(token);
continue;
}
if ( ! token.match(regex) ) {
new_tokens.push(token);
continue;
}
token = token.replace(regex, function(all, prefix, match) {
new_tokens.push(prefix);
new_tokens.push({
mentionedUser: match,
own: false
});
return "";
});
if ( token )
new_tokens.push(token);
}
return new_tokens;
}
// ---------------------
// Banned Words
// ---------------------
@ -790,58 +700,9 @@ FFZ.prototype._remove_banned = function(tokens) {
// Emoticon Replacement
// ---------------------
FFZ.prototype._emoticonize = function(controller, tokens) {
var room_id = controller.get("parentController.model.id"),
user_id = controller.get("model.from"),
f = this;
FFZ.prototype._emoticonize = function(component, tokens) {
var room_id = component.get("msgObject.room") || App.__container__.lookup('controller:chat').get('currentRoom.id'),
user_id = component.get("msgObject.from");
// Get our sets.
var sets = this.getEmotes(user_id, room_id),
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) {
//var eo = {isEmoticon:true, cls: emote.klass};
var eo = {isEmoticon:true, cls: emote.klass,srcSet: emote.url + ' 1x', emoticonSrc: emote.url + '" data-ffz-emote="' + encodeURIComponent(JSON.stringify([emote.id, emote.set_id])), altText: (emote.hidden ? "???" : emote.name)};
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;
return this.tokenize_emotes(user_id, room_id, tokens);
}

View file

@ -51,6 +51,7 @@ FFZ.prototype.setup_room = function() {
var inst = instances[key];
this.add_room(inst.id, inst);
this._modify_room(inst);
inst.ffzPatchTMI();
}
}
@ -199,7 +200,7 @@ FFZ.prototype.add_room = function(id, room) {
this.ws_send("sub", id);
// For now, we use the legacy function to grab the .css file.
this._legacy_add_room(id);
this.load_room(id);
}
@ -219,14 +220,14 @@ FFZ.prototype.remove_room = function(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;
if ( id.charAt(0) === "_" )
return;
var set = this.emote_sets[room.set];
if ( set ) {
set.users.removeObject(id);
if ( !set.global && !set.users.length )
this.unload_set(set_id);
if ( ! this.global_sets.contains(room.set) && ! set.users.length )
this.unload_set(room.set);
}
}
@ -235,12 +236,36 @@ FFZ.prototype.remove_room = function(id) {
// Receiving Set Info
// --------------------
FFZ.prototype.load_room = function(room_id, callback) {
return this._legacy_load_room(room_id, callback);
FFZ.prototype.load_room = function(room_id, callback, tries) {
var f = this;
jQuery.getJSON(constants.API_SERVER + "v1/room/" + room_id)
.done(function(data) {
if ( data.sets ) {
for(var key in data.sets)
data.sets.hasOwnProperty(key) && f._load_set_json(key, undefined, data.sets[key]);
}
f._load_room_json(room_id, callback, data);
}).fail(function(data) {
if ( data.status == 404 )
return typeof callback == "function" && callback(false);
tries = (tries || 0) + 1;
if ( tries < 10 )
return f.load_room(room_id, callback, tries);
return typeof callback == "function" && callback(false);
});
}
FFZ.prototype._load_room_json = function(room_id, callback, data) {
if ( ! data || ! data.room )
return typeof callback == "function" && callback(false);
data = data.room;
// Preserve the pointer to the Room instance.
if ( this.rooms[room_id] )
data.room = this.rooms[room_id].room;
@ -250,11 +275,8 @@ FFZ.prototype._load_room_json = function(room_id, callback, 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);
}
if ( ! this.emote_sets.hasOwnProperty(data.set) )
this.load_set(data.set);
this.update_ui_link();
@ -275,6 +297,7 @@ FFZ.prototype._modify_room = function(room) {
this._super();
try {
f.add_room(this.id, this);
this.set("ffz_chatters", {});
} catch(err) {
f.error("add_room: " + err);
}
@ -289,20 +312,25 @@ FFZ.prototype._modify_room = function(room) {
}
},
getSuggestions: function() {
// This returns auto-complete suggestions for use in chat. We want
// to apply our capitalizations here. Overriding the
// filteredSuggestions property of the chat-input component would
// be even better, but I was already hooking the room model.
var suggestions = this._super();
addMessage: function(msg) {
try {
suggestions = _.map(suggestions, FFZ.get_capitalization);
if ( msg ) {
msg.room = this.get('id');
f.tokenize_chat_line(msg);
}
} catch(err) {
f.error("get_suggestions: " + err);
f.error("Room addMessage: " + err);
}
return suggestions;
return this._super(msg);
},
setHostMode: function(e) {
var Chat = App.__container__.lookup('controller:chat');
if ( ! Chat || Chat.get('currentChannelRoom') !== this )
return;
return this._super(e);
},
send: function(text) {
@ -323,54 +351,168 @@ FFZ.prototype._modify_room = function(room) {
}
return this._super(text);
}
},
ffzUpdateUnread: function() {
if ( f.settings.group_tabs ) {
var Chat = App.__container__.lookup('controller:chat');
if ( Chat && Chat.get('currentRoom') === this )
this.resetUnreadCount();
else if ( f._chatv )
f._chatv.ffzTabUnread(this.get('id'));
}
}.observes('unreadCount'),
ffzInitChatterCount: function() {
if ( ! this.tmiRoom )
return;
var room = this;
this.tmiRoom.list().done(function(data) {
var chatters = {};
data = data.data.chatters;
for(var i=0; i < data.admins.length; i++)
chatters[data.admins[i]] = true;
for(var i=0; i < data.global_mods.length; i++)
chatters[data.global_mods[i]] = true;
for(var i=0; i < data.moderators.length; i++)
chatters[data.moderators[i]] = true;
for(var i=0; i < data.staff.length; i++)
chatters[data.staff[i]] = true;
for(var i=0; i < data.viewers.length; i++)
chatters[data.viewers[i]] = true;
room.set("ffz_chatters", chatters);
room.ffzUpdateChatters();
});
},
ffzUpdateChatters: function(add, remove) {
var chatters = this.get("ffz_chatters") || {};
if ( add )
chatters[add] = true;
if ( remove && chatters[remove] )
delete chatters[remove];
if ( ! f.settings.chatter_count )
return;
if ( f._cindex )
f._cindex.ffzUpdateChatters();
if ( window.parent && window.parent.postMessage )
window.parent.postMessage({from_ffz: true, command: 'chatter_count', message: Object.keys(this.get('ffz_chatters') || {}).length}, "http://www.twitch.tv/");
},
ffzPatchTMI: function() {
if ( this.get('ffz_is_patched') || ! this.get('tmiRoom') )
return;
if ( f.settings.chatter_count )
this.ffzInitChatterCount();
var tmi = this.get('tmiRoom'),
room = this;
// This method is stupid and bad and it leaks between rooms.
if ( ! tmi.ffz_notice_patched ) {
tmi.ffz_notice_patched = true;
tmi._roomConn.off("notice", tmi._onNotice, tmi);
tmi._roomConn.on("notice", function(ircMsg) {
var target = ircMsg.target || (ircMsg.params && ircMsg.params[0]) || this.ircChannel;
if( target != this.ircChannel )
return;
this._trigger("notice", {
msgId: ircMsg.tags['msg-id'],
message: ircMsg.message
});
}, tmi);
}
// Let's get chatter information!
var connection = tmi._roomConn._connection;
if ( ! connection.ffz_cap_patched ) {
connection.ffz_cap_patched = true;
connection._send("CAP REQ :twitch.tv/membership");
connection.on("opened", function() {
this._send("CAP REQ :twitch.tv/membership");
}, connection);
// Since TMI starts sending SPECIALUSER with this, we need to
// ignore that. \ CatBag /
var orig_handle = connection._handleTmiPrivmsg.bind(connection);
connection._handleTmiPrivmsg = function(msg) {
if ( msg.message && msg.message.split(" ",1)[0] === "SPECIALUSER" )
return;
return orig_handle(msg);
}
}
// Check this shit.
tmi._roomConn._connection.off("message", tmi._roomConn._onIrcMessage, tmi._roomConn);
tmi._roomConn._onIrcMessage = function(ircMsg) {
if ( ircMsg.target != this.ircChannel )
return;
switch ( ircMsg.command ) {
case "JOIN":
if ( this._session && this._session.nickname === ircMsg.sender ) {
this._onIrcJoin(ircMsg);
} else
f.settings.chatter_count && room.ffzUpdateChatters(ircMsg.sender);
break;
case "PART":
if ( this._session && this._session.nickname === ircMsg.sender ) {
this._resetActiveState();
this._connection._exitedRoomConn();
this._trigger("exited");
} else
f.settings.chatter_count && room.ffzUpdateChatters(null, ircMsg.sender);
break;
default:
break;
}
}
tmi._roomConn._connection.on("message", tmi._roomConn._onIrcMessage, tmi._roomConn);
// Okay, we need to patch the *session's* updateUserState
if ( ! tmi.session.ffz_patched ) {
tmi.session.ffz_patched = true;
var uus = tmi.session._updateUserState.bind(tmi.session);
tmi.session._updateUserState = function(user, tags) {
try {
if ( tags.color )
this._onUserColorChanged(user, tags.color);
if ( tags['display-name'] )
this._onUserDisplayNameChanged(user, tags['display-name']);
if ( tags.turbo )
this._onUserSpecialAdded(user, 'turbo');
if ( tags['user_type'] === 'staff' || tags['user_type'] === 'admin' || tags['user_type'] === 'global_mod' )
this._onUserSpecialAdded(user, tags['user-type']);
} catch(err) {
f.error("SessionManager _updateUserState: " + err);
}
}
}
this.set('ffz_is_patched', true);
}.observes('tmiRoom')
});
}
// --------------------
// 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);
}