From 800553c6026c40b7721ee76313979f304d5bf415 Mon Sep 17 00:00:00 2001 From: SirStendec Date: Sat, 12 Dec 2015 13:28:35 -0500 Subject: [PATCH] 3.5.84 to 3.5.100. Most importantly, started using new Ember stuff. -view-registry instead of Ember.View.views, and such. Finally added UI for managing pinned channels. Use HTTPS for the API and socket servers. Don't immediately unload chat rooms. Smarter chat tab behavior. Added /card command for opening mod cards. Other stuff. --- dark.css | 12 +- src/badges.js | 25 +- src/colors.js | 6 +- src/commands.js | 41 ++ src/constants.js | 14 +- src/ember/channel.js | 11 +- src/ember/chat-input.js | 32 +- src/ember/chatview.js | 799 +++++++++++++++++++++++------------ src/ember/conversations.js | 6 +- src/ember/directory.js | 62 ++- src/ember/following.js | 7 +- src/ember/line.js | 2 +- src/ember/moderation-card.js | 192 +++++---- src/ember/player.js | 7 +- src/ember/room.js | 93 +++- src/ember/viewers.js | 22 +- src/emoticons.js | 4 +- src/ext/api.js | 22 +- src/ext/betterttv.js | 10 +- src/ext/rechat.js | 24 +- src/main.js | 10 +- src/socket.js | 2 +- src/tokenize.js | 22 +- src/ui/dark.js | 22 +- src/ui/group_chat.js | 47 --- src/ui/menu.js | 9 +- src/ui/styles.js | 2 +- style.css | 36 +- 28 files changed, 1016 insertions(+), 525 deletions(-) delete mode 100644 src/ui/group_chat.js diff --git a/dark.css b/dark.css index e189644b..309ca7cb 100644 --- a/dark.css +++ b/dark.css @@ -3,7 +3,7 @@ background-color:rgb(16,16,16)!important; } -.ffz-dark div#channel > .target-frame.active{ +.ffz-dark div#channel > .target-frame { background-color:rgb(16,16,16)!important; } @@ -22,6 +22,14 @@ border-top: 1px solid rgba(255, 255, 255, 0.05); } +.ffz-dark .offlineChannelStatus { + background-color: rgba(255,255,255, 0.05); +} + +.ffz-dark .close-hostmode a:before, +.ffz-dark .close-hostmode a:after { + border-bottom-color: rgba(255,255,255, 0.05); +} /* hidden chat */ @@ -248,7 +256,7 @@ .ffz-dark .manager .videos-grid .video:hover .meta .actions li a, .ffz-dark .ember-chat .chat-room-list .room:not(:hover) p.room-title, .ffz-dark .dropmenu_action:not(:hover) span, -.ffz-dark a:not(.filter-item):not(.ui-state-focus):not(.button):not(.switch):not(.follow):not(.fb_button) { +.ffz-dark a:not(.filter-item):not(.ui-state-focus):not(.button):not(.switch):not(.follow):not(.fb_button):not(.what) { color: #a68ed2; } diff --git a/src/badges.js b/src/badges.js index 0632e4bc..f0359d37 100644 --- a/src/badges.js +++ b/src/badges.js @@ -228,9 +228,9 @@ FFZ.prototype.get_line_badges = function(msg) { } } - if ( msg.labels.indexOf('subscriber') !== -1 ) + if ( msg.labels && msg.labels.indexOf('subscriber') !== -1 ) badges[10] = {klass: 'subscriber', title: 'Subscriber'} - if ( msg.labels.indexOf('turbo') !== -1 ) + if ( msg.labels && msg.labels.indexOf('turbo') !== -1 ) badges[15] = {klass: 'turbo', title: 'Turbo'}; // FFZ Badges @@ -338,7 +338,7 @@ FFZ.prototype.bttv_badges = function(data) { if ( b.type === full_badge.replaces_type ) { b.type = "ffz-badge-replacement " + b.type; b.description += ", " + (badge.title || full_badge.title) + - '" style="background-image: url("' + utils.quote_attr(badge.image || full_badge.image) + '")'; + '" style="background-image: url(' + utils.quote_attr('"' + (badge.image || full_badge.image) + '"') + ')'; replaced = true; break; } @@ -349,18 +349,18 @@ FFZ.prototype.bttv_badges = function(data) { } if ( alpha && badge.transparent_image ) - style += 'background-image: url("' + utils.quote_attr(badge.transparent_image) + '");'; + style += 'background-image: url("' + badge.transparent_image + '");'; else if ( badge.image ) - style += 'background-image: url("' + utils.quote_attr(badge.image) + '");'; + style += 'background-image: url("' + badge.image + '");'; if ( badge.color && ! alpha ) - style += 'background-color: ' + utils.quote_attr(badge.color) + '; '; + style += 'background-color: ' + badge.color + '; '; if ( badge.extra_css ) - style += utils.quote_attr(badge.extra_css); + style += badge.extra_css; if ( style ) - desc += '" style="' + style; + desc += '" style="' + utils.quote_attr(style); badges_out.push([(insert_at == -1 ? 1 : -1) * slot, {type: "ffz-badge-" + badge.id + (alpha ? " alpha" : ""), name: "", description: desc}]); } @@ -451,14 +451,17 @@ FFZ.prototype._legacy_load_donors = function(callback, tries) { FFZ.prototype._legacy_parse_badges = function(callback, data, slot, badge_id, title_template) { var title = this.badges[badge_id].title, - count = 0; + count = 0, ds = null; title_template = title_template || '{}'; if ( data != null ) { - var lines = data.trim().split(/\W+/); + var lines = data.trim().split(/[ \t\n\r]+/); for(var i=0; i < lines.length; i++) { + if ( ! /^\w/.test(lines[i]) ) + continue; + var line_data = lines[i].split(";"), user_id = line_data[0], user = this.users[user_id] = this.users[user_id] || {}, @@ -471,7 +474,7 @@ FFZ.prototype._legacy_parse_badges = function(callback, data, slot, badge_id, ti if ( badges[slot] ) continue; - badges[slot] = {id:badge_id}; + badges[slot] = {id: badge_id}; if ( line_data.length > 1 ) badges[slot].title = title_template.replace('{}', line_data[1]); count += 1; diff --git a/src/colors.js b/src/colors.js index 8b844339..3290db5c 100644 --- a/src/colors.js +++ b/src/colors.js @@ -129,9 +129,9 @@ FFZ.prototype.setup_colors = function() { Layout.addObserver("isTheatreMode", this._update_colors.bind(this, true)); if ( Settings ) - Settings.addObserver("model.darkMode", this._update_colors.bind(this, true)) + Settings.addObserver("settings.darkMode", this._update_colors.bind(this, true)) - this._color_old_darkness = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('model.darkMode')); + this._color_old_darkness = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('settings.darkMode')); } @@ -602,7 +602,7 @@ FFZ.prototype._update_colors = function(darkness_only) { var Layout = window.App && App.__container__.lookup('controller:layout'), Settings = window.App && App.__container__.lookup('controller:settings'), - is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('model.darkMode')); + is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('settings.darkMode')); if ( darkness_only && this._color_old_darkness === is_dark ) return; diff --git a/src/commands.js b/src/commands.js index ff279b11..811272ca 100644 --- a/src/commands.js +++ b/src/commands.js @@ -69,6 +69,47 @@ FFZ.ffz_commands.reload = function(room, args) { } +// ----------------- +// Moderation Cards +// ----------------- + +FFZ.chat_commands.card = function(room, args) { + if ( ! args || ! args.length || args.length > 1 ) + return "Usage: /card "; + + if ( ! this._roomv ) + return "An error occured. (We don't have the Room View.)"; + + // Get the position of the input box. + var el = this._roomv.get('element'), + ta = el && el.querySelector('textarea'), + bounds = ta && ta.getBoundingClientRect(), + + x = 0, y = 0, bottom, right; + + if ( ! bounds ) + bounds = el && el.getBoundingClientRect() || document.body.getBoundingClientRect(); + + if ( bounds ) { + if ( bounds.left > 400 ) { + right = bounds.left - 40; + bottom = bounds.top + bounds.height; + } else { + x = bounds.left - 20; + bottom = bounds.top - 20; + } + } + + this._roomv.get('controller').send('showModOverlay', { + top: y, + left: x, + bottom: bottom, + right: right, + sender: args[0] + }); +} + + // ----------------- // Mass Moderation // ----------------- diff --git a/src/constants.js b/src/constants.js index 15220f15..5ed52212 100644 --- a/src/constants.js +++ b/src/constants.js @@ -2,21 +2,21 @@ var SVGPATH = ' 1 ) { this.set('ffz_chatters', value); diff --git a/src/ember/chatview.js b/src/ember/chatview.js index 8359ffc4..cb354b63 100644 --- a/src/ember/chatview.js +++ b/src/ember/chatview.js @@ -258,24 +258,45 @@ FFZ.settings_info.chat_history = { }; FFZ.settings_info.group_tabs = { - type: "boolean", - value: false, + type: "select", + options: { + 0: "Disabled", + 1: "Rooms with Recent Activity", + 2: "Rooms with Recent Mentions", + 3: "All Rooms" + }, + + value: 0, + + process_value: function(val) { + if ( val === false ) + return 0; + else if ( val === true ) + return 3; + else if ( typeof val === "string" ) + return parseInt(val) || 0; + return val; + }, no_bttv: true, - category: "Chat Moderation", - name: "Chat Room Tabs Beta", - help: "Enhanced UI for switching the current chat room and noticing new messages.", + category: "Chat Appearance", + name: "Chat Room Tabs", + help: "Display tabs for chat rooms with recent activity at the top of the chat window for more convenient chatting.", on_update: function(val) { - var enabled = !this.has_bttv && val; - if ( ! this._chatv || enabled === this._group_tabs_state ) + if ( this.has_bttv || ! this._chatv ) return; - if ( enabled ) - this._chatv.ffzEnableTabs(); + if ( val ) + if ( this._chatv._ffz_tabs ) + this._chatv.ffzRebuildTabs(); + else + this._chatv.ffzEnableTabs(); else this._chatv.ffzDisableTabs(); + + this._chatv.ffzUpdateMenuUnread(); } }; @@ -307,7 +328,7 @@ FFZ.prototype.setup_chatview = function() { if ( Chat ) { Chat.reopen({ ffzUpdateChannels: function() { - if ( ! f._chatv ) + if ( ! f._chatv || f.has_bttv ) return; f._chatv.ffzRebuildMenu(); @@ -316,21 +337,72 @@ FFZ.prototype.setup_chatview = function() { }.observes("currentChannelRoom", "connectedPrivateGroupRooms"), + ffzUpdateInvites: function() { + if ( ! f._chatv || f.has_bttv ) + return; + + f._chatv.ffzUpdateMenuUnread(); + }.observes("invitedPrivateGroupRooms"), + + notificationsCount: function() { + if ( ! f._chatv || f.has_bttv ) + return this._super(); + + var total = this.get('invitedPrivateGroupRooms.length') || 0; + + if ( ! f._chatv._ffz_tabs ) + for(var room_id in f._chatv.ffz_unread) + if ( f._chatv.ffz_unread[room_id] ) + total++; + + return total; + }.property("currentRoom", "currentChannelRoom", "currentChannelRoom.unreadCount", "invitedPrivateGroupRooms.length", "connectedPrivateGroupRooms.@each.unreadCount"), + + _kickUserFromRoomNoLongerInList: function() { + // Remove an unread notice for any missing channels. + if ( f._chatv ) { + var updated = false; + for(var room_id in f._chatv.ffz_unread) + if ( f._chatv.ffz_unread[room_id] && (!f.rooms[room_id] || !f.rooms[room_id].room) ) { + f._chatv.ffz_unread[room_id] = false; + updated = true; + } + + if ( updated ) + f._chatv.ffzUpdateMenuUnread(); + } + + var room = this.get("currentRoom"), + room_id = room && room.get('id'), + channel_room = this.get("currentChannelRoom"), + is_group = room && _.contains(this.get("privateGroupRooms.content") || [], room); + + if ( room === channel_room || is_group || (f._chatv && f._chatv._ffz_host === room_id) || (f.settings.pinned_rooms && f.settings.pinned_rooms.indexOf(room_id) !== -1) ) + return; + + this.blurRoom(); + + if ( ! this.get("showList") ) + this.send("toggleMode"); + + }.observes("privateGroupRooms.@each"), + removeCurrentChannelRoom: function() { - if ( ! f.settings.group_tabs || f.has_bttv ) + if ( f.has_bttv ) return this._super(); var room = this.get("currentChannelRoom"), room_id = room && room.get('id'), user = f.get_user(); - if ( ! f.settings.pinned_rooms || f.settings.pinned_rooms.indexOf(room_id) === -1 ) { + // Don't clean up pinned rooms or the current host target. + if ( !((f._chatv && f._chatv._ffz_host === room_id) || (f.settings.pinned_rooms && f.settings.pinned_rooms.indexOf(room_id) !== -1)) ) { if ( room === this.get("currentRoom") ) this.blurRoom(); // Don't destroy it if it's the user's room. if ( room && user && user.login !== room_id ) - room.destroy(); + room.ffzScheduleDestroy(); } this.set("currentChannelRoom", void 0); @@ -351,11 +423,12 @@ FFZ.prototype.setup_chatview = function() { } catch(err) { } // Modify all existing Chat views. - for(var key in Ember.View.views) { - if ( ! Ember.View.views.hasOwnProperty(key) ) + var views = window.App && App.__container__.lookup('-view-registry:main') || Ember.View.views; + for(var key in views) { + if ( ! views.hasOwnProperty(key) ) continue; - var view = Ember.View.views[key]; + var view = views[key]; if ( !(view instanceof Chat) ) continue; @@ -417,10 +490,16 @@ FFZ.prototype._modify_cview = function(view) { this.$('.textarea-contain').append(f.build_ui_link(this)); this.$('.chat-messages').find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')}); - if ( !f.has_bttv && f.settings.group_tabs ) - this.ffzEnableTabs(); + this.ffz_unread = {}; - this.ffzRebuildMenu(); + if ( ! f.has_bttv ) { + if ( f.settings.group_tabs ) + this.ffzEnableTabs(); + + this.ffzRebuildMenu(); + } + + this.ffz_pruner = setInterval(this.ffzPruneTabs.bind(this), 10000); setTimeout(function() { if ( f.settings.group_tabs && f._chatv && f._chatv._ffz_tabs ) @@ -435,12 +514,45 @@ FFZ.prototype._modify_cview = function(view) { if ( f._chatv === this ) f._chatv = null; + if ( this.ffz_pruner ) { + clearInterval(this.ffz_pruner); + this.ffz_pruner = null; + } + this.$('.textarea-contain .ffz-ui-toggle').remove(); if ( f.settings.group_tabs ) this.ffzDisableTabs(); + + this.ffzTeardownMenu(); + this.ffzUnloadHost(); }, + + ffzPruneTabs: function() { + if ( ! this._ffz_tabs ) + return; + + var elements = this._ffz_tabs.querySelectorAll('.ffz-chat-tab:not(.hidden):not(.active)'), + update_height = false; + + for(var i=0; i < elements.length; i++) { + var el = elements[i], + room_id = el.getAttribute('data-room'), + was_hidden = el.classList.contains('hidden'), + is_hidden = ! this.ffzTabVisible(room_id); + + if ( was_hidden !== is_hidden ) { + el.classList.toggle('hidden', is_hidden); + update_height = true; + } + } + + if ( update_height ) + this.$('.chat-room').css('top', this._ffz_tabs.offsetHeight + "px"); + }, + + ffzChangeRoom: Ember.observer('controller.currentRoom', function() { f.update_ui_link(); @@ -448,67 +560,228 @@ FFZ.prototype._modify_cview = function(view) { if ( f._mod_card ) f._mod_card.send('close'); - var room = this.get('controller.currentRoom'), rows; - room && room.resetUnreadCount(); + var room = this.get('controller.currentRoom'), + room_id = room && room.get('id'), + was_unread = room_id && this.ffz_unread[room_id], + update_height = false; - if ( room && room._ffz_was_unread ) { - room._ffz_was_unread = false; - - var el = this.get('element'), - unread_display = el && el.querySelector('#ffz-group-tabs .button .notifications'), - unread_count = unread_display ? parseInt(unread_display.textContent) : 0; - - unread_count--; - if ( unread_display ) - unread_display.textContent = unread_count || ''; + if ( room ) { + room.resetUnreadCount(); + room.ffz_last_view = Date.now(); } - if ( this._ffz_chan_table ) { - rows = jQuery(this._ffz_chan_table); - rows.children('.ffz-room-row').removeClass('active'); - } - if ( this._ffz_group_table ) { - rows = jQuery(this._ffz_group_table); - rows.children('.ffz-room-row').removeClass('active'); - } + if ( room && room._ffz_tab ) { + var was_hidden = room._ffz_tab.classList.contains('hidden'), + is_hidden = ! this.ffzTabVisible(room_id); - if ( !f.has_bttv && f.settings.group_tabs && this._ffz_tabs ) { - var tabs = jQuery(this._ffz_tabs); - tabs.children('.ffz-chat-tab').removeClass('active'); - - if ( room && room._ffz_tab ) { - room._ffz_tab.classList.remove('tab-mentioned'); - room._ffz_tab.classList.remove('hidden'); - room._ffz_tab.classList.add('active'); - var sp = room._ffz_tab.querySelector('span'); - if ( sp ) - sp.innerHTML = ''; + if ( was_hidden !== is_hidden ) { + room._ffz_tab.classList.toggle('hidden', is_hidden); + update_height = true; } + } - if ( room && room._ffz_row ) { - room._ffz_row.classList.remove('row-mentioned'); - room._ffz_row.classList.remove('hidden'); - room._ffz_row.classList.add('active'); - var sp = room._ffz_row.querySelector('span'); - if ( sp ) - sp.innerHTML = ''; - } + if ( was_unread && room_id ) { + this.ffz_unread[room_id] = false; + this.ffzUpdateMenuUnread(); + } + + if ( this._ffz_chan_table ) + jQuery('.ffz-room-row.active', this._ffz_chan_table).removeClass('active'); + + if ( this._ffz_group_table ) + jQuery('.ffz-room-row.active', this._ffz_group_table).removeClass('active'); + + if ( this._ffz_tabs ) { + jQuery('.ffz-chat-tab.active', this._ffz_tabs).removeClass('active'); // 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')) + if ( this._ffz_invite ) + this._ffz_invite.classList.toggle('hidden', ! can_invite); - // Now, adjust the chat-room. - this.$('.chat-room').css('top', this._ffz_tabs.offsetHeight + "px"); + this.set('controller.showInviteUser', can_invite && this.get('controller.showInviteUser')); + update_height = true; } + + if ( room && room._ffz_tab ) { + room._ffz_tab.classList.remove('tab-mentioned'); + room._ffz_tab.classList.add('active'); + var sp = room._ffz_tab.querySelector('span'); + if ( sp ) + sp.innerHTML = ''; + } + + if ( room && room._ffz_row ) { + room._ffz_row.classList.remove('row-mentioned'); + room._ffz_row.classList.add('active'); + var sp = room._ffz_row.querySelector('span'); + if ( sp ) + sp.innerHTML = ''; + } + + if ( update_height ) + this.$('.chat-room').css('top', this._ffz_tabs.offsetHeight + "px"); }), - // Better Menu + + // Hosted Channel Chat + ffzUnloadHost: function() { + if ( ! this._ffz_host ) + return; + + if ( f.settings.pinned_rooms.indexOf(this._ffz_host) === -1 ) { + if ( this.get('controller.currentRoom') === this._ffz_host_room ) + this.get('controller').blurRoom(); + + // Schedule the room to be destroyed. This is after a short + // delay to make sure we aren't just loading the room in a + // new way. + this._ffz_host_room.ffzScheduleDestroy(); + } + + this._ffz_host = null; + this._ffz_host_room = null; + }, + + ffzUpdateHost: function() { + var Channel = App.__container__.lookup('controller:channel'), + Room = App.__container__.resolve('model:room'), + target = Room && Channel && Channel.get('hostModeTarget'), + + updated = false; + + if ( f.has_bttv ) + return; + + if ( target ) { + var target_id = target.get('id'); + if ( this._ffz_host !== target_id ) { + this.ffzUnloadHost(); + + this._ffz_host = target_id; + this._ffz_host_room = Room.findOne(target_id); + updated = true; + } + + } else if ( this._ffz_host ) { + this.ffzUnloadHost(); + updated = true; + } + + if ( updated ) { + this.ffzRebuildMenu(); + this.ffzRebuildTabs(); + } + }, + + + // Unread Handling + + ffzUpdateMenuUnread: function() { + var el = this.get('element'), + controller = this.get('controller'), + unread_display = el && el.querySelector('#ffz-group-tabs .button .notifications'); + + Ember.propertyDidChange(controller, 'notificationsCount'); + + if ( unread_display ) + unread_display.innerHTML = utils.format_unread(controller.get('notificationsCount')); + }, + + + ffzUpdateUnread: function(target_id) { + var current_id = this.get('controller.currentRoom.id'); + + if ( target_id === current_id ) + // We don't care about updates to the current room. + return; + + var to_update, + update_unread = false, + update_height = false; + + // If we DO have a room ID, only update that room. + if ( target_id ) + to_update = [target_id]; + else + to_update = Object.keys(f.rooms); + + for(var i=0; i < to_update.length; i++) { + var room_id = to_update[i], + room = f.rooms[room_id] && f.rooms[room_id].room, + row = room && room._ffz_row, + tab = room && room._ffz_tab, + + unread_count = room_id === current_id ? 0 : room.get('unreadCount'), + is_unread = unread_count > 0, + unread = utils.format_unread(unread_count); + + + if ( this.ffz_unread[room_id] !== is_unread ) { + this.ffz_unread[room_id] = is_unread; + update_unread = true; + } + + if ( row ) { + var sp = row.querySelector('span'); + if ( sp ) + sp.innerHTML = unread; + } + + if ( tab ) { + var was_hidden = tab.classList.contains('hidden'), + is_hidden = ! this.ffzTabVisible(room_id), + sp = tab.querySelector('span'); + + if ( was_hidden !== is_hidden ) { + tab.classList.toggle('hidden', is_hidden); + update_height = true; + } + + if ( sp ) + sp.innerHTML = unread; + } + } + + if ( update_height ) + this.$('.chat-room').css('top', this._ffz_tabs.offsetHeight + "px"); + + if ( update_unread ) + this.ffzUpdateMenuUnread(); + }, + + + // Menu Rendering + + ffzTeardownMenu: function() { + var el = this.get('element'), + room_list = el && el.querySelector('.chat-rooms .tse-content'), + + chan_table = room_list && room_list.querySelector('#ffz-channel-table'), + group_table = room_list && room_list.querySelector('#ffz-group-table'); + + if ( chan_table ) + chan_table.parentElement.removeChild(chan_table); + + if ( group_table ) + group_table.parentElement.removeChild(group_table); + + this._ffz_chan_table = null; + this._ffz_group_table = null; + + if ( room_list && room_list.classList.contains('ffz-room-list') ) { + room_list.classList.remove('ffz-room-list'); + jQuery('.ffz', room_list).removeClass('ffz'); + } + + for(var room_id in f.rooms) + if ( f.rooms[room_id] && f.rooms[room_id].room && f.rooms[room_id].room._ffz_row ) + f.rooms[room_id].room._ffz_row = null; + }, ffzRebuildMenu: function() { - /*var el = this.get('element'), + var el = this.get('element'), room_list = el && el.querySelector('.chat-rooms .tse-content'); if ( ! room_list ) @@ -530,40 +803,48 @@ FFZ.prototype._modify_cview = function(view) { // Channel Table - var t = this, + var view = this, chan_table = this._ffz_chan_table || room_list.querySelector('#ffz-channel-table tbody'); if ( ! chan_table ) { var tbl = document.createElement('table'); - tbl.setAttribute('cellspacing', 0); + tbl.setAttribute('cellspacing', '0'); tbl.id = 'ffz-channel-table'; tbl.className = 'ffz'; - tbl.innerHTML = 'ChannelsPin'; + tbl.innerHTML = 'ChannelsPin'; room_list.insertBefore(tbl, room_list.firstChild); - chan_table = this._ffz_chan_table = tbl.querySelector('tbody'); - } + jQuery('.ffz-row-switch', tbl).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'se')}); + + chan_table = this._ffz_chan_table = tbl.querySelector('tbody'); + + } else + chan_table.innerHTML = ''; - chan_table.innerHTML = ''; // Current Channel - var room = this.get('controller.currentChannelRoom'), row; + var room = this.get('controller.currentChannelRoom'), + room_id = room && room.get('id'), + row; + if ( room ) { - row = this.ffzBuildRow(this, room, true); + row = this.ffzBuildRow(room, true); row && chan_table.appendChild(row); } + // Host Target if ( this._ffz_host_room ) { - row = this.ffzBuildRow(this, this._ffz_host_room, false, true); + row = this.ffzBuildRow(this._ffz_host_room, false, true); row && chan_table.appendChild(row); } + // 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 ) { - row = this.ffzBuildRow(this, f.rooms[room_id].room); + var pinned_id = f.settings.pinned_rooms[i]; + if ( room_id !== pinned_id && this._ffz_host !== pinned_id && f.rooms[pinned_id] && f.rooms[pinned_id].room ) { + row = this.ffzBuildRow(f.rooms[pinned_id].room); row && chan_table.appendChild(row); } } @@ -573,7 +854,7 @@ FFZ.prototype._modify_cview = function(view) { var group_table = this._ffz_group_table || room_list.querySelector('#ffz-group-table tbody'); if ( ! group_table ) { var tbl = document.createElement('table'); - tbl.setAttribute('cellspacing', 0); + tbl.setAttribute('cellspacing', '0'); tbl.id = 'ffz-group-table'; tbl.className = 'ffz'; tbl.innerHTML = 'Group Chats'; @@ -582,12 +863,12 @@ FFZ.prototype._modify_cview = function(view) { room_list.insertBefore(tbl, before.nextSibling); group_table = this._ffz_group_table = tbl.querySelector('tbody'); - } - group_table.innerHTML = ''; + } else + group_table.innerHTML = ''; _.each(this.get('controller.connectedPrivateGroupRooms'), function(room) { - var row = t.ffzBuildRow(t, room); + var row = view.ffzBuildRow(room); row && group_table && group_table.appendChild(row); }); @@ -595,64 +876,67 @@ FFZ.prototype._modify_cview = function(view) { // Change Create Tooltip var create_btn = el.querySelector('.button.create'); if ( create_btn ) - create_btn.title = 'Create a Group Room';*/ + create_btn.title = 'Create a Group Room'; }, - /*ffzBuildRow: function(view, room, current_channel, host_channel) { - var row = document.createElement('tr'), + + ffzBuildRow: function(room, current_channel, host_channel) { + var view = this, + + row = document.createElement('tr'), icon = document.createElement('td'), name_el = document.createElement('td'), btn, toggle_pinned = document.createElement('td'), + room_id = room.get('id'), group = room.get('isGroupRoom'), - current = room === view.get('controller.currentRoom'), - unread = utils.format_unread(current ? 0 : room.get('unreadCount')), + active_channel = room === this.get('controller.currentRoom'), + unread = utils.format_unread(active_channel ? 0 : room.get('unreadCount')), - name = room.get('tmiRoom.displayName') || (group ? room.get('tmiRoom.name') : FFZ.get_capitalization(room.get('id'), function(name) { - f.log("Name for Row: " + name); - unread = utils.format_unread(current ? 0 : room.get('unreadCount')); + name = room.get('tmiRoom.displayName') || (group ? room.get('tmiRoom.name') : FFZ.get_capitalization(room_id, function(name) { + var active_channel = room === view.get('controller.currentRoom'); + unread = utils.format_unread(active_channel ? 0 : room.get('unreadCount')); name_el.innerHTML = utils.sanitize(name) + ' ' + unread + ''; })); - name_el.className = 'ffz-room'; - name_el.innerHTML = utils.sanitize(name) + ' ' + unread + ''; - if ( current_channel ) { - icon.innerHTML = constants.CAMERA; - icon.title = name_el.title = "Current Channel"; - icon.className = name_el.className = 'tooltip'; - } else if ( host_channel ) { - icon.innerHTML = constants.EYE; - icon.title = name_el.title = "Hosted Channel"; - icon.className = name_el.className = 'tooltip'; - } - - toggle_pinned.className = 'ffz-row-switch'; - - toggle_pinned.innerHTML = ''; - - row.setAttribute('data-room', room.get('id')); + row.setAttribute('data-room', room_id); row.className = 'ffz-room-row'; row.classList.toggle('current-channel', current_channel); row.classList.toggle('host-channel', host_channel); row.classList.toggle('group-chat', group); - row.classList.toggle('active', current); + row.classList.toggle('active', active_channel); + + if ( current_channel ) { + icon.innerHTML = constants.CAMERA; + row.title = "Current Channel"; + row.classList.add('tooltip'); + + } else if ( host_channel ) { + icon.innerHTML = constants.EYE; + row.title = "Hosted Channel"; + row.classList.add('tooltip'); + } + + name_el.className = 'ffz-room'; + name_el.innerHTML = utils.sanitize(name) + ' ' + unread + ''; row.appendChild(icon); row.appendChild(name_el); + toggle_pinned.className = 'ffz-row-switch'; + if ( ! group ) { - row.appendChild(toggle_pinned); + toggle_pinned.innerHTML = ''; btn = toggle_pinned.querySelector('a.switch'); btn.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation && e.stopPropagation(); - var room_id = room.get('id'), - is_pinned = f.settings.pinned_rooms.indexOf(room_id) !== -1; + var is_pinned = f.settings.pinned_rooms.indexOf(room_id) !== -1; if ( is_pinned ) f._leave_room(room_id); @@ -667,7 +951,8 @@ FFZ.prototype._modify_cview = function(view) { btn.innerHTML = constants.CLOSE; btn.title = 'Leave Group'; - name_el.appendChild(btn); + toggle_pinned.innerHTML = ''; + toggle_pinned.appendChild(btn); btn.addEventListener('click', function(e) { e.preventDefault(); @@ -680,6 +965,8 @@ FFZ.prototype._modify_cview = function(view) { }); } + row.appendChild(toggle_pinned); + row.addEventListener('click', function() { var controller = view.get('controller'); controller.focusRoom(room); @@ -689,12 +976,13 @@ FFZ.prototype._modify_cview = function(view) { room._ffz_row = row; return row; - },*/ + }, - // Group Tabs~! + + // Group Tabs ffzEnableTabs: function() { - if ( f.has_bttv || ! f.settings.group_tabs ) + if ( f.has_bttv || ! f.settings.group_tabs || this._ffz_tabs ) return; // Hide the existing chat UI. @@ -709,34 +997,41 @@ FFZ.prototype._modify_cview = function(view) { this.ffzRebuildTabs(); }, - ffzRebuildTabs: function() { - if ( f.has_bttv || ! f.settings.group_tabs ) - return; + ffzDisableTabs: function() { + if ( this._ffz_tabs ) { + this._ffz_tabs.parentElement.removeChild(this._ffz_tabs); + this._ffz_tabs = null; + this._ffz_invite = null; + for(var room_id in f.rooms) + if ( f.rooms[room_id] && f.rooms[room_id].room && f.rooms[room_id].room._ffz_tab ) + f.rooms[room_id].room._ffz_tab = null; + } + + // Show the old chat UI. + this.$('.chat-room').css('top', ''); + this.$(".chat-header").removeClass("hidden"); + }, + + + ffzRebuildTabs: function() { var tabs = this._ffz_tabs || this.get('element').querySelector('#ffz-group-tabs'); if ( ! tabs ) return; tabs.innerHTML = ""; + if ( f.has_bttv || ! f.settings.group_tabs ) + return; + var link = document.createElement('a'), view = this; - //total_unread = 0; - /*for(var room_id in f.rooms) { - var room = f.rooms[room_id] && f.rooms[room_id].room, - is_unread = room && room.get('unreadCount') > 0; - - if ( is_unread ) { - room._ffz_was_unread = true; - total_unread++; - } else if ( room ) - room._ffz_was_unread = false; - }*/ + // Chat Room Management Button link.className = 'button glyph-only tooltip'; link.title = "Chat Room Management"; - link.innerHTML = constants.ROOMS; // + '' + (total_unread || '') + ''; + link.innerHTML = constants.ROOMS + ''; link.addEventListener('click', function() { var controller = view.get('controller'); @@ -746,6 +1041,7 @@ FFZ.prototype._modify_cview = function(view) { tabs.appendChild(link); + // Invite Button link = document.createElement('a'), link.className = 'button glyph-only tooltip invite'; link.title = "Invite a User"; @@ -760,145 +1056,70 @@ FFZ.prototype._modify_cview = function(view) { view._ffz_invite = link; tabs.appendChild(link); - var room = this.get('controller.currentChannelRoom'), tab; + + // Current Room + var room = this.get('controller.currentChannelRoom'), + room_id = room && room.get('id'), + tab; + if ( room ) { - tab = this.ffzBuildTab(view, room, true); + tab = this.ffzBuildTab(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 ( f.settings.pinned_rooms.indexOf(this._ffz_host) === -1 && 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 ( f.settings.pinned_rooms.indexOf(this._ffz_host) === -1 && 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; - } + // Host Target if ( this._ffz_host_room ) { - tab = view.ffzBuildTab(view, this._ffz_host_room, false, true); + tab = view.ffzBuildTab(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); + var pinned_id = f.settings.pinned_rooms[i]; + if ( room_id !== pinned_id && this._ffz_host !== pinned_id && f.rooms[pinned_id] && f.rooms[pinned_id].room ) { + tab = view.ffzBuildTab(f.rooms[pinned_id].room, false, false); tab && tabs.appendChild(tab); } } + + // Group Chat _.each(this.get('controller.connectedPrivateGroupRooms'), function(room) { - var tab = view.ffzBuildTab(view, room); + var tab = view.ffzBuildTab(room); tab && tabs.appendChild(tab); }); - // Now, adjust the chat-room. + + // Adjust the height of the chat room to account for the height of the numerous tabs. this.$('.chat-room').css('top', tabs.offsetHeight + "px"); + this.ffzUpdateMenuUnread(); }, - ffzTabUnread: function(room_id) { - var current_id = this.get('controller.currentRoom.id'); + ffzBuildTab: function(room, current_channel, host_channel) { + var view = this, - if ( room_id ) { - var room = f.rooms && f.rooms[room_id] && f.rooms[room_id].room, - row = room && room._ffz_row, - tab = room && room._ffz_tab, - - unread_count = room_id === current_id ? 0 : room.get('unreadCount'), - is_unread = unread_count > 0, - unread = utils.format_unread(unread_count); - - if ( ! room._ffz_was_unread && is_unread ) { - room._ffz_was_unread = true; - - var el = this.get('element'), - unread_display = el && el.querySelector('#ffz-group-tabs .button .notifications'), - unread_count = unread_display ? parseInt(unread_display.textContent) : 0; - - unread_count++; - if ( unread_display ) - unread_display.textContent = unread_count || ''; - } - - if ( row ) - row.querySelector('span').innerHTML = unread; - - if ( tab ) - tab.querySelector('span').innerHTML = unread; - - return; - } - - for(var room_id in f.rooms) { - var room = f.rooms[room_id] && f.rooms[room_id].room, - row = room && room._ffz_row, - tab = room && room._ffz_tab, - - unread_count = room_id === current_id ? 0 : room.get('unreadCount'), - is_unread = unread_count > 0, - unread = utils.format_unread(unread_count); - - if ( ! room._ffz_was_unread && is_unread ) { - room._ffz_was_unread = true; - - var el = this.get('element'), - unread_display = el && el.querySelector('#ffz-group-tabs .button .notifications'), - unread_count = unread_display ? parseInt(unread_display.textContent) : 0; - - unread_count++; - if ( unread_display ) - unread_display.textContent = unread_count || ''; - } - - if ( row ) - row.querySelector('span').innerHTML = unread; - - if ( tab ) - tab.querySelector('span').innerHTML = unread; - } - }, - - ffzBuildTab: function(view, room, current_channel, host_channel) { - var tab = document.createElement('span'), name, unread, icon = '', + tab = document.createElement('span'), name, unread, icon = '', room_id = room.get('id'), group = room.get('isGroupRoom'), - current = room === view.get('controller.currentRoom'), - visible = current || f.settings.visible_rooms.indexOf(room_id) !== -1; + active_channel = room === this.get('controller.currentRoom'); - tab.setAttribute('data-room', room.id); + tab.setAttribute('data-room', room_id); tab.className = 'ffz-chat-tab tooltip'; - //tab.classList.toggle('hidden', ! visible); 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); + tab.classList.toggle('active', active_channel); - unread = utils.format_unread(current ? 0 : room.get('unreadCount')); + tab.classList.toggle('hidden', ! this.ffzTabVisible(room_id)); - name = room.get('tmiRoom.displayName') || (group ? room.get('tmiRoom.name') : FFZ.get_capitalization(room.get('id'), function(name) { - unread = utils.format_unread(current ? 0 : room.get('unreadCount')); + unread = utils.format_unread(active_channel ? 0 : room.get('unreadCount')); + + name = room.get('tmiRoom.displayName') || (group ? room.get('tmiRoom.name') : FFZ.get_capitalization(room_id, function(name) { + var active_channel = room === view.get('controller.currentRoom'); + unread = utils.format_unread(active_channel ? 0 : room.get('unreadCount')); tab.innerHTML = icon + utils.sanitize(name) + '' + unread + ''; })); @@ -925,28 +1146,31 @@ FFZ.prototype._modify_cview = function(view) { return tab; }, - ffzDisableTabs: function() { - if ( this._ffz_tabs ) { - this._ffz_tabs.parentElement.removeChild(this._ffz_tabs); - delete this._ffz_tabs; - delete this._ffz_invite; - } + ffzTabVisible: function(room_id) { + var room = f.rooms[room_id] && f.rooms[room_id].room, + is_current = room === this.get('controller.currentRoom'), + is_channel = room === this.get('controller.currentChannelRoom'), - if ( this._ffz_host ) { - if ( f.settings.pinned_rooms.indexOf(this._ffz_host) === -1 && this._ffz_host_room ) { - if ( this.get('controller.currentRoom') === this._ffz_host_room ) - this.get('controller').blurRoom(); - this._ffz_host_room.destroy(); - } + now = Date.now(); - delete this._ffz_host; - delete this._ffz_host_room; - } + if ( is_current || is_channel || room_id === this._ffz_host || f.settings.group_tabs === 3 ) + // Important Tabs + return true; - // Show the old chat UI. - this.$('.chat-room').css('top', ''); - this.$(".chat-header").removeClass("hidden"); - }, + else if ( now - room.ffz_last_view < 60000 || now - room.ffz_last_input < 2700000 ) + // Recent Self Input or View + return true; + + else if ( f.settings.group_tabs === 1 && now - (room.ffz_last_activity || 0) < 2700000 ) + // Any Recent Activity + return true; + + else if ( f.settings.group_tabs === 2 && now - (room.ffz_last_mention || 0) < 2700000 ) + // Recent Mentions + return true; + + return false; + } }); } @@ -960,11 +1184,12 @@ FFZ.prototype.connect_extra_chat = function() { if ( user && user.login ) { // Make sure we're in the user's room. if ( ! this.rooms[user.login] || this.rooms[user.login].room ) { - var Room = App.__container__.resolve('model:room'), - r = Room && Room.findOne(user.login); + var Room = App.__container__.resolve('model:room'); + Room && Room.findOne(user.login); } } + // We don't join extra rooms with BTTV. if ( this.has_bttv ) return; @@ -974,13 +1199,39 @@ FFZ.prototype.connect_extra_chat = function() { if ( ! this._chatv ) return; - if ( ! this.has_bttv && this.settings.group_tabs ) + + // Rebuild the chat UI. + if ( this.settings.group_tabs ) this._chatv.ffzRebuildTabs(); this._chatv.ffzRebuildMenu(); } +FFZ.prototype.disconnect_extra_chat = function() { + var Chat = App.__container__.lookup('controller:chat'), + current_channel_id = Chat && Chat.get('currentChannelRoom.id'), + current_id = Chat && Chat.get('currentRoom.id'), + user = this.get_user(); + + if ( ! Chat ) + return; + + for(var i=0; i < this.settings.pinned_rooms.length; i++) { + var room_id = this.settings.pinned_rooms[i]; + if ( room_id === current_channel_id || (user && room_id === user.login) ) + continue; + + if ( this.rooms[room_id] && this.rooms[room_id].room ) { + if ( current_id === room_id ) + Chat.blurRoom(); + + this.rooms[room_id].room.destroy(); + } + } +} + + FFZ.prototype._join_room = function(room_id, no_rebuild) { var did_join = false; if ( this.settings.pinned_rooms.indexOf(room_id) === -1 ) { @@ -989,23 +1240,22 @@ FFZ.prototype._join_room = function(room_id, no_rebuild) { did_join = true; } + // Make sure we're not already there. - if ( this.rooms[room_id] && this.rooms[room_id].room ) { - if ( did_join && ! no_rebuild && ! this.has_bttv && this._chatv && this.settings.group_tabs ) - this._chatv.ffzRebuildTabs(); - return did_join; + if ( ! this.rooms[room_id] || ! this.rooms[room_id].room ) { + // Okay, fine. Get it. + var Room = App.__container__.resolve('model:room'); + Room && Room.findOne(room_id); } - // 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(); + // Rebuild the chat UI. + if ( ! no_rebuild && ! this.has_bttv && this._chatv ) { + if ( this.settings.group_tabs ) + this._chatv.ffzRebuildTabs(); - if ( ! no_rebuild && this._chatv ) this._chatv.ffzRebuildMenu(); + } return did_join; } @@ -1036,11 +1286,14 @@ FFZ.prototype._leave_room = function(room_id, no_rebuild) { if ( ! user || user.login !== room_id ) r.destroy(); - if ( ! no_rebuild && ! this.has_bttv && this._chatv && this.settings.group_tabs ) - this._chatv.ffzRebuildTabs(); - if ( ! no_rebuild && this._chatv ) + // Rebuild the chat UI. + if ( ! no_rebuild && ! this.has_bttv && this._chatv ) { + if ( this.settings.group_tabs ) + this._chatv.ffzRebuildTabs(); + this._chatv.ffzRebuildMenu(); + } return did_leave; } @@ -1051,6 +1304,9 @@ FFZ.prototype._leave_room = function(room_id, no_rebuild) { // ---------------------- FFZ.chat_commands.join = function(room, args) { + if ( this.has_bttv ) + return "Pinned Rooms are not available with BetterTTV installed."; + if ( ! args || ! args.length || args.length > 1 ) return "Join Usage: /join "; @@ -1066,6 +1322,9 @@ FFZ.chat_commands.join = function(room, args) { FFZ.chat_commands.part = function(room, args) { + if ( this.has_bttv ) + return "Pinned Rooms are not available with BetterTTV installed."; + if ( ! args || ! args.length || args.length > 1 ) return "Part Usage: /part "; diff --git a/src/ember/conversations.js b/src/ember/conversations.js index 5345189d..52f3c243 100644 --- a/src/ember/conversations.js +++ b/src/ember/conversations.js @@ -58,8 +58,7 @@ FFZ.prototype.setup_conversations = function() { FFZ.prototype._modify_conversation_window = function(component) { var f = this, - Layout = App.__container__.lookup('controller:layout'), - Settings = App.__container__.lookup('controller:settings'); + Layout = App.__container__.lookup('controller:layout'); component.reopen({ headerBadges: Ember.computed("thread.participants", "currentUsername", function() { @@ -106,8 +105,7 @@ FFZ.prototype._modify_conversation_window = function(component) { FFZ.prototype._modify_conversation_line = function(component) { var f = this, - Layout = App.__container__.lookup('controller:layout'), - Settings = App.__container__.lookup('controller:settings'); + Layout = App.__container__.lookup('controller:layout'); component.reopen({ tokenizedMessage: function() { diff --git a/src/ember/directory.js b/src/ember/directory.js index f107ed32..dc3ed711 100644 --- a/src/ember/directory.js +++ b/src/ember/directory.js @@ -149,9 +149,11 @@ FFZ.prototype.setup_directory = function() { document.body.classList.toggle('ffz-creative-tags', this.settings.directory_creative_all_tags); document.body.classList.toggle('ffz-creative-showcase', this.settings.directory_creative_showcase); - this.log("Hooking the Ember games-following controller."); - var GamesFollowing = App.__container__.lookup('controller:games-following'); + var GamesFollowing = App.__container__.lookup('controller:games-following'), + f = this; + if ( GamesFollowing ) { + this.log("Hooking the Ember games-following controller."); GamesFollowing.reopen({ ffz_sidebar_games: this.settings.sidebar_followed_games, @@ -164,7 +166,8 @@ FFZ.prototype.setup_directory = function() { }); Ember.propertyDidChange(GamesFollowing, 'sidePanelFollowing'); - } + } else + this.error("Unable to load the Ember games-following controller."); this.log("Attempting to modify the Following collection."); @@ -189,8 +192,9 @@ FFZ.prototype.setup_directory = function() { this._modify_directory_host(HostView); // Initialize existing views. - for(var key in Ember.View.views) { - var view = Ember.View.views[key]; + var views = window.App && App.__container__.lookup('-view-registry:main') || Ember.View.views; + for(var key in views) { + var view = views[key]; try { if ( (ChannelView && view instanceof ChannelView) || (CreativeChannel && view instanceof CreativeChannel) || (CSGOChannel && view instanceof CSGOChannel) || (HostView && view instanceof HostView) ) view.ffzInit(); @@ -469,10 +473,20 @@ FFZ.prototype._modify_directory_host = function(dir) { for(var i=0; i < hosts.length; i++) make_link(hosts[i]); - f.show_popup(menu, [e.clientX - 60, e.clientY - 60], document.querySelector('#main_col > .tse-scroll-content > .tse-content')); + var cont = document.querySelector('#main_col > .tse-scroll-content > .tse-content'), + bounds = cont && cont.getBoundingClientRect(), + + x = e.clientX - 60, + y = e.clientY - 60; + + if ( bounds ) + x = Math.max(bounds.left, Math.min(x, (bounds.left + bounds.width) - 302)); + + f.show_popup(menu, [x, y], document.querySelector('#main_col > .tse-scroll-content > .tse-content')); }, ffzCleanup: function() { + var target = this.get('context.model.target.channel'); if ( f._popup && f._popup.classList.contains('ffz-channel-selector') && f._popup.getAttribute('data-channel') === target ) f.close_popup(); }, @@ -485,7 +499,41 @@ FFZ.prototype._modify_directory_host = function(dir) { title = meta && meta.querySelector('.title a'), target = this.get('context.model.target.channel'), - hosts = this.get('context.model.ffz_hosts'); + hosts = this.get('context.model.ffz_hosts'), + + boxart = thumb && thumb.querySelector('.boxart'); + + + // Fix the game not showing + if ( ! boxart && thumb && this.get('context.model.game') ) { + var img = document.createElement('img'), + game = this.get("context.model.game"), + c = this.get('controller'); + + boxart = document.createElement('a'); + boxart.className = 'boxart'; + boxart.href = this.get("context.model.gameUrl"); + boxart.setAttribute('original-title', game); + + boxart.addEventListener('click', function(e) { + e.preventDefault(); + jQuery('.tipsy').remove(); + + if ( game === "Counter-Strike: Global Offensive" ) + c.transitionTo('csgo.channels.index') + else if ( game === "Creative" ) + c.transitionTo('creative.channels.index'); + else + c.transitionTo('gameDirectory.index', encodeURIComponent(game)); + + return false; + }); + + img.src = this.get("context.model.gameBoxart"); + boxart.appendChild(img); + thumb.appendChild(boxart); + } + if ( f.settings.directory_logos ) { el.classList.add('ffz-directory-logo'); diff --git a/src/ember/following.js b/src/ember/following.js index a57b292a..321f2a76 100644 --- a/src/ember/following.js +++ b/src/ember/following.js @@ -71,7 +71,7 @@ FFZ.prototype.setup_profile_following = function() { ffzInit: function() { // Only process our own profile following page. var user = f.get_user(); - if ( ! f.settings.enhance_profile_following || ! user || ! user.login === this.get('context.id') ) + if ( ! f.settings.enhance_profile_following || ! user || user.login !== this.get('context.id') ) return; var el = this.get('element'), @@ -259,8 +259,9 @@ FFZ.prototype.setup_profile_following = function() { ProfileView.create().destroy(); } catch(err) { } - for(var key in Ember.View.views) { - var view = Ember.View.views[key]; + var views = window.App && App.__container__.lookup('-view-registry:main') || Ember.View.views; + for(var key in views) { + var view = views[key]; if ( ! view || !(view instanceof ProfileView) ) continue; diff --git a/src/ember/line.js b/src/ember/line.js index 0babc0aa..feba7407 100644 --- a/src/ember/line.js +++ b/src/ember/line.js @@ -671,7 +671,7 @@ FFZ.prototype._modify_line = function(component) { raw_color = this.get('msgObject.color'), colors = raw_color && f._handle_color(raw_color), - is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('model.darkMode')); + is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('settings.darkMode')); e.push('
'); diff --git a/src/ember/moderation-card.js b/src/ember/moderation-card.js index a66234b7..a5373bcc 100644 --- a/src/ember/moderation-card.js +++ b/src/ember/moderation-card.js @@ -399,7 +399,10 @@ FFZ.prototype.setup_mod_card = function() { Card.reopen({ ffzForceRedraw: function() { this.rerender(); - }.observes("cardInfo.isModeratorOrHigher", "cardInfo.user"), + if ( f.settings.mod_card_history ) + this.ffzRenderHistory(); + + }.observes("cardInfo.isModeratorOrHigher", "cardInfo.user.id"), ffzRebuildInfo: function() { var el = this.get('element'), @@ -730,74 +733,26 @@ FFZ.prototype.setup_mod_card = function() { // Message History - if ( f.settings.mod_card_history ) { - var Chat = App.__container__.lookup('controller:chat'), - room = Chat && Chat.get('currentRoom'), - delete_links = room && room.get('roomProperties.hide_chat_links'), - tmiSession = room.tmiSession || (window.TMI && TMI._sessions && TMI._sessions[0]), - room_id = room.get('id'), - user_id = controller.get('cardInfo.user.id'), - ffz_room = room && f.rooms && f.rooms[room_id], - user_history = ffz_room && ffz_room.user_history && ffz_room.user_history[user_id] || [], - - history = document.createElement('ul'); - - history.className = 'interface clearfix chat-history'; - - if ( user_history.length < 50 ) { - var before = (user_history.length > 0 ? user_history[0].date.getTime() : Date.now()) - (f._ws_server_offset || 0); - f.ws_send("user_history", [room_id, user_id, 50 - user_history.length], function(success, data) { - if ( ! success ) - return; - - f.parse_history(data, null, room_id, delete_links, tmiSession); - - var i = data.length, - was_at_top = history && history.scrollTop >= (history.scrollHeight - history.clientHeight), - first = true; - - while(i--) { - var msg = data[i]; - if ( ! msg ) - continue; - - msg.from_server = true; - - if ( ! msg.date || msg.date.getTime() >= before ) - continue; - - if ( first ) { - first = false; - history.insertBefore(f._build_mod_card_history({ - date: msg.date, - from: "jtv", - style: "admin", - cachedTokens: ["(Server History Above)"] - }), history.firstElementChild); - } - - history.insertBefore(f._build_mod_card_history(msg, t), history.firstElementChild); - } - - if ( was_at_top ) - setTimeout(function() { history.scrollTop = history.scrollHeight; }); - }); - } - - for(var i=0; i < user_history.length; i++) - history.appendChild(f._build_mod_card_history(user_history[i], t)); - - el.appendChild(history); - - // Lazy scroll-to-bottom - history.scrollTop = history.scrollHeight; - } + if ( f.settings.mod_card_history ) + this.ffzRenderHistory(); // Reposition the menu if it's off-screen. var el_bound = el.getBoundingClientRect(), - body_bound = document.body.getBoundingClientRect(); + body_bound = document.body.getBoundingClientRect(), - if ( el_bound.bottom > body_bound.bottom ) { + renderBottom = this.get('cardInfo.renderBottom'), + renderRight = this.get('cardInfo.renderRight'); + + if ( renderRight ) { + var offset = (el_bound.left + el_bound.width) - renderRight; + el.style.left = (el_bound.left - offset) + "px"; + } + + if ( renderBottom ) { + var offset = el_bound.bottom - renderBottom; + el.style.top = (el_bound.top - offset) + "px"; + + } else if ( el_bound.bottom > body_bound.bottom ) { var offset = el_bound.bottom - body_bound.bottom; if ( el_bound.top - offset > body_bound.top ) el.style.top = (el_bound.top - offset) + "px"; @@ -818,9 +773,78 @@ FFZ.prototype.setup_mod_card = function() { } }, + ffzRenderHistory: function() { + var t = this, + Chat = App.__container__.lookup('controller:chat'), + room = Chat && Chat.get('currentRoom'), + delete_links = room && room.get('roomProperties.hide_chat_links'), + tmiSession = room.tmiSession || (window.TMI && TMI._sessions && TMI._sessions[0]), + room_id = room.get('id'), + user_id = this.get('cardInfo.user.id'), + ffz_room = room && f.rooms && f.rooms[room_id], + user_history = ffz_room && ffz_room.user_history && ffz_room.user_history[user_id] || [], + el = this.get('element'), + + history = el && el.querySelector('.chat-history'); + + if ( ! history ) { + history = document.createElement('ul'); + history.className = 'interface clearfix chat-history'; + el.appendChild(history); + } else { + history.classList.remove('loading'); + history.innerHTML = ''; + } + + if ( user_history.length < 50 ) { + var before = (user_history.length > 0 ? user_history[0].date.getTime() : Date.now()) - (f._ws_server_offset || 0); + f.ws_send("user_history", [room_id, user_id, 50 - user_history.length], function(success, data) { + if ( ! success ) + return; + + f.parse_history(data, null, room_id, delete_links, tmiSession); + + var i = data.length, + was_at_top = history && history.scrollTop >= (history.scrollHeight - history.clientHeight), + first = true; + + while(i--) { + var msg = data[i]; + if ( ! msg ) + continue; + + msg.from_server = true; + + if ( ! msg.date || msg.date.getTime() >= before ) + continue; + + if ( first ) { + first = false; + history.insertBefore(f._build_mod_card_history({ + date: msg.date, + from: "jtv", + style: "admin", + cachedTokens: ["(Server History Above)"] + }), history.firstElementChild); + } + + history.insertBefore(f._build_mod_card_history(msg, t), history.firstElementChild); + } + + if ( was_at_top ) + setTimeout(function() { history.scrollTop = history.scrollHeight; }); + }); + } + + for(var i=0; i < user_history.length; i++) + history.appendChild(f._build_mod_card_history(user_history[i], t)); + + // Lazy scroll-to-bottom + history.scrollTop = history.scrollHeight; + }, + ffzAdjacentHistory: function(line) { var Chat = App.__container__.lookup('controller:chat'), - controller = this.get('controller'), t = this, user_id = this.get('cardInfo.user.id'), @@ -835,23 +859,31 @@ FFZ.prototype.setup_mod_card = function() { history = el && el.querySelector('.chat-history'), logs = el && el.querySelector('.chat-history.adjacent-history'), - when = line.date.getTime(); + when = line.date.getTime(), + scroll_top = logs && logs.scrollTop || history && history.scrollTop || 0; if ( ! history ) return; - if ( logs ) + if ( logs ) { logs.classList.add('loading'); - else + logs.scrollTop = 0; + } else { history.classList.add('loading'); + history.scrollTop = 0; + } if ( ! f.ws_send("adjacent_history", [room_id, when, 2], function(success, data) { - if ( logs ) + var was_loading = history.classList.contains('loading'); + if ( logs ) { logs.classList.remove('loading'); - else + logs.scrollTop = scroll_top; + } else { history.classList.remove('loading'); + history.scrollTop = scroll_top; + } - if ( ! success || ! data || ! data.length ) + if ( ! success || ! data || ! data.length || ! was_loading ) return; var had_logs = false, @@ -908,10 +940,13 @@ FFZ.prototype.setup_mod_card = function() { }); }) ) - if ( logs ) + if ( logs ) { logs.classList.remove('loading'); - else + logs.scrollTop = scroll_top; + } else { history.classList.remove('loading'); + history.scrollTop = scroll_top; + } } }); } @@ -928,6 +963,9 @@ FFZ.prototype._build_mod_card_history = function(msg, modcard, show_from) { out.push('' + helpers.getTime(msg.date) + ''); + var alias = this.aliases[msg.from], + name = (msg.tags && msg.tags['display-name']) || (msg.from && msg.from.capitalize()) || "unknown user"; + if ( show_from ) { // Badges out.push(''); @@ -942,13 +980,11 @@ FFZ.prototype._build_mod_card_history = function(msg, modcard, show_from) { Layout = App.__container__.lookup('controller:layout'), Settings = App.__container__.lookup('controller:settings'), - is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('model.darkMode')); + is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('settings.darkMode')); // Aliases and Styling - var alias = this.aliases[msg.from], - name = (msg.tags && msg.tags['display-name']) || (msg.from && msg.from.capitalize()) || "unknown user", - style = colors && 'color:' + (is_dark ? colors[1] : colors[0]), + var style = colors && 'color:' + (is_dark ? colors[1] : colors[0]), colored = style ? ' has-color' : ''; diff --git a/src/ember/player.js b/src/ember/player.js index 245dadaf..cd3b5823 100644 --- a/src/ember/player.js +++ b/src/ember/player.js @@ -72,11 +72,12 @@ FFZ.prototype.setup_player = function() { if ( ! window.Ember ) return; - for(var key in Ember.View.views) { - if ( ! Ember.View.views.hasOwnProperty(key) ) + var views = window.App && App.__container__.lookup('-view-registry:main') || Ember.View.views; + for(var key in views) { + if ( ! views.hasOwnProperty(key) ) continue; - var view = Ember.View.views[key]; + var view = views[key]; if ( !(view instanceof Player2) ) continue; diff --git a/src/ember/room.js b/src/ember/room.js index 92986ffc..0aa6ba41 100644 --- a/src/ember/room.js +++ b/src/ember/room.js @@ -1,7 +1,4 @@ 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+$/, HOSTED_SUB = / subscribed to /, constants = require('../constants'), utils = require('../utils'), @@ -58,6 +55,35 @@ FFZ.prototype.setup_room = function() { this.get("model.tmiRoom").sendMessage("/timeout " + e.user + " 1"); this.get("model").clearMessages(e.user); } + + RC._actions.showModOverlay = function(e) { + var Channel = App.__container__.resolve('model:channel'); + if ( ! Channel ) + return; + + var chan = Channel.find({id: e.sender}); + + // Don't try loading the channel if it's already loaded. Don't make mod cards + // refresh the channel page when you click the broadcaster, basically. + if ( ! chan.get('isLoaded') ) + chan.load(); + + this.set("showModerationCard", true); + + // We pass in renderBottom and renderRight, which we use to reposition the window + // after we know how big it actually is. + this.set("moderationCardInfo", { + user: chan, + renderTop: e.top, + renderLeft: e.left, + renderBottom: e.bottom, + renderRight: e.right, + isIgnored: this.get("tmiSession").isIgnored(e.sender), + isChannelOwner: this.get("controllers.login.userData.login") === e.sender, + profileHref: Twitch.uri.profile(e.sender), + isModeratorOrHigher: this.get("model.isModeratorOrHigher") + }); + } } this.log("Hooking the Ember Room model."); @@ -90,11 +116,12 @@ FFZ.prototype.setup_room = function() { } catch(err) { } // Modify all existing Room views. - for(var key in Ember.View.views) { - if ( ! Ember.View.views.hasOwnProperty(key) ) + var views = window.App && App.__container__.lookup('-view-registry:main') || Ember.View.views; + for(var key in views) { + if ( ! views.hasOwnProperty(key) ) continue; - var view = Ember.View.views[key]; + var view = views[key]; if ( !(view instanceof RoomView) ) continue; @@ -834,7 +861,7 @@ FFZ.prototype._insert_history = function(room_id, data, from_server) { FFZ.prototype.load_room = function(room_id, callback, tries) { var f = this; - jQuery.getJSON(((tries||0)%2 === 0 ? constants.API_SERVER : constants.API_SERVER_2) + "v1/room/" + room_id) + jQuery.getJSON(constants.API_SERVER + "v1/room/" + room_id) .done(function(data) { if ( data.sets ) { for(var key in data.sets) @@ -935,6 +962,28 @@ FFZ.prototype._modify_room = function(room) { } }, + ffzScheduleDestroy: function() { + if ( this._ffz_destroy_timer ) + return; + + var t = this; + this._ffz_destroy_timer = setTimeout(function() { + t._ffz_destroy_timer = null; + t.ffzCheckDestroy(); + }, 5000); + }, + + ffzCheckDestroy: function() { + var Chat = App.__container__.lookup('controller:chat'), + user = f.get_user(), + room_id = this.get('id'); + + if ( (Chat && Chat.get('currentChannelRoom') === this) || (user && user.login === room_id) || (f._chatv && f._chatv._ffz_host === room_id) || (f.settings.pinned_rooms && f.settings.pinned_rooms.indexOf(room_id) !== -1) ) + return; + + this.destroy(); + }, + ffzUpdateStatus: function() { if ( f._roomv ) f._roomv.ffzUpdateStatus(); @@ -1089,7 +1138,14 @@ FFZ.prototype._modify_room = function(room) { this.get("messages").pushObject(msg); this.trimMessages(); - "admin" === msg.style || ("whisper" === msg.style && ! this.ffz_whisper_room ) || this.incrementProperty("unreadCount", 1); + if ( msg.style !== "admin" && msg.style !== "whisper" ) { + if ( msg.ffz_has_mention ) { + this.ffz_last_mention = Date.now(); + } + + this.ffz_last_activity = Date.now(); + this.incrementProperty("unreadCount", 1); + } } }, @@ -1102,9 +1158,6 @@ FFZ.prototype._modify_room = function(room) { if ( this._ffz_pending_flush ) return; - /*if ( this._ffz_pending_flush ) - clearTimeout(this._ffz_pending_flush);*/ - if ( this.ffzPending && this.ffzPending.length ) { // We need either the amount of chat delay past the first message, if chat_delay is on, or the // amount of time from the last batch. @@ -1258,10 +1311,9 @@ FFZ.prototype._modify_room = function(room) { }, send: function(text, ignore_history) { - if ( f.settings.group_tabs && f.settings.whisper_room && this.ffz_whisper_room ) - return; - try { + this.ffz_last_input = Date.now(); + if ( text && ! ignore_history ) { // Command History var mru = this.get('mru_list'), @@ -1294,16 +1346,13 @@ FFZ.prototype._modify_room = function(room) { }, 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')); - } + var Chat = App.__container__.lookup('controller:chat'); + if ( Chat && Chat.get('currentRoom') === this ) + this.resetUnreadCount(); + else if ( f._chatv ) + f._chatv.ffzUpdateUnread(this.get('id')); }.observes('unreadCount'), - ffzInitChatterCount: function() { if ( ! this.tmiRoom ) return; diff --git a/src/ember/viewers.js b/src/ember/viewers.js index 2aab88c0..b34283dc 100644 --- a/src/ember/viewers.js +++ b/src/ember/viewers.js @@ -21,12 +21,28 @@ FFZ.settings_info.sort_viewers = { FFZ.prototype.setup_viewers = function() { this.log("Hooking the Ember Viewers controller."); - var Viewers = App.__container__.resolve('controller:viewers'); - this._modify_viewers(Viewers); + if ( Viewers ) + this._modify_viewers(Viewers); + + /* Disable for now because Twitch reverted this change + this.log("Hooking the Ember Viewers view."); + var ViewerView = App.__container__.resolve('view:viewers'); + if ( ViewerView ) + this._modify_viewer_view(ViewerView);*/ } +/*FFZ.prototype._modify_viewer_view = function(view) { + view.reopen({ + setListDimensions: function(e) { + // Don't set the stupid scroll thing. Don't use the stupid height thing. + this.$(".js-chatters-container").width(e.width).height(e.height); + } + }); +}*/ + + FFZ.prototype._modify_viewers = function(controller) { var f = this; @@ -55,7 +71,7 @@ FFZ.prototype._modify_viewers = function(controller) { // If the current room isn't the channel's chat, then we shouldn't // display them as the broadcaster. - if ( room_id != broadcaster ) + if ( room_id !== broadcaster ) broadcaster = null; // Now, break the viewer array down into something we can use. diff --git a/src/emoticons.js b/src/emoticons.js index 34bcea8a..ea19e83e 100644 --- a/src/emoticons.js +++ b/src/emoticons.js @@ -372,7 +372,7 @@ FFZ.prototype.load_emoji_data = function(callback, tries) { FFZ.prototype.load_global_sets = function(callback, tries) { var f = this; - jQuery.getJSON(((tries||0)%2 === 0 ? constants.API_SERVER : constants.API_SERVER_2) + "v1/set/global") + jQuery.getJSON(constants.API_SERVER + "v1/set/global") .done(function(data) { f.default_sets = data.default_sets; var gs = f.global_sets = [], @@ -409,7 +409,7 @@ FFZ.prototype.load_global_sets = function(callback, tries) { FFZ.prototype.load_set = function(set_id, callback, tries) { var f = this; - jQuery.getJSON(((tries||0)%2 === 0 ? constants.API_SERVER : constants.API_SERVER_2) + "v1/set/" + set_id) + jQuery.getJSON(constants.API_SERVER + "v1/set/" + set_id) .done(function(data) { f._load_set_json(set_id, callback, data && data.set); diff --git a/src/ext/api.js b/src/ext/api.js index 83cfb267..28280973 100644 --- a/src/ext/api.js +++ b/src/ext/api.js @@ -86,14 +86,14 @@ API.prototype.log = function(msg, data, to_json, log_json) { API.prototype._load_set = function(real_id, set_id, data) { if ( ! data ) - return false; + return null; // Check for an existing set to copy the users. var users = []; if ( this.emote_sets[real_id] && this.emote_sets[real_id].users ) users = this.emote_sets[real_id].users; - var set = { + var emote_set = { source: this.name, source_ext: this.id, source_id: set_id, @@ -108,14 +108,14 @@ API.prototype._load_set = function(real_id, set_id, data) { title: data.title || "Global Emoticons", }; - this.emote_sets[real_id] = set; + this.emote_sets[real_id] = emote_set; if ( this.ffz.emote_sets ) - this.ffz.emote_sets[real_id] = set; + this.ffz.emote_sets[real_id] = emote_set; var output_css = "", ems = data.emoticons, - emoticons = set.emoticons; + emoticons = emote_set.emoticons; for(var i=0; i < ems.length; i++) { var emote = ems[i], @@ -158,15 +158,17 @@ API.prototype._load_set = function(real_id, set_id, data) { new_emote.regex = emote.regex; else if ( typeof emote.name !== "string" ) new_emote.regex = emote.name; + else if ( emote_set.require_spaces || emote.require_spaces ) + new_emote.regex = new RegExp("(^| )(" + utils.escape_regex(emote.name) + ")(?= |$)", "g"); else new_emote.regex = new RegExp("(^|\\W|\\b)(" + utils.escape_regex(emote.name) + ")(?=\\W|$)", "g"); output_css += build_css(new_emote); - set.count++; + emote_set.count++; emoticons[id] = new_emote; } - utils.update_css(this.ffz._emote_style, real_id, output_css + (set.css || "")); + utils.update_css(this.ffz._emote_style, real_id, output_css + (emote_set.css || "")); if ( this.ffz._cindex ) this.ffz._cindex.ffzFixTitle(); @@ -175,7 +177,7 @@ API.prototype._load_set = function(real_id, set_id, data) { this.ffz.update_ui_link(); } catch(err) { } - return set; + return emote_set; } @@ -223,7 +225,7 @@ API.prototype.unload_set = function(id) { if ( ! room ) continue; - ind = room.ext_sets.indexOf(exact_id); + var ind = room.ext_sets.indexOf(exact_id); if ( ind !== -1 ) room.ext_sets.splice(ind,1); } @@ -232,7 +234,7 @@ API.prototype.unload_set = function(id) { } - return set; + return emote_set; } diff --git a/src/ext/betterttv.js b/src/ext/betterttv.js index 8a421127..1effcf1c 100644 --- a/src/ext/betterttv.js +++ b/src/ext/betterttv.js @@ -43,10 +43,16 @@ FFZ.prototype.setup_bttv = function(delay) { } // Disable Chat Tabs - if ( this.settings.group_tabs && this._chatv ) { - this._chatv.ffzDisableTabs(); + if ( this._chatv ) { + if ( this.settings.group_tabs ) + this._chatv.ffzDisableTabs(); + + this._chatv.ffzTeardownMenu(); + this._chatv.ffzUnloadHost(); } + this.disconnect_extra_chat(); + if ( this._roomv ) { // Disable Chat Pause if ( this.settings.chat_hover_pause ) diff --git a/src/ext/rechat.js b/src/ext/rechat.js index e06d006c..2bc50944 100644 --- a/src/ext/rechat.js +++ b/src/ext/rechat.js @@ -47,6 +47,26 @@ FFZ.prototype.setup_rechat = function() { FFZ.prototype.find_rechat = function() { var el = !this.has_bttv ? document.querySelector('.rechat-chat-line') : null; + if ( ! this._rechat_listening && ! el ) { + // Try darkening a chat container. We don't have chat. + var container = document.querySelector('.chat-container'), + header = container && container.querySelector('.chat-header'); + + if ( header && header.textContent.indexOf('ReChat') !== -1 ) { + // Look-up dark mode. + var dark_chat = this.settings.dark_twitch; + if ( ! dark_chat ) { + var model = window.App ? App.__container__.lookup('controller:settings').get('model') : undefined; + dark_chat = model && model.get('darkMode'); + } + + container.classList.toggle('dark', dark_chat); + jQuery(container).find('.chat-lines').addClass('ffz-scrollbar'); + } + + return; + } + // If there's no change, don't continue. if ( !!el === this._rechat_listening ) return; @@ -128,7 +148,7 @@ FFZ.prototype.process_rechat_line = function(line, reprocess) { Layout = App.__container__.lookup('controller:layout'), Settings = App.__container__.lookup('controller:settings'), - is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('model.darkMode')), + is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('settings.darkMode')), badges_el = line.querySelector('.badges'), from_el = line.querySelector('.from'), @@ -196,7 +216,7 @@ FFZ.prototype.process_rechat_line = function(line, reprocess) { if ( ! message_el ) return; - if ( ! reprocess && message_el.style.color ) { + if ( ! reprocess && colors && message_el.style.color ) { message_el.classList.add('has-color'); message_el.style.color = is_dark ? colors[1] : colors[0]; } diff --git a/src/main.js b/src/main.js index 8377a227..60999bed 100644 --- a/src/main.js +++ b/src/main.js @@ -22,7 +22,7 @@ FFZ.get = function() { return FFZ.instance; } // Version var VER = FFZ.version_info = { - major: 3, minor: 5, revision: 83, + major: 3, minor: 5, revision: 100, toString: function() { return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || ""); } @@ -333,10 +333,18 @@ FFZ.prototype.init_ember = function(delay) { this.users = {}; this.is_dashboard = false; + try { this.embed_in_dash = window.top !== window && /\/[^\/]+\/dashboard/.test(window.top.location.pathname) && !/bookmarks$/.test(window.top.location.pathname); } catch(err) { this.embed_in_dash = false; } + + // Make an alias so they STOP RENAMING THIS ON ME + var Settings = App.__container__.lookup('controller:settings'); + if ( Settings && Settings.get('settings') === undefined ) + Settings.reopen({settings: Ember.computed.alias('model')}); + + // Initialize all the modules. this.load_settings(); diff --git a/src/socket.js b/src/socket.js index e3266130..26cb2079 100644 --- a/src/socket.js +++ b/src/socket.js @@ -357,7 +357,7 @@ FFZ.prototype.ws_ping = function() { return; this._ws_ping_time = window.performance ? performance.now() : Date.now(); - if ( ! this.ws_send("ping", null, this._ws_on_pong.bind(this)) ) + if ( ! this.ws_send("ping", undefined, this._ws_on_pong.bind(this)) ) this._ws_ping_time = null; } diff --git a/src/tokenize.js b/src/tokenize.js index bd7866ea..b45acebb 100644 --- a/src/tokenize.js +++ b/src/tokenize.js @@ -449,11 +449,21 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del // We have a mention! msgObject.ffz_has_mention = true; - // If we have chat tabs, update the status. - if ( room_id && ! this.has_bttv && this.settings.group_tabs && this._chatv && this._chatv._ffz_tabs ) { - var el = this._chatv._ffz_tabs.querySelector('.ffz-chat-tab[data-room="' + room_id + '"]'); - if ( el && ! el.classList.contains('active') ) - el.classList.add('tab-mentioned'); + // If we have chat tabs/rows, update the status. + if ( room_id && ! this.has_bttv && this._chatv ) { + var room = this.rooms[room_id] && this.rooms[room_id].room; + if ( room._ffz_tab && ! room._ffz_tab.classList.contains('active') ) { + room._ffz_tab.classList.add('tab-mentioned'); + var was_hidden = room._ffz_tab.classList.contains('hidden'); + + if ( was_hidden ) { + room._ffz_tab.classList.remove('hidden'); + this._chatv.$('.chat-room').css('top', this._chatv._ffz_tabs.offsetHeight + "px"); + } + } + + if ( room._ffz_row && ! room._ffz_row.classList.contains('active') ) + room._ffz_row.classList.add('row-mentioned'); } // Display notifications if that setting is enabled. Also make sure @@ -601,7 +611,7 @@ FFZ.prototype.render_tokens = function(tokens, render_links) { } //var mirror_url = utils.quote_attr(constants.EMOTE_MIRROR_BASE + id + '.png'); - extra = ' data-emote="' + id + '"'; // onerror="FrankerFaceZ._emote_mirror_swap(this)"'; // Disable error checking for now. + extra = ' data-emote="' + id + '" onerror="FrankerFaceZ._emote_mirror_swap(this)"'; // Disable error checking for now. if ( ! constants.EMOTE_REPLACEMENTS[id] ) srcset = utils.build_srcset(id); diff --git a/src/ui/dark.js b/src/ui/dark.js index 8992999a..9d4dcf1f 100644 --- a/src/ui/dark.js +++ b/src/ui/dark.js @@ -136,14 +136,15 @@ FFZ.settings_info.dark_twitch = { document.body.classList.toggle("ffz-dark", val); - var model = window.App ? App.__container__.lookup('controller:settings').get('model') : undefined; + var Settings = window.App && App.__container__.lookup('controller:settings'), + settings = Settings.get('settings'); if ( val ) { this._load_dark_css(); - model && this.settings.set('twitch_chat_dark', model.get('darkMode')); - model && model.set('darkMode', true); + settings && this.settings.set('twitch_chat_dark', settings.get('darkMode')); + settings && settings.set('darkMode', true); } else - model && model.set('darkMode', this.settings.twitch_chat_dark); + settings && settings.set('darkMode', this.settings.twitch_chat_dark); // Try coloring ReChat jQuery('.rechat-chat-line').parents('.chat-container').toggleClass('dark', val || this.settings.twitch_chat_dark); @@ -199,7 +200,16 @@ FFZ.prototype.setup_dark = function() { if ( ! this.settings.dark_twitch ) return; - window.App && App.__container__.lookup('controller:settings').set('model.darkMode', true); + var Settings = window.App && App.__container__.lookup('controller:settings'); + if ( Settings ) { + try { + Settings.set('settings.darkMode', true); + } catch(err) { + this.error("Unable to set the darkMode setting because it isn't named what we expect. WTF?"); + } + } else + this.error("Unable to load the Ember settings controller."); + this._load_dark_css(); } @@ -214,6 +224,6 @@ FFZ.prototype._load_dark_css = function() { s.id = "ffz-dark-css"; s.setAttribute('rel', 'stylesheet'); - s.setAttribute('href', constants.DIRECT_SERVER + "script/dark" + (constants.DEBUG ? "" : ".min") + ".css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info)); + s.setAttribute('href', constants.SERVER + "script/dark" + (constants.DEBUG ? "" : ".min") + ".css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info)); document.head.appendChild(s); } \ No newline at end of file diff --git a/src/ui/group_chat.js b/src/ui/group_chat.js deleted file mode 100644 index 269fb491..00000000 --- a/src/ui/group_chat.js +++ /dev/null @@ -1,47 +0,0 @@ -var FFZ = window.FrankerFaceZ, - constants = require('../constants'); - - -// -------------------- -// Configuration -// -------------------- - -FFZ.settings_info.group_tabs = { - type: "boolean", - value: false, - - no_bttv: true, - - category: "Chat", - name: "Chat Room Tabs Beta", - 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(); - } -} - - -// -------------------- -// Initializer -// -------------------- - -FFZ.prototype.setup_group_chat = function() { - if ( this.has_bttv || ! this.settings.group_tabs ) - return; - - this.log("Initializing secondary group chat UI."); - //this.group_tabs_enable(); -} - - -// -------------------- -// -// -------------------- \ No newline at end of file diff --git a/src/ui/menu.js b/src/ui/menu.js index ce29afdd..ef2a6dcc 100644 --- a/src/ui/menu.js +++ b/src/ui/menu.js @@ -126,7 +126,7 @@ FFZ.prototype.setup_menu = function() { content.appendChild(p); a.addEventListener('click', function(e) { - view.set('controller.model.hidden', true); + view.set('controller.settings.hidden', true); f._last_page = 'settings'; f.build_ui_popup(f._chatv); e.stopPropagation(); @@ -164,11 +164,12 @@ FFZ.prototype.setup_menu = function() { } catch(err) { } // Modify all existing Chat Settings views. - for(var key in Ember.View.views) { - if ( ! Ember.View.views.hasOwnProperty(key) ) + var views = window.App && App.__container__.lookup('-view-registry:main') || Ember.View.views; + for(var key in views) { + if ( ! views.hasOwnProperty(key) ) continue; - var view = Ember.View.views[key]; + var view = views[key]; if ( !(view instanceof Settings) ) continue; diff --git a/src/ui/styles.js b/src/ui/styles.js index 84bf28c5..095e48aa 100644 --- a/src/ui/styles.js +++ b/src/ui/styles.js @@ -11,7 +11,7 @@ FFZ.prototype.setup_css = function() { var s = this._main_style = document.createElement('link'); s.id = "ffz-main-css"; s.setAttribute('rel', 'stylesheet'); - s.setAttribute('href', constants.DIRECT_SERVER + "script/style" + (constants.DEBUG ? "" : ".min") + ".css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info)); + s.setAttribute('href', constants.SERVER + "script/style" + (constants.DEBUG ? "" : ".min") + ".css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info)); document.head.appendChild(s); this.log("Readying toggleable styles."); diff --git a/style.css b/style.css index 0c9514b4..441a297c 100644 --- a/style.css +++ b/style.css @@ -955,6 +955,7 @@ body:not(.ffz-bttv) .dropmenu.share { margin-bottom: 0; } /* Menu Scrollbar */ +.chatters-container::-webkit-scrollbar, .ffz-scrollbar::-webkit-scrollbar, .ember-chat .chat-settings::-webkit-scrollbar, .conversations-list .conversations-list-inner::-webkit-scrollbar, @@ -967,6 +968,7 @@ body:not(.ffz-bttv) .dropmenu.share { margin-bottom: 0; } width: 6px; } +.chatters-container::-webkit-scrollbar-thumb, .ffz-scrollbar::-webkit-scrollbar-thumb, .ember-chat .chat-settings::-webkit-scrollbar-thumb, .conversations-list .conversations-list-inner::-webkit-scrollbar-thumb, @@ -987,6 +989,7 @@ body:not(.ffz-bttv) .dropmenu.share { margin-bottom: 0; } .ffz-dark .conversations-list .conversations-list-inner::-webkit-scrollbar-thumb, .ffz-dark .conversation-input-bar .emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb, +.theatre .chatters-container::-webkit-scrollbar-thumb, .theatre .ffz-scrollbar::-webkit-scrollbar-thumb, .theatre .ember-chat .chat-settings::-webkit-scrollbar-thumb, .theatre .conversation-window .conversation-content::-webkit-scrollbar-thumb, @@ -995,8 +998,9 @@ body:not(.ffz-bttv) .dropmenu.share { margin-bottom: 0; } .theatre .chat-history::-webkit-scrollbar-thumb, .theatre .emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb, .theatre .ffz-ui-menu-page::-webkit-scrollbar-thumb, -.theatre .ffz-ui-sub-menu-page::-webkit-scrollbar-thumb +.theatre .ffz-ui-sub-menu-page::-webkit-scrollbar-thumb, +.dark .chatters-container::-webkit-scrollbar-thumb, .dark .ffz-scrollbar::-webkit-scrollbar-thumb, .dark .ember-chat .chat-settings::-webkit-scrollbar-thumb, .dark .chat-history::-webkit-scrollbar-thumb, @@ -1004,6 +1008,7 @@ body:not(.ffz-bttv) .dropmenu.share { margin-bottom: 0; } .dark .ffz-ui-menu-page::-webkit-scrollbar-thumb, .dark .ffz-ui-sub-menu-page::-webkit-scrollbar-thumb, +.force-dark .chatters-container::-webkit-scrollbar-thumb, .force-dark .ffz-scrollbar::-webkit-scrollbar-thumb, .force-dark .ember-chat .chat-settings::-webkit-scrollbar-thumb, .force-dark .chat-history::-webkit-scrollbar-thumb, @@ -1014,6 +1019,12 @@ body:not(.ffz-bttv) .dropmenu.share { margin-bottom: 0; } box-shadow: 0 0 1px 1px rgba(0,0,0,0.25); } + +.chatters-container { + overflow-x: hidden !important; + overflow-y: auto !important; +} + /* Fix Moderation Cards */ img.channel_background[src="null"] { display: none; } @@ -1377,7 +1388,7 @@ a.unsafe-link { } .ffz-room-list td svg { - margin: 5px; + margin: 5px 10px 5px 0; float: left; } @@ -1391,9 +1402,9 @@ a.unsafe-link { .ffz-room-row:focus svg path, .ffz-room-row.active svg path { fill: #fff; } -.ffz-room-row:hover td, -.ffz-room-row:focus td, -.ffz-room-row.active td { +.ffz-room-row:hover, +.ffz-room-row:focus, +.ffz-room-row.active { background-color: #6441A5; color: #fff !important; } @@ -1404,7 +1415,7 @@ th.ffz-row-switch { .ffz-room-row a.leave-chat { float: right; - margin-right: 12px; + padding: 0 7px; } .ffz-row-switch .switch { @@ -1419,6 +1430,8 @@ th.ffz-row-switch { /* Chat Tabs */ +.ember-chat .chat-room { z-index: 5; } + #ffz-group-tabs { padding: 10px 10px 6px; box-shadow: inset 0 -1px 0 0 rgba(0,0,0,0.2); @@ -1525,6 +1538,17 @@ th.ffz-row-switch { margin: 5px 0; } +.ffz-room-row.row-mentioned { + background-color: rgba(128,50,50,0.1); + color: red !important; +} + +.ffz-room-row.row-mentioned:not(.active):hover, +.ffz-room-row.row-mentioned:not(.active):focus { + background-color: #a54141; + color: #fff !important; +} + #ffz-group-tabs .button .notifications { background-color: #d44949; top: 0;