mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-31 10:20:55 +00:00
Merge branch 'master' into socketdev
This commit is contained in:
commit
715a3a993c
51 changed files with 2832 additions and 989 deletions
175
dark.css
175
dark.css
|
@ -63,35 +63,6 @@
|
|||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* moderation card */
|
||||
|
||||
.ember-chat-container.dark .ember-chat .ffz-moderation-card,
|
||||
.chat-container.dark .ember-chat .ffz-moderation-card,
|
||||
.app-main.theatre .ember-chat .ffz-moderation-card {
|
||||
border-color: #1b1b20;
|
||||
}
|
||||
|
||||
.ember-chat-container.dark .ember-chat .moderation-card:focus,
|
||||
.chat-container.dark .ember-chat .moderation-card:focus,
|
||||
.app-main.theatre .ember-chat .moderation-card:focus {
|
||||
border-color: #cbcbcb;
|
||||
}
|
||||
|
||||
.ember-chat-container.dark .ember-chat .moderation-card .interface,
|
||||
.chat-container.dark .ember-chat .moderation-card .interface,
|
||||
.app-main.theatre .ember-chat .moderation-card .interface {
|
||||
background-color: #232329;
|
||||
}
|
||||
|
||||
.ffz-no-blue .ember-chat-container.dark .ember-chat .moderation-card .interface,
|
||||
.ffz-no-blue .chat-container.dark .ember-chat .moderation-card .interface,
|
||||
.ffz-no-blue .app-main.theatre .ember-chat .moderation-card .interface {
|
||||
background-color: #232323;
|
||||
}
|
||||
|
||||
.moderation-card h3.name a { color: #fff !important; }
|
||||
|
||||
|
||||
/* stats */
|
||||
.ffz-dark .stats-and-actions,
|
||||
.ffz-dark #main_col .content #stats_and_actions {
|
||||
|
@ -164,6 +135,7 @@
|
|||
|
||||
/* Popups */
|
||||
|
||||
.ffz-dark .conversation-settings-menu,
|
||||
.ffz-dark .ember-chat .chat-interface .ffz-ui-popup.emoticon-selector .emoticon-selector-box,
|
||||
.ffz-dark .card,
|
||||
.ffz-dark #flyout .content,
|
||||
|
@ -530,7 +502,7 @@
|
|||
|
||||
/* FrankerFaceZ Menu */
|
||||
|
||||
.ffz-dark .ember-chat .chat-menu .list-header {
|
||||
/*.ffz-dark .ember-chat .chat-menu .list-header {
|
||||
border-top-color: rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
|
@ -552,7 +524,7 @@
|
|||
|
||||
.ffz-dark .ffz-ui-popup ul.menu li.active a {
|
||||
border-top-color: rgb(16,16,16);
|
||||
}
|
||||
}*/
|
||||
|
||||
/* New User Prompt */
|
||||
|
||||
|
@ -925,4 +897,143 @@
|
|||
.ffz-dark .playlist-container:not(.playlist-enabled) .playlist-item:hover,
|
||||
.ffz-dark .playlist-container:not(.playlist-enabled) .ui-sortable-helper {
|
||||
background-color: rgba(255,255,255,0.2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Conversations */
|
||||
|
||||
.ffz-dark .conversation-input-bar .emoticon-selector-toggle svg path {
|
||||
fill: rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-input-bar .emoticon-selector-toggle:hover svg path {
|
||||
fill: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-input-bar .emoticon-selector-box .emote-set {
|
||||
border-color: #323232;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-input-bar .emoticon-selector-box .emoticon-grid {
|
||||
background-color: #191919;
|
||||
}
|
||||
|
||||
.ffz-dark .ember-chat .chat-settings .experimental-options {
|
||||
border-top-color: rgba(255,255,255, 0.2);
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-settings-menu .options-divider {
|
||||
border-bottom-color: rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-settings-menu:before {
|
||||
border-color: transparent;
|
||||
border-bottom-color: #32323e;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-settings-menu:after {
|
||||
border-color: transparent;
|
||||
border-bottom-color: rgb(16,16,16);
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list-icon {
|
||||
background: #19191f;
|
||||
color: #8c8c9c;
|
||||
border-color: #19191f
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list-icon:hover {
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list {
|
||||
background: #19191f;
|
||||
border: 1px solid #32323e;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list:after {
|
||||
border-color: rgba(25,25,31,0);
|
||||
border-top-color: #19191f;
|
||||
border-width: 10px;
|
||||
margin-left: -10px
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list:before {
|
||||
right: 9px;
|
||||
border-color: rgba(50,50,62,0);
|
||||
border-top-color: #32323e
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list .conversations-list-header {
|
||||
background: #121218;
|
||||
border-bottom: 1px solid #32323e;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list .conversation-preview-line {
|
||||
color: #8c8c9c
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list .conversations-list-item {
|
||||
border-bottom: 1px solid #32323e
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list .conversations-list-item:hover {
|
||||
background-color: #121218;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window {
|
||||
background: #19191f;
|
||||
box-shadow: none;
|
||||
color: #8c8c9c;
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list-icon,
|
||||
.ffz-dark .conversation-window {
|
||||
border: 1px solid rgba(255,255,255,0.2);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-header {
|
||||
background: #121217;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window:not(.collapsed) .conversation-header {
|
||||
border-bottom: 1px solid rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-input-bar textarea {
|
||||
border-color: rgba(255,255,255,0.1);
|
||||
background-color: rgba(255,255,255,0.05);
|
||||
color: #b6b6b6
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-input-actions .button,
|
||||
.ffz-dark .conversation-input-actions .follow-button:not(.ember-follow) .follow {
|
||||
background-color: #444
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window.has-focus .conversation-header {
|
||||
background-color: #121217
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window.has-focus .conversation-header-name {
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window.has-focus .conversation-input-bar textarea:focus {
|
||||
border-color: rgba(255,255,255,0.2)
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window.has-focus .conversation-input-actions .button,
|
||||
.ffz-dark .conversation-window.has-focus .conversation-input-actions .follow-button:not(.ember-follow) .follow {
|
||||
background-color: #6441a5
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window .timestamp-line span,
|
||||
.ffz-dark .conversation-window .new-message-divider span { background: transparent; }
|
||||
|
||||
.ffz-dark .conversation-window .timestamp-line:after,
|
||||
.ffz-dark .conversation-window .new-message-divider:after { display: none; }
|
45
gulpfile.js
45
gulpfile.js
|
@ -44,26 +44,26 @@ gulp.task('prepare', ['clean'], function() {
|
|||
});
|
||||
|
||||
|
||||
gulp.task('templates', ['prepare'], function() {
|
||||
gulp.src(['build/templates/**/*.hbs'])
|
||||
.pipe(jsEscape())
|
||||
.pipe(wrap('Handlebars.compile(<%= contents %>)'))
|
||||
.pipe(declare({
|
||||
root: 'exports',
|
||||
noRedeclare: true,
|
||||
processName: function(filePath) {
|
||||
var match = filePath.match(/build[\\\/]templates[\\\/](.*)\.hbs$/);
|
||||
return declare.processNameByPath((match && match.length > 1) ? match[1] : filePath);
|
||||
}
|
||||
}))
|
||||
.pipe(concat('templates.js'))
|
||||
.pipe(gulp.dest('build/'))
|
||||
.on('error', util.log);
|
||||
});
|
||||
//gulp.task('templates', ['prepare'], function() {
|
||||
// return gulp.src(['build/templates/**/*.hbs'])
|
||||
// .pipe(jsEscape())
|
||||
// .pipe(wrap('Handlebars.compile(<%= contents %>)'))
|
||||
// .pipe(declare({
|
||||
// root: 'exports',
|
||||
// noRedeclare: true,
|
||||
// processName: function(filePath) {
|
||||
// var match = filePath.match(/build[\\\/]templates[\\\/](.*)\.hbs$/);
|
||||
// return declare.processNameByPath((match && match.length > 1) ? match[1] : filePath);
|
||||
// }
|
||||
// }))
|
||||
// .pipe(concat('templates.js'))
|
||||
// .pipe(gulp.dest('build/'))
|
||||
// .on('error', util.log);
|
||||
//});
|
||||
|
||||
|
||||
gulp.task('styles', ['prepare'], function() {
|
||||
gulp.src(['build/styles/**/*.css'])
|
||||
return gulp.src(['build/styles/**/*.css'])
|
||||
.pipe(minifyCss())
|
||||
.pipe(jsEscape())
|
||||
.pipe(declare({
|
||||
|
@ -74,14 +74,14 @@ gulp.task('styles', ['prepare'], function() {
|
|||
return declare.processNameByPath((match && match.length > 1) ? match[1] : filePath);
|
||||
}
|
||||
}))
|
||||
.pipe(concat('styles.js'))
|
||||
.pipe(concat('compiled_styles.js'))
|
||||
.pipe(gulp.dest('build/'))
|
||||
.on('error', util.log)
|
||||
});
|
||||
|
||||
|
||||
gulp.task('scripts', ['prepare', 'templates', 'styles'], function() {
|
||||
gulp.src(['build/main.js'])
|
||||
gulp.task('scripts', ['styles'], function() {
|
||||
return gulp.src(['build/main.js'])
|
||||
.pipe(browserify())
|
||||
.pipe(concat('script.js'))
|
||||
.pipe(header('(function(window) {'))
|
||||
|
@ -94,7 +94,7 @@ gulp.task('scripts', ['prepare', 'templates', 'styles'], function() {
|
|||
});
|
||||
|
||||
gulp.task('watch', ['default', 'server'], function() {
|
||||
gulp.watch('src/**/*', ['default']);
|
||||
return gulp.watch('src/**/*', ['default']);
|
||||
});
|
||||
|
||||
gulp.task('default', ['scripts']);
|
||||
|
@ -130,7 +130,7 @@ gulp.task('upload', ['default'], function() {
|
|||
.on('error', util.log);
|
||||
});
|
||||
|
||||
gulp.task('clear_cache', ['upload'], function() {
|
||||
gulp.task('clear_cache', ['upload'], function(cb) {
|
||||
// Load credentials from an external file.
|
||||
var contents = fs.readFileSync('credentials.json', 'utf8'),
|
||||
cred = JSON.parse(contents);
|
||||
|
@ -167,6 +167,7 @@ gulp.task('clear_cache', ['upload'], function() {
|
|||
return util.log("[FAIL] Non-200 Status: " + request.statusCode);
|
||||
|
||||
util.log("[SUCCESS] Cache cleared.");
|
||||
cb();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -43,9 +43,9 @@ FFZ.settings_info.legacy_badges = {
|
|||
},
|
||||
|
||||
on_update: function(val) {
|
||||
document.body.classList.toggle("ffz-legacy-mod-badges", val !== 0);
|
||||
document.body.classList.toggle("ffz-legacy-turbo-badges", val > 1);
|
||||
document.body.classList.toggle("ffz-legacy-badges", val === 3);
|
||||
this.toggle_style('badges-legacy', val === 3);
|
||||
this.toggle_style('badges-legacy-mod', val !== 0);
|
||||
this.toggle_style('badges-legacy-turbo', val > 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -83,11 +83,12 @@ FFZ.settings_info.transparent_badges = {
|
|||
if ( this.has_bttv )
|
||||
return;
|
||||
|
||||
document.body.classList.toggle("ffz-rounded-badges", val === 1);
|
||||
document.body.classList.toggle("ffz-circular-badges", val === 2);
|
||||
document.body.classList.toggle("ffz-circular-blank-badges", val === 3);
|
||||
document.body.classList.toggle("ffz-circular-small-badges", val === 4);
|
||||
document.body.classList.toggle("ffz-transparent-badges", val === 5);
|
||||
this.toggle_style('badges-rounded', val === 1);
|
||||
this.toggle_style('badges-circular', val === 2 || val === 3 || val === 4);
|
||||
this.toggle_style('badges-blank', val === 3 || val === 4);
|
||||
this.toggle_style('badges-circular-small', val === 4);
|
||||
this.toggle_style('badges-transparent', val === 5);
|
||||
document.body.classList.toggle('ffz-transparent-badges', val === 5);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -98,16 +99,18 @@ FFZ.settings_info.transparent_badges = {
|
|||
|
||||
FFZ.prototype.setup_badges = function() {
|
||||
if ( ! this.has_bttv ) {
|
||||
document.body.classList.toggle("ffz-rounded-badges", this.settings.transparent_badges === 1);
|
||||
document.body.classList.toggle("ffz-circular-badges", this.settings.transparent_badges === 2);
|
||||
document.body.classList.toggle("ffz-circular-blank-badges", this.settings.transparent_badges === 3);
|
||||
document.body.classList.toggle("ffz-circular-small-badges", this.settings.transparent_badges === 4);
|
||||
document.body.classList.toggle("ffz-transparent-badges", this.settings.transparent_badges === 5);
|
||||
var val = this.settings.transparent_badges;
|
||||
this.toggle_style('badges-rounded', val === 1);
|
||||
this.toggle_style('badges-circular', val === 2 || val === 3 || val === 4);
|
||||
this.toggle_style('badges-blank', val === 3 || val === 4);
|
||||
this.toggle_style('badges-circular-small', val === 4);
|
||||
this.toggle_style('badges-transparent', val === 5);
|
||||
document.body.classList.toggle('ffz-transparent-badges', val === 5);
|
||||
}
|
||||
|
||||
document.body.classList.toggle("ffz-legacy-mod-badges", this.settings.legacy_badges !== 0);
|
||||
document.body.classList.toggle("ffz-legacy-turbo-badges", this.settings.legacy_badges > 1);
|
||||
document.body.classList.toggle("ffz-legacy-badges", this.settings.legacy_badges === 3);
|
||||
this.toggle_style('badges-legacy', this.settings.legacy_badges === 3);
|
||||
this.toggle_style('badges-legacy-mod', this.settings.legacy_badges !== 0);
|
||||
this.toggle_style('badges-legacy-turbo', this.settings.legacy_badges > 1);
|
||||
|
||||
this.log("Preparing badge system.");
|
||||
this.badges = {};
|
||||
|
@ -254,9 +257,13 @@ FFZ.prototype.render_badges = function(component, badges) {
|
|||
if ( ! this.settings.show_badges )
|
||||
return badges;
|
||||
|
||||
var user = component.get('msgObject.from'),
|
||||
var user = component.get('msgObject.from') || component.get('message.from.username'),
|
||||
room_id = component.get('msgObject.room') || App.__container__.lookup('controller:chat').get('currentRoom.id');
|
||||
|
||||
return this._render_badges(user, room_id, badges, component);
|
||||
}
|
||||
|
||||
FFZ.prototype._render_badges = function(user, room_id, badges, component) {
|
||||
var data = this.users[user];
|
||||
if ( ! data || ! data.badges )
|
||||
return badges;
|
||||
|
|
|
@ -45,7 +45,7 @@ FFZ.settings_info.fix_color = {
|
|||
},
|
||||
|
||||
on_update: function(val) {
|
||||
document.body.classList.toggle("ffz-chat-colors-gray", !this.has_bttv && (val === '-1'));
|
||||
this.toggle_style('chat-colors-gray', !this.has_bttv && val === '-1');
|
||||
|
||||
if ( ! this.has_bttv && val !== '-1' )
|
||||
this._rebuild_colors();
|
||||
|
@ -114,6 +114,8 @@ FFZ.settings_info.color_blind = {
|
|||
// --------------------
|
||||
|
||||
FFZ.prototype.setup_colors = function() {
|
||||
this.toggle_style('chat-colors-gray', !this.has_bttv && this.settings.fix_color === '-1');
|
||||
|
||||
this._colors = {};
|
||||
this._rebuild_contrast();
|
||||
|
||||
|
@ -185,6 +187,43 @@ RGBColor.prototype.eq = function(rgb) {
|
|||
return rgb.r === this.r && rgb.g === this.g && rgb.b === this.b;
|
||||
}
|
||||
|
||||
RGBColor.fromCSS = function(rgb) {
|
||||
rgb = rgb.trim();
|
||||
|
||||
if ( rgb.charAt(0) === '#' )
|
||||
return RGBColor.fromHex(rgb);
|
||||
|
||||
var match = /rgba?\( *(\d+%?) *, *(\d+%?) *, *(\d+%?) *(?:,[^\)]+)?\)/.exec(rgb);
|
||||
if ( match ) {
|
||||
var r = match[1],
|
||||
g = match[2],
|
||||
b = match[3];
|
||||
|
||||
if ( r.charAt(r.length-1) === '%' )
|
||||
r = 255 * (parseInt(r) / 100);
|
||||
else
|
||||
r = parseInt(r);
|
||||
|
||||
if ( g.charAt(g.length-1) === '%' )
|
||||
g = 255 * (parseInt(g) / 100);
|
||||
else
|
||||
g = parseInt(g);
|
||||
|
||||
if ( b.charAt(b.length-1) === '%' )
|
||||
b = 255 * (parseInt(b) / 100);
|
||||
else
|
||||
b = parseInt(b);
|
||||
|
||||
return new RGBColor(
|
||||
Math.min(Math.max(0, r), 255),
|
||||
Math.min(Math.max(0, g), 255),
|
||||
Math.min(Math.max(0, b), 255)
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
RGBColor.fromHex = function(code) {
|
||||
var raw = parseInt(code.charAt(0) === '#' ? code.substr(1) : code, 16);
|
||||
return new RGBColor(
|
||||
|
@ -570,7 +609,7 @@ FFZ.prototype._update_colors = function(darkness_only) {
|
|||
|
||||
this._color_old_darkness = is_dark;
|
||||
|
||||
var colored_bits = document.querySelectorAll('.chat-line .has-color');
|
||||
var colored_bits = document.querySelectorAll('.has-color');
|
||||
for(var i=0, l=colored_bits.length; i < l; i++) {
|
||||
var bit = colored_bits[i],
|
||||
color = bit.getAttribute('data-color'),
|
||||
|
@ -585,6 +624,9 @@ FFZ.prototype._update_colors = function(darkness_only) {
|
|||
|
||||
|
||||
FFZ.prototype._handle_color = function(color) {
|
||||
if ( color instanceof RGBColor )
|
||||
color = color.toHex();
|
||||
|
||||
if ( ! color || this._colors.hasOwnProperty(color) )
|
||||
return this._colors[color];
|
||||
|
||||
|
|
|
@ -2,10 +2,13 @@ var SVGPATH = '<path d="m120.95 1.74c4.08-0.09 8.33-0.84 12.21 0.82 3.61 1.8 7 4
|
|||
|
||||
DEBUG = localStorage.ffzDebugMode == "true" && document.body.classList.contains('ffz-dev'),
|
||||
SERVER = DEBUG ? "//localhost:8000/" : "//cdn.frankerfacez.com/";
|
||||
DIRECT_SERVER = DEBUG ? "//localhost:8000/" : "//direct-cdn.frankerfacez.com/";
|
||||
|
||||
module.exports = {
|
||||
DEBUG: DEBUG,
|
||||
SERVER: SERVER,
|
||||
DIRECT_SERVER: DIRECT_SERVER,
|
||||
|
||||
API_SERVER: "//api.frankerfacez.com/",
|
||||
API_SERVER_2: "//direct-api.frankerfacez.com/",
|
||||
|
||||
|
@ -14,6 +17,8 @@ module.exports = {
|
|||
2: ["ws://localhost:8001/"]
|
||||
},
|
||||
|
||||
TOOLTIP_DISTANCE: 50,
|
||||
|
||||
KNOWN_CODES: {
|
||||
"#-?[\\\\/]": "#-/",
|
||||
":-?(?:7|L)": ":-7",
|
||||
|
@ -39,6 +44,7 @@ module.exports = {
|
|||
"Gr(a|e)yFace": "GrayFace"
|
||||
},
|
||||
|
||||
TWITCH_BASE: 'http://static-cdn.jtvnw.net/emoticons/v1/',
|
||||
EMOTE_MIRROR_BASE: SERVER + "twitch-emote-mirror/",
|
||||
|
||||
EMOTE_REPLACEMENT_BASE: SERVER + "script/replacements/",
|
||||
|
@ -74,6 +80,7 @@ module.exports = {
|
|||
CLOCK: '<svg class="svg-glyph_views ffz-svg svg-clock" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path fill-rule="evenodd" clip-rule="evenodd" fill="#888888" d="M8,15c-3.866,0-7-3.134-7-7s3.134-7,7-7s7,3.134,7,7 S11.866,15,8,15z M8,3C5.238,3,3,5.238,3,8s2.238,5,5,5s5-2.238,5-5S10.762,3,8,3z M7.293,8.707L7,8l1-4l0.902,3.607L11,11 L7.293,8.707z"/></svg>',
|
||||
GEAR: '<svg class="svg-gear" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M15,7v2h-2.115c-0.125,0.615-0.354,1.215-0.713,1.758l1.484,1.484l-1.414,1.414l-1.484-1.484C10.215,12.531,9.615,12.76,9,12.885V15H7v-2.12c-0.614-0.126-1.21-0.356-1.751-0.714l-1.491,1.49l-1.414-1.414l1.491-1.49C3.477,10.211,3.247,9.613,3.12,9H1V7h2.116C3.24,6.384,3.469,5.785,3.829,5.242L2.343,3.757l1.414-1.414l1.485,1.485C5.785,3.469,6.384,3.24,7,3.115V1h2v2.12c0.613,0.126,1.211,0.356,1.752,0.714l1.49-1.491l1.414,1.414l-1.49,1.492C12.523,5.79,12.754,6.387,12.88,7H15z M8,6C6.896,6,6,6.896,6,8s0.896,2,2,2s2-0.896,2-2S9.104,6,8,6z" fill-rule="evenodd"></path></svg>',
|
||||
HEART: '<svg class="svg-heart" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M8,13.5L1.5,7V4l2-2h3L8,3.5L9.5,2h3l2,2v3L8,13.5z" fill-rule="evenodd"></path></svg>',
|
||||
UNHEART: '<svg class="svg-unheart" height="16px" version="1.1" viewbox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M1,9V7h14v2H1z M1,4l2-2h3l2,2l2-2h3l2,2v2H1V4z M8,14l-4.667-4h9.333L8,14z" fill-rule="evenodd"></path></svg>',
|
||||
EMOTE: '<svg class="svg-emote" height="16px" version="1.1" viewBox="0 0 18 18" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M9,18c-4.971,0-9-4.029-9-9s4.029-9,9-9s9,4.029,9,9S13.971,18,9,18z M14,4.111V4h-0.111C12.627,2.766,10.904,2,9,2C7.095,2,5.373,2.766,4.111,4H4v0.111C2.766,5.373,2,7.096,2,9s0.766,3.627,2,4.889V14l0.05-0.051C5.317,15.217,7.067,16,9,16c1.934,0,3.684-0.783,4.949-2.051L14,14v-0.111c1.234-1.262,2-2.984,2-4.889S15.234,5.373,14,4.111zM11,6h2v4h-2V6z M12.535,12.535C11.631,13.44,10.381,14,9,14s-2.631-0.56-3.536-1.465l0.707-0.707C6.896,12.553,7.896,13,9,13s2.104-0.447,2.828-1.172L12.535,12.535z M5,6h2v4H5V6z" fill-rule="evenodd"></path></svg>',
|
||||
STAR: '<svg class="svg-star" height="16px" version="1.1" viewbox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M15,6l-4.041,2.694L13,14l-5-3.333L3,14l2.041-5.306L1,6h5.077L8,1l1.924,5H15z" fill-rule="evenodd"></path></svg>',
|
||||
CLOSE: '<svg class="svg-close_small" height="16px" version="1.1" viewbox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M12.657,4.757L9.414,8l3.243,3.242l-1.415,1.415L8,9.414l-3.243,3.243l-1.414-1.415L6.586,8L3.343,4.757l1.414-1.414L8,6.586l3.242-3.243L12.657,4.757z" fill-rule="evenodd"></path></svg>',
|
||||
|
|
|
@ -16,6 +16,7 @@ FFZ.prototype.setup_channel = function() {
|
|||
|
||||
// Settings stuff!
|
||||
document.body.classList.toggle("ffz-hide-view-count", !this.settings.channel_views);
|
||||
document.body.classList.toggle('ffz-theater-stats', this.settings.theater_stats);
|
||||
|
||||
this.log("Creating channel style element.");
|
||||
var s = this._channel_style = document.createElement('style');
|
||||
|
@ -214,7 +215,13 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
el.classList.add('ffz-channel');
|
||||
|
||||
// Try changing the theater mode tooltip.
|
||||
this.$('.theatre-button a').attr('title', 'Theater Mode (Alt+T)');
|
||||
var tb = this.$('.theatre-button > a'),
|
||||
opts = tb.data('tipsy');
|
||||
|
||||
tb.attr('title', 'Theater Mode (Alt+T)');
|
||||
if ( opts && opts.options && typeof opts.options.gravity !== "function" )
|
||||
opts.options.gravity = utils.tooltip_placement(constants.TOOLTIP_DISTANCE, opts.options.gravity || 'n');
|
||||
|
||||
|
||||
this.ffzFixTitle();
|
||||
this.ffzUpdateUptime();
|
||||
|
@ -282,7 +289,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
if ( ! btn ) {
|
||||
btn = document.createElement('span');
|
||||
btn.id = 'ffz-ui-host-button';
|
||||
btn.className = 'button action tooltip';
|
||||
btn.className = 'button action';
|
||||
|
||||
btn.addEventListener('click', this.ffzClickHost.bind(btn, this, false));
|
||||
|
||||
|
@ -294,6 +301,8 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
container.insertBefore(btn, before);
|
||||
else
|
||||
container.appendChild(btn);
|
||||
|
||||
jQuery(btn).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
|
||||
btn.classList.remove('disabled');
|
||||
|
@ -320,7 +329,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
if ( ! btn ) {
|
||||
btn = document.createElement('span');
|
||||
btn.id = 'ffz-ui-host-button';
|
||||
btn.className = 'button action tooltip';
|
||||
btn.className = 'button action';
|
||||
|
||||
btn.addEventListener('click', this.ffzClickHost.bind(btn, this, true));
|
||||
|
||||
|
@ -332,6 +341,8 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
container.insertBefore(btn, before);
|
||||
else
|
||||
container.appendChild(btn);
|
||||
|
||||
jQuery(btn).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
|
||||
btn.classList.remove('disabled');
|
||||
|
@ -405,7 +416,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
else
|
||||
cont.appendChild(stat);
|
||||
|
||||
jQuery(stat).tipsy();
|
||||
jQuery(stat).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
|
||||
el.innerHTML = utils.number_commas(chatter_count);
|
||||
|
@ -437,7 +448,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
else
|
||||
cont.appendChild(stat);
|
||||
|
||||
jQuery(stat).tipsy();
|
||||
jQuery(stat).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
|
||||
el.innerHTML = utils.number_commas(ffz_viewers) + " (" + utils.number_commas(ffz_chatters) + ")";
|
||||
|
@ -472,7 +483,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
if ( ! stat_el ) {
|
||||
stat_el = document.createElement('span');
|
||||
stat_el.id = 'ffz-ui-player-stats';
|
||||
stat_el.className = 'ffz stat tooltip';
|
||||
stat_el.className = 'ffz stat';
|
||||
|
||||
stat_el.innerHTML = constants.GRAPH + " ";
|
||||
el = document.createElement('span');
|
||||
|
@ -483,16 +494,18 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
container.insertBefore(stat_el, other.nextSibling);
|
||||
else
|
||||
container.appendChild(stat_el);
|
||||
|
||||
jQuery(stat_el).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
|
||||
var delay = parseFloat(stats.hlsLatencyBroadcaster);
|
||||
|
||||
if ( delay > 180 ) {
|
||||
delay = Math.floor(delay);
|
||||
stat_el.setAttribute('original-title', 'Video Information\nBroadcast ' + utils.time_to_string(delay, true) + ' Ago\n\nVideo: ' + stats.videoResolution + 'p @ ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps')
|
||||
stat_el.setAttribute('original-title', 'Video Information<br>Broadcast ' + utils.time_to_string(delay, true) + ' Ago<br><br>Video: ' + stats.videoResolution + 'p @ ' + stats.fps + '<br>Playback Rate: ' + stats.playbackRate + ' Kbps')
|
||||
el.textContent = utils.time_to_string(Math.floor(delay), true, delay > 172800) + ' old';
|
||||
} else {
|
||||
stat_el.setAttribute('original-title', 'Stream Latency\nVideo: ' + stats.videoResolution + 'p @ ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps');
|
||||
stat_el.setAttribute('original-title', 'Stream Latency<br>Video: ' + stats.videoResolution + 'p @ ' + stats.fps + '<br>Playback Rate: ' + stats.playbackRate + ' Kbps');
|
||||
|
||||
delay = stats.hlsLatencyBroadcaster;
|
||||
var pos = delay.lastIndexOf('.');
|
||||
|
@ -531,7 +544,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
if ( ! stat_el ) {
|
||||
stat_el = document.createElement('span');
|
||||
stat_el.id = 'ffz-ui-player-stats';
|
||||
stat_el.className = 'ffz stat tooltip';
|
||||
stat_el.className = 'ffz stat';
|
||||
|
||||
stat_el.innerHTML = constants.GRAPH + " ";
|
||||
el = document.createElement('span');
|
||||
|
@ -542,16 +555,18 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
container.insertBefore(stat_el, other.nextSibling);
|
||||
else
|
||||
container.appendChild(stat_el);
|
||||
|
||||
jQuery(stat_el).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
|
||||
var delay = parseFloat(stats.hlsLatencyBroadcaster);
|
||||
|
||||
if ( delay > 180 ) {
|
||||
delay = Math.floor(delay);
|
||||
stat_el.setAttribute('original-title', 'Video Information\nBroadcast ' + utils.time_to_string(delay, true) + ' Ago\n\nVideo: ' + stats.videoResolution + 'p @ ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps')
|
||||
stat_el.setAttribute('original-title', 'Video Information<br>Broadcast ' + utils.time_to_string(delay, true) + ' Ago<br><br>Video: ' + stats.videoResolution + 'p @ ' + stats.fps + '<br>Playback Rate: ' + stats.playbackRate + ' Kbps')
|
||||
el.textContent = utils.time_to_string(Math.floor(delay), true, delay > 172800) + ' old';
|
||||
} else {
|
||||
stat_el.setAttribute('original-title', 'Stream Latency\nVideo: ' + stats.videoResolution + 'p @ ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps');
|
||||
stat_el.setAttribute('original-title', 'Stream Latency<br>Video: ' + stats.videoResolution + 'p @ ' + stats.fps + '<br>Playback Rate: ' + stats.playbackRate + ' Kbps');
|
||||
|
||||
delay = stats.hlsLatencyBroadcaster;
|
||||
var pos = delay.lastIndexOf('.');
|
||||
|
@ -623,7 +638,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
}
|
||||
}
|
||||
|
||||
jQuery(stat).tipsy({html: true});
|
||||
jQuery(stat).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
|
||||
el.innerHTML = utils.time_to_string(uptime, false, false, false, f.settings.stream_uptime === 1 || f.settings.stream_uptime === 3);
|
||||
|
@ -790,6 +805,20 @@ FFZ.settings_info.stream_title = {
|
|||
};
|
||||
|
||||
|
||||
FFZ.settings_info.theater_stats = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
no_mobile: true,
|
||||
|
||||
category: "Channel Metadata",
|
||||
name: "Display on Theater Mode Hover",
|
||||
help: "Show the channel metadata and actions over the video player in theater mode when you hover it with your mouse.",
|
||||
on_update: function(val) {
|
||||
document.body.classList.toggle('ffz-theater-stats', val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
FFZ.basic_settings.channel_info = {
|
||||
type: "select",
|
||||
options: {
|
||||
|
|
|
@ -415,7 +415,7 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
ffzInit: function() {
|
||||
f._chatv = this;
|
||||
this.$('.textarea-contain').append(f.build_ui_link(this));
|
||||
this.$('.chat-messages').find('.html-tooltip').tipsy({live: true, html: true, gravity: jQuery.fn.tipsy.autoNS});
|
||||
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();
|
||||
|
@ -447,23 +447,32 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
var room = this.get('controller.currentRoom'), rows;
|
||||
room && room.resetUnreadCount();
|
||||
|
||||
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 ( this._ffz_chan_table ) {
|
||||
rows = jQuery(this._ffz_chan_table);
|
||||
rows.children('.ffz-room-row').removeClass('active');
|
||||
if ( room )
|
||||
rows.children('.ffz-room-row[data-room="' + room.get('id') + '"]').addClass('active').children('span').text('');
|
||||
}
|
||||
|
||||
if ( this._ffz_group_table ) {
|
||||
rows = jQuery(this._ffz_group_table);
|
||||
rows.children('.ffz-room-row').removeClass('active');
|
||||
if ( room )
|
||||
rows.children('.ffz-room-row[data-room="' + room.get('id') + '"]').addClass('active').children('span').text('');
|
||||
}
|
||||
|
||||
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');
|
||||
|
@ -473,6 +482,15 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
sp.innerHTML = '';
|
||||
}
|
||||
|
||||
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 = '';
|
||||
}
|
||||
|
||||
// Invite Link
|
||||
var can_invite = room && room.get('canInvite');
|
||||
this._ffz_invite && this._ffz_invite.classList.toggle('hidden', !can_invite);
|
||||
|
@ -486,9 +504,7 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
// Better Menu
|
||||
|
||||
ffzRebuildMenu: function() {
|
||||
return;
|
||||
|
||||
var el = this.get('element'),
|
||||
/*var el = this.get('element'),
|
||||
room_list = el && el.querySelector('.chat-rooms .tse-content');
|
||||
|
||||
if ( ! room_list )
|
||||
|
@ -518,7 +534,7 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
tbl.setAttribute('cellspacing', 0);
|
||||
tbl.id = 'ffz-channel-table';
|
||||
tbl.className = 'ffz';
|
||||
tbl.innerHTML = '<thead><tr><th colspan="2">Channels</th><th class="ffz-row-switch">Join</th><th class="ffz-row-switch">Pin</th></tr></thead><tbody></tbody>';
|
||||
tbl.innerHTML = '<thead><tr><th colspan="2">Channels</th><th class="ffz-row-switch">Pin</th></tr></thead><tbody></tbody>';
|
||||
room_list.insertBefore(tbl, room_list.firstChild);
|
||||
|
||||
chan_table = this._ffz_chan_table = tbl.querySelector('tbody');
|
||||
|
@ -556,7 +572,7 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
tbl.setAttribute('cellspacing', 0);
|
||||
tbl.id = 'ffz-group-table';
|
||||
tbl.className = 'ffz';
|
||||
tbl.innerHTML = '<thead><tr><th colspan="2">Group Chats</th><th class="ffz-row-switch">Pin</th></tr></thead><tbody></tbody>';
|
||||
tbl.innerHTML = '<thead><tr><th colspan="2">Group Chats</th></tr></thead><tbody></tbody>';
|
||||
|
||||
var before = room_list.querySelector('#ffz-channel-table');
|
||||
room_list.insertBefore(tbl, before.nextSibling);
|
||||
|
@ -575,30 +591,29 @@ 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) {
|
||||
/*ffzBuildRow: function(view, room, current_channel, host_channel) {
|
||||
var row = document.createElement('tr'),
|
||||
icon = document.createElement('td'),
|
||||
name_el = document.createElement('td'),
|
||||
|
||||
btn,
|
||||
toggle_pinned = document.createElement('td'),
|
||||
toggle_visible = document.createElement('td'),
|
||||
|
||||
group = room.get('isGroupRoom'),
|
||||
current = room === view.get('controller.currentRoom'),
|
||||
//unread = format_unread(current ? 0 : room.get('unreadCount')),
|
||||
unread = utils.format_unread(current ? 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 = format_unread(current ? 0 : room.get('unreadCount'));
|
||||
name_el.innerHTML = utils.sanitize(name);
|
||||
unread = utils.format_unread(current ? 0 : room.get('unreadCount'));
|
||||
name_el.innerHTML = utils.sanitize(name) + ' <span>' + unread + '</span>';
|
||||
}));
|
||||
|
||||
name_el.className = 'ffz-room';
|
||||
name_el.innerHTML = utils.sanitize(name);
|
||||
name_el.innerHTML = utils.sanitize(name) + ' <span>' + unread + '</span>';
|
||||
|
||||
if ( current_channel ) {
|
||||
icon.innerHTML = constants.CAMERA;
|
||||
|
@ -610,10 +625,9 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
icon.className = name_el.className = 'tooltip';
|
||||
}
|
||||
|
||||
toggle_pinned.className = toggle_visible.className = 'ffz-row-switch';
|
||||
toggle_pinned.className = 'ffz-row-switch';
|
||||
|
||||
toggle_pinned.innerHTML = '<a class="switch' + (f.settings.pinned_rooms.indexOf(room.get('id')) !== -1 ? ' active' : '') + '"><span></span></a>';
|
||||
toggle_visible.innerHTML = '<a class="switch' + (f.settings.visible_rooms.indexOf(room.get('id')) !== -1 ? ' active' : '') + '"><span></span></a>';
|
||||
|
||||
row.setAttribute('data-room', room.get('id'));
|
||||
|
||||
|
@ -662,34 +676,16 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
});
|
||||
}
|
||||
|
||||
row.appendChild(toggle_visible);
|
||||
btn = toggle_visible.querySelector('a.switch');
|
||||
btn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation && e.stopPropagation();
|
||||
|
||||
var room_id = room.get('id'),
|
||||
visible_rooms = f.settings.visible_rooms,
|
||||
is_visible = visible_rooms.indexOf(room_id) !== -1;
|
||||
|
||||
if ( is_visible )
|
||||
visible_rooms.removeObject(room_id);
|
||||
else
|
||||
visible_rooms.push(room_id);
|
||||
|
||||
f.settings.set('visible_rooms', visible_rooms);
|
||||
this.classList.toggle('active', !is_visible);
|
||||
view.ffzRebuildTabs();
|
||||
});
|
||||
|
||||
row.addEventListener('click', function() {
|
||||
var controller = view.get('controller');
|
||||
controller.focusRoom(room);
|
||||
controller.set('showList', false);
|
||||
});
|
||||
|
||||
|
||||
room._ffz_row = row;
|
||||
return row;
|
||||
},
|
||||
},*/
|
||||
|
||||
// Group Tabs~!
|
||||
|
||||
|
@ -721,10 +717,22 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
|
||||
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;
|
||||
}*/
|
||||
|
||||
link.className = 'button glyph-only tooltip';
|
||||
link.title = "Chat Room Management";
|
||||
link.innerHTML = constants.ROOMS;
|
||||
link.innerHTML = constants.ROOMS; // + '<span class="notifications">' + (total_unread || '') + '</span>';
|
||||
|
||||
link.addEventListener('click', function() {
|
||||
var controller = view.get('controller');
|
||||
|
@ -806,38 +814,64 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
},
|
||||
|
||||
ffzTabUnread: function(room_id) {
|
||||
// TODO: Update menu.
|
||||
|
||||
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;
|
||||
var current_id = this.get('controller.currentRoom.id');
|
||||
|
||||
if ( room_id ) {
|
||||
var room = f.rooms && f.rooms[room_id] && f.rooms[room_id].room,
|
||||
tab = room && room._ffz_tab;
|
||||
row = room && room._ffz_row,
|
||||
tab = room && room._ffz_tab,
|
||||
|
||||
if ( tab ) {
|
||||
var unread = utils.format_unread(room_id === current_id ? 0 : room.get('unreadCount'));
|
||||
tab.querySelector('span').innerHTML = unread;
|
||||
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;
|
||||
}
|
||||
|
||||
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] && f.rooms[room_id];
|
||||
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,
|
||||
|
||||
if ( ! room )
|
||||
continue;
|
||||
unread_count = room_id === current_id ? 0 : room.get('unreadCount'),
|
||||
is_unread = unread_count > 0,
|
||||
unread = utils.format_unread(unread_count);
|
||||
|
||||
var unread = utils.format_unread(room_id === current_id ? 0 : room.room.get('unreadCount'));
|
||||
tab.querySelector('span').innerHTML = unread;
|
||||
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;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
210
src/ember/conversations.js
Normal file
210
src/ember/conversations.js
Normal file
|
@ -0,0 +1,210 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require('../utils'),
|
||||
constants = require('../constants');
|
||||
|
||||
|
||||
// ---------------
|
||||
// Settings
|
||||
// ---------------
|
||||
|
||||
FFZ.settings_info.conv_focus_on_click = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
no_mobile: true,
|
||||
visible: false,
|
||||
|
||||
category: "Conversations",
|
||||
name: "Focus Input on Click",
|
||||
help: "Focus on a conversation's input box when you click it."
|
||||
};
|
||||
|
||||
FFZ.settings_info.top_conversations = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
no_mobile: true,
|
||||
|
||||
category: "Conversations",
|
||||
name: "Position on Top",
|
||||
help: "Display the new conversation-style whisper UI at the top of the window instead of the bottom.",
|
||||
on_update: function(val) {
|
||||
document.body.classList.toggle('ffz-top-conversations', val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// ---------------
|
||||
// Initialization
|
||||
// ---------------
|
||||
|
||||
FFZ.prototype.setup_conversations = function() {
|
||||
document.body.classList.toggle('ffz-top-conversations', this.settings.top_conversations);
|
||||
|
||||
this.log("Hooking the Ember Conversation Window component.");
|
||||
var ConvWindow = App.__container__.resolve('component:conversation-window');
|
||||
if ( ConvWindow )
|
||||
this._modify_conversation_window(ConvWindow);
|
||||
|
||||
|
||||
this.log("Hooking the Ember Conversation Line component.");
|
||||
var ConvLine = App.__container__.resolve('component:conversation-line');
|
||||
if ( ConvLine )
|
||||
this._modify_conversation_line(ConvLine);
|
||||
|
||||
// TODO: Make this better later.
|
||||
jQuery('.conversations-list').find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._modify_conversation_window = function(component) {
|
||||
var f = this,
|
||||
|
||||
Layout = App.__container__.lookup('controller:layout'),
|
||||
Settings = App.__container__.lookup('controller:settings');
|
||||
|
||||
component.reopen({
|
||||
headerBadges: Ember.computed("thread.participants", "currentUsername", function() {
|
||||
var e = this.get("thread.participants").rejectBy("username", this.get("currentUsername")).objectAt(0),
|
||||
badges = {},
|
||||
|
||||
ut = e.get("userType");
|
||||
|
||||
if ( ut === "staff" )
|
||||
badges[0] = {classes: 'badge staff', title: 'Staff'};
|
||||
else if ( ut === 'admin' )
|
||||
badges[0] = {classes: 'badge admin', title: 'Admin'};
|
||||
else if ( ut === 'global_mod' )
|
||||
badges[0] = {classes: 'badge global-moderator', title: 'Global Moderator'};
|
||||
|
||||
if ( e.get('hasTurbo') )
|
||||
badges[15] = {classes: 'badge turbo', title: 'Turbo'}
|
||||
|
||||
// FFZ Badges
|
||||
var data = f.users[e.get('username')];
|
||||
if ( data && data.badges ) {
|
||||
for(var slot in data.badges) {
|
||||
if ( ! data.badges.hasOwnProperty(slot) )
|
||||
continue;
|
||||
|
||||
var badge = data.badges[slot],
|
||||
full_badge = f.badges[badge.id] || {},
|
||||
old_badge = badges[slot];
|
||||
|
||||
if ( full_badge.visible !== undefined ) {
|
||||
var visible = full_badge.visible;
|
||||
if ( typeof visible === "function" )
|
||||
try {
|
||||
visible = visible.bind(f)(null, e.get('username'), null, badges);
|
||||
} catch(err) {
|
||||
f.error("badge " + badge.id + " visible: " + err);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! visible )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( old_badge ) {
|
||||
var replaces = badge.hasOwnProperty('replaces') ? badge.replaces : full_badge.replaces;
|
||||
if ( ! replaces )
|
||||
continue;
|
||||
|
||||
old_badge.klass = 'badge ffz-badge-' + badge.id;
|
||||
old_badge.title += ', ' + (badge.title || full_badge.title);
|
||||
continue;
|
||||
}
|
||||
|
||||
badges[slot] = {
|
||||
classes: 'badge ffz-badge-' + badge.id,
|
||||
title: badge.title || full_badge.title
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var out = [];
|
||||
for(var slot in badges)
|
||||
out.push(badges[slot]);
|
||||
|
||||
return out;
|
||||
}),
|
||||
|
||||
didInsertElement: function() {
|
||||
var el = this.get('element'),
|
||||
header = el && el.querySelector('.conversation-header'),
|
||||
header_name = header && header.querySelector('.conversation-header-name'),
|
||||
|
||||
raw_color = this.get('otherUser.color'),
|
||||
colors = raw_color && f._handle_color(raw_color),
|
||||
|
||||
is_dark = (Layout && Layout.get('isTheatreMode')) || f.settings.dark_twitch;
|
||||
|
||||
if ( header_name && raw_color ) {
|
||||
header_name.style.color = (is_dark ? colors[1] : colors[0]);
|
||||
header_name.classList.add('has-color');
|
||||
header_name.setAttribute('data-color', raw_color);
|
||||
}
|
||||
|
||||
jQuery(el).find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._modify_conversation_line = function(component) {
|
||||
var f = this,
|
||||
|
||||
Layout = App.__container__.lookup('controller:layout'),
|
||||
Settings = App.__container__.lookup('controller:settings');
|
||||
|
||||
component.reopen({
|
||||
tokenizedMessage: function() {
|
||||
try {
|
||||
return f.tokenize_conversation_line(this.get('message'));
|
||||
} catch(err) {
|
||||
f.error("convo-line tokenizedMessage: " + err);
|
||||
return this._super();
|
||||
}
|
||||
|
||||
}.property("message", "currentUsername"),
|
||||
|
||||
click: function(e) {
|
||||
if ( e.target && e.target.classList.contains('deleted-link') )
|
||||
return f._deleted_link_click.bind(e.target)(e);
|
||||
|
||||
if ( f._click_emote(e.target, e) )
|
||||
return;
|
||||
|
||||
return this._super(e);
|
||||
},
|
||||
|
||||
render: function(e) {
|
||||
var user = this.get('message.from.username'),
|
||||
raw_color = this.get('message.from.color'),
|
||||
colors = raw_color && f._handle_color(raw_color),
|
||||
|
||||
is_dark = (Layout && Layout.get('isTheatreMode')) || f.settings.dark_twitch;
|
||||
|
||||
e.push('<div class="indicator"></div>');
|
||||
|
||||
var alias = f.aliases[user],
|
||||
name = this.get('message.from.displayName') || (user && user.capitalize()) || "unknown user",
|
||||
style = colors && 'color:' + (is_dark ? colors[1] : colors[0]),
|
||||
colored = style ? ' has-color' : '';
|
||||
|
||||
if ( alias )
|
||||
e.push('<span class="from ffz-alias tooltip' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '" title="' + utils.sanitize(name) + '">' + utils.sanitize(alias) + '</span>');
|
||||
else
|
||||
e.push('<span class="from' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">' + utils.sanitize(name) + '</span>');
|
||||
|
||||
e.push('<span class="colon">:</span> ');
|
||||
|
||||
if ( ! this.get('isActionMessage') ) {
|
||||
style = '';
|
||||
colored = '';
|
||||
}
|
||||
|
||||
e.push('<span class="message' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">');
|
||||
e.push(f.render_tokens(this.get('tokenizedMessage'), true));
|
||||
e.push('</span>');
|
||||
}
|
||||
});
|
||||
}
|
|
@ -30,49 +30,52 @@ FFZ.prototype.setup_directory = function() {
|
|||
if ( ChannelView )
|
||||
this._modify_directory_live(ChannelView);
|
||||
|
||||
var CreativeChannel = App.__container__.resolve('view:creative-channel');
|
||||
if ( CreativeChannel )
|
||||
this._modify_directory_live(CreativeChannel);
|
||||
|
||||
var CSGOChannel = App.__container__.resolve('view:cs-go-channel');
|
||||
if ( CSGOChannel )
|
||||
this._modify_directory_live(CSGOChannel, true);
|
||||
|
||||
var HostView = App.__container__.resolve('view:host');
|
||||
if ( HostView )
|
||||
this._modify_directory_host(HostView);
|
||||
|
||||
var VideoView = App.__container__.resolve('view:video');
|
||||
if ( VideoView )
|
||||
this._modify_directory_video(VideoView);
|
||||
|
||||
// TODO: Process existing views.
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._modify_directory_video = function(dir) {
|
||||
var f = this;
|
||||
dir.reopen({
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
// Initialize existing views.
|
||||
for(var key in Ember.View.views) {
|
||||
var view = Ember.View.views[key];
|
||||
try {
|
||||
if ( (ChannelView && view instanceof ChannelView) || (CreativeChannel && view instanceof CreativeChannel) || (CSGOChannel && view instanceof CSGOChannel) || (HostView && view instanceof HostView) )
|
||||
view.ffzInit();
|
||||
} catch(err) {
|
||||
this.error("Directory Setup: " + err);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
dir.create().destroy();
|
||||
} catch(err) { }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._modify_directory_live = function(dir) {
|
||||
FFZ.prototype._modify_directory_live = function(dir, is_csgo) {
|
||||
var f = this;
|
||||
dir.reopen({
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
this.ffzInit();
|
||||
},
|
||||
|
||||
ffzInit: function() {
|
||||
var el = this.get('element'),
|
||||
meta = el && el.querySelector('.meta'),
|
||||
thumb = el && el.querySelector('.thumb'),
|
||||
cap = thumb && thumb.querySelector('.cap');
|
||||
|
||||
|
||||
if ( f.settings.stream_uptime && f.settings.stream_uptime < 3 && cap ) {
|
||||
// CSGO doesn't provide the actual uptime information...
|
||||
if ( !is_csgo && f.settings.stream_uptime && f.settings.stream_uptime < 3 && cap ) {
|
||||
var t_el = this._ffz_uptime = document.createElement('div');
|
||||
t_el.className = 'overlay_info length live';
|
||||
|
||||
jQuery(t_el).tipsy({html: true});
|
||||
jQuery(t_el).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 's')});
|
||||
|
||||
cap.appendChild(t_el);
|
||||
this._ffz_uptime_timer = setInterval(this.ffzUpdateUptime.bind(this), 1000);
|
||||
|
@ -88,6 +91,8 @@ FFZ.prototype._modify_directory_live = function(dir) {
|
|||
target = this.get('context.model.channel.name');
|
||||
|
||||
logo.className = 'profile-photo';
|
||||
logo.classList.toggle('is-csgo', is_csgo);
|
||||
|
||||
logo.src = this.get('context.model.channel.logo') || "http://static-cdn.jtvnw.net/jtv_user_pictures/xarth/404_user_150x150.png";
|
||||
logo.alt = this.get('context.model.channel.display_name');
|
||||
|
||||
|
|
318
src/ember/following.js
Normal file
318
src/ember/following.js
Normal file
|
@ -0,0 +1,318 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require('../utils'),
|
||||
constants = require('../constants');
|
||||
|
||||
|
||||
// --------------------
|
||||
// Settings
|
||||
// --------------------
|
||||
|
||||
FFZ.settings_info.enhance_profile_following = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
|
||||
category: "Appearance",
|
||||
name: "Enhanced Following Control",
|
||||
help: "Display additional controls on your own profile's Following tab to make management easier."
|
||||
}
|
||||
|
||||
|
||||
// --------------------
|
||||
// Initialization
|
||||
// --------------------
|
||||
|
||||
FFZ.prototype.setup_profile_following = function() {
|
||||
if ( ! window.App )
|
||||
return;
|
||||
|
||||
var f = this;
|
||||
|
||||
// Build our is-following cache.
|
||||
this._following_cache = {};
|
||||
|
||||
// First, we need to hook the model. This is what we'll use to grab the following notification state,
|
||||
// rather than making potentially hundreds of API requests.
|
||||
var Following = App.__container__.resolve('model:kraken-channel-following');
|
||||
if ( ! Following )
|
||||
return;
|
||||
|
||||
this._hook_following(Following);
|
||||
|
||||
// Also try hooking that other model.
|
||||
var Notification = App.__container__.resolve('model:notification');
|
||||
if ( Notification )
|
||||
this._hook_following(Notification, true);
|
||||
|
||||
|
||||
// Now, we need to edit the profile Following view itself.
|
||||
var ProfileView = App.__container__.resolve('view:channel/following');
|
||||
if ( ! ProfileView )
|
||||
return;
|
||||
|
||||
ProfileView.reopen({
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
try {
|
||||
this.ffzInit();
|
||||
} catch(err) {
|
||||
f.error("ProfileView ffzInit: " + err);
|
||||
}
|
||||
},
|
||||
|
||||
willClearRender: function() {
|
||||
try {
|
||||
this.ffzTeardown();
|
||||
} catch(err) {
|
||||
f.error("ProvileView ffzTeardown: " + err);
|
||||
}
|
||||
this._super();
|
||||
},
|
||||
|
||||
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') )
|
||||
return;
|
||||
|
||||
var el = this.get('element'),
|
||||
users = el && el.querySelectorAll('.user.item');
|
||||
|
||||
el.classList.add('ffz-enhanced-following');
|
||||
|
||||
var had_data = true;
|
||||
|
||||
if ( users && users.length )
|
||||
for(var i=0; i < users.length; i++)
|
||||
had_data = this.ffzProcessUser(users[i]) && had_data;
|
||||
else
|
||||
had_data = false;
|
||||
|
||||
if ( ! had_data ) {
|
||||
// Force a refresh.
|
||||
f.log("Forcing a refresh of user following data.");
|
||||
var following = this.get('context.following'),
|
||||
refresher = function() {
|
||||
if ( following.get('isLoading') )
|
||||
setTimeout(refresher, 25);
|
||||
|
||||
following.clear();
|
||||
following.load();
|
||||
}
|
||||
|
||||
// We use this weird function to prevent trying to load twice mucking things up.
|
||||
setTimeout(refresher);
|
||||
}
|
||||
|
||||
// Watch for new ones the bad way.
|
||||
if ( ! this._ffz_observer ) {
|
||||
var t = this;
|
||||
var observer = this._ffz_observer = new MutationObserver(function(mutations) {
|
||||
for(var i=0; i < mutations.length; i++) {
|
||||
var mutation = mutations[i];
|
||||
if ( mutation.type !== "childList" )
|
||||
continue;
|
||||
|
||||
for(var x=0; x < mutation.addedNodes.length; x++) {
|
||||
var added = mutation.addedNodes[x];
|
||||
if ( added.nodeType !== added.ELEMENT_NODE || added.tagName !== "DIV" )
|
||||
continue;
|
||||
|
||||
// Is it an ember-view? Check its kids.
|
||||
if ( added.classList.contains('ember-view') ) {
|
||||
var users = added.querySelectorAll('.user.item');
|
||||
if ( users )
|
||||
for(var y=0; y < users.length; y++)
|
||||
t.ffzProcessUser(users[y]);
|
||||
|
||||
} else if ( added.classList.contains('user') )
|
||||
t.ffzProcessUser(added);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(el, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
ffzTeardown: function() {
|
||||
if ( this._ffz_observer ) {
|
||||
this._ffz_observer.disconnect();
|
||||
this._ffz_observer = null;
|
||||
}
|
||||
},
|
||||
|
||||
ffzProcessUser: function(user) {
|
||||
if ( user.classList.contains('ffz-processed') )
|
||||
return true;
|
||||
|
||||
var link = user.querySelector('a'),
|
||||
link_parts = link && link.href.split("/"),
|
||||
user_id = link_parts && link_parts[3],
|
||||
data = f._following_cache[user_id],
|
||||
t_el = document.createElement('div');
|
||||
|
||||
user.classList.add('ffz-processed');
|
||||
if ( ! data )
|
||||
return false;
|
||||
|
||||
t_el.className = 'overlay_info length';
|
||||
jQuery(t_el).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 's')});
|
||||
|
||||
var age = data[0] ? Math.floor((Date.now() - data[0].getTime()) / 1000) : 0;
|
||||
if ( age ) {
|
||||
t_el.innerHTML = constants.CLOCK + ' ' + utils.human_time(age, 10);
|
||||
t_el.setAttribute('original-title', 'Following Since: <nobr>' + data[0].toLocaleString() + '</nobr>');
|
||||
} else
|
||||
t_el.style.display = 'none';
|
||||
|
||||
user.appendChild(t_el);
|
||||
|
||||
var actions = document.createElement('div'),
|
||||
follow = document.createElement('button'),
|
||||
notif = document.createElement('button'),
|
||||
|
||||
update_follow = function() {
|
||||
data = f._following_cache[user_id];
|
||||
user.classList.toggle('followed', data);
|
||||
follow.innerHTML = constants.HEART + constants.UNHEART + '<span> Follow</span>';
|
||||
|
||||
if ( t_el ) {
|
||||
var age = data && data[0] ? Math.floor((Date.now() - data[0].getTime()) / 1000) : undefined;
|
||||
if ( age !== undefined ) {
|
||||
t_el.innerHTML = constants.CLOCK + ' ' + (age < 60 ? 'now' : utils.human_time(age, 10));
|
||||
t_el.setAttribute('original-title', 'Following Since: <nobr>' + data[0].toLocaleString() + '</nobr>');
|
||||
t_el.style.display = '';
|
||||
} else {
|
||||
t_el.style.display = 'none';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
update_notif = function() {
|
||||
data = f._following_cache[user_id];
|
||||
notif.classList.toggle('notifications-on', data && data[1]);
|
||||
notif.textContent = 'Notification ' + (data && data[1] ? 'On' : 'Off');
|
||||
};
|
||||
|
||||
actions.className = 'actions';
|
||||
|
||||
follow.className = 'button follow';
|
||||
notif.className = 'button notifications';
|
||||
|
||||
update_follow();
|
||||
update_notif();
|
||||
|
||||
follow.addEventListener('click', function() {
|
||||
var was_following = !!data;
|
||||
|
||||
follow.disabled = true;
|
||||
notif.disabled = true;
|
||||
follow.textContent = 'Updating';
|
||||
|
||||
(was_following ?
|
||||
Twitch.api.del("users/:login/follows/channels/" + user_id) :
|
||||
Twitch.api.put("users/:login/follows/channels/" + user_id, {notifications: false}))
|
||||
.done(function() {
|
||||
data = f._following_cache[user_id] = was_following ? null : [new Date(), false];
|
||||
})
|
||||
.always(function() {
|
||||
update_follow();
|
||||
update_notif();
|
||||
follow.disabled = false;
|
||||
notif.disabled = false;
|
||||
})
|
||||
});
|
||||
|
||||
notif.addEventListener('click', function() {
|
||||
var was_following = data[1];
|
||||
|
||||
follow.disabled = true;
|
||||
notif.disabled = true;
|
||||
notif.textContent = 'Updating';
|
||||
|
||||
Twitch.api.put("users/:login/follows/channels/" + user_id, {notifications: !was_following})
|
||||
.done(function() {
|
||||
data[1] = ! was_following;
|
||||
})
|
||||
.always(function() {
|
||||
update_notif();
|
||||
follow.disabled = false;
|
||||
notif.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
actions.appendChild(follow);
|
||||
actions.appendChild(notif);
|
||||
user.appendChild(actions);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Now, rebuild any views.
|
||||
try {
|
||||
ProfileView.create().destroy();
|
||||
} catch(err) { }
|
||||
|
||||
for(var key in Ember.View.views) {
|
||||
var view = Ember.View.views[key];
|
||||
if ( ! view || !(view instanceof ProfileView) )
|
||||
continue;
|
||||
|
||||
this.log("Manually updating existing Following View.", view);
|
||||
try {
|
||||
var following = view.get('context.following');
|
||||
this._hook_following(following);
|
||||
} catch(err) {
|
||||
this.error("setup: view:channel/following: model hook: " + err);
|
||||
}
|
||||
|
||||
try {
|
||||
view.ffzInit();
|
||||
} catch(err) {
|
||||
this.error("setup: view:channel/following: " + err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._hook_following = function(Following) {
|
||||
var f = this;
|
||||
Following.reopen({
|
||||
apiLoad: function(e) {
|
||||
var user = f.get_user(),
|
||||
channel_id = this.get('id'),
|
||||
t = this;
|
||||
|
||||
if ( ! user || user.login !== channel_id )
|
||||
return this._super(e);
|
||||
|
||||
return new RSVP.Promise(function(success, fail) {
|
||||
t._super(e).then(function(data) {
|
||||
if ( data && data.follows ) {
|
||||
var now = Date.now();
|
||||
for(var i=0; i < data.follows.length; i++) {
|
||||
var follow = data.follows[i];
|
||||
if ( ! follow || ! follow.channel || ! follow.channel.name ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( follow.channel.display_name )
|
||||
FFZ.capitalization[follow.channel.name] = [follow.channel.display_name, now];
|
||||
|
||||
f._following_cache[follow.channel.name] = [follow.created_at ? utils.parse_date(follow.created_at) : null, follow.notifications || false];
|
||||
}
|
||||
}
|
||||
|
||||
success(data);
|
||||
|
||||
}, function(err) {
|
||||
fail(err);
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -47,6 +47,12 @@ FFZ.settings_info.portrait_mode = {
|
|||
}
|
||||
}
|
||||
|
||||
FFZ.settings_info.portrait_warning = {
|
||||
value: false,
|
||||
visible: false
|
||||
}
|
||||
|
||||
|
||||
FFZ.settings_info.swap_sidebars = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
|
@ -177,7 +183,7 @@ FFZ.prototype.setup_layout = function() {
|
|||
height = size[1];
|
||||
|
||||
// Make sure we have at least a bit of room for the chat.
|
||||
return this.get("windowHeight") < (height + 120 + 60 + 100);
|
||||
return this.get("windowHeight") < (height + 120 + 60 + 200);
|
||||
|
||||
} else
|
||||
return this.get("windowWidth") < (1090 - this.get('rightColumnWidth'))
|
||||
|
@ -217,27 +223,14 @@ FFZ.prototype.setup_layout = function() {
|
|||
return "<style>.dynamic-player, .dynamic-player object, .dynamic-player video{width:" + width + "px !important;height:" + height + "px !important} .dynamic-target-player,.dynamic-target-player object, .dynamic-target-player video{width:" + width + "px !important;height:" + host_height + "px !important}</style><style>.dynamic-player .player object{width:100% !important; height:100% !important}</style>";
|
||||
}.property("playerSize"),
|
||||
|
||||
/*ffzUpdateWidth: _.throttle(function() {
|
||||
var rc = document.querySelector('#right_close');
|
||||
if ( ! rc )
|
||||
ffzPortraitWarning: function() {
|
||||
if ( ! f.settings.portrait_mode || f._portrait_warning || f.settings.portrait_warning || ! this.get('isTooSmallForRightColumn') )
|
||||
return;
|
||||
|
||||
var left_width = this.get("isLeftColumnClosed") ? 50 : 240,
|
||||
right_width;
|
||||
f._portrait_warning = true;
|
||||
f.show_message('Twitch\'s Chat Sidebar has been hidden as a result of FrankerFaceZ\'s Portrait Mode because the window is too wide.<br><br>Please <a href="#" onclick="ffz.settings.set(\'portrait_mode\',0);jQuery(this).parents(\'.ffz-noty\').remove();ffz._portrait_warning = false;return false">disable Portrait Mode</a> or make your window narrower.<br><br><a href="#" onclick="ffz.settings.set(\'portrait_warning\',true);jQuery(this).parents(\'.ffz-noty\').remove();return false">Do not show this message again</a>');
|
||||
|
||||
if ( f.settings.swap_sidebars )
|
||||
right_width = rc.offsetLeft; // + this.get('rightColumnWidth') - 5;
|
||||
else
|
||||
right_width = document.body.offsetWidth - rc.offsetLeft - left_width - 25;
|
||||
|
||||
if ( right_width < 250 ) {
|
||||
// Close it!
|
||||
|
||||
}
|
||||
|
||||
this.set('rightColumnWidth', right_width);
|
||||
Ember.propertyDidChange(Layout, 'contentWidth');
|
||||
}, 200),*/
|
||||
}.observes("isTooSmallForRightColumn"),
|
||||
|
||||
ffzUpdateCss: function() {
|
||||
// TODO: Fix this mess of duplicate code.
|
||||
|
@ -332,6 +325,6 @@ FFZ.prototype.setup_layout = function() {
|
|||
Layout.set('rawPortraitMode', this.settings.portrait_mode);
|
||||
|
||||
// Force re-calculation of everything.
|
||||
Ember.propertyDidChange(Layout, 'contentWidth');
|
||||
Ember.propertyDidChange(Layout, 'windowWidth');
|
||||
Ember.propertyDidChange(Layout, 'windowHeight');
|
||||
}
|
|
@ -113,7 +113,7 @@ FFZ.settings_info.scrollback_length = {
|
|||
|
||||
for(var room_id in this.rooms) {
|
||||
var room = this.rooms[room_id];
|
||||
room.room.set('messageBufferSize', new_val + ((this._roomv && !this._roomv.get('stuckToBottom') && current_id === room_id) ? 150 : 0));
|
||||
room.room && room.room.set('messageBufferSize', new_val + ((this._roomv && !this._roomv.get('stuckToBottom') && current_id === room_id) ? 150 : 0));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -149,7 +149,6 @@ FFZ.settings_info.banned_words = {
|
|||
|
||||
category: "Chat Filtering",
|
||||
no_bttv: true,
|
||||
//visible: function() { return ! this.has_bttv },
|
||||
|
||||
name: "Banned Words",
|
||||
help: "Set a list of words that will be locally removed from chat messages.",
|
||||
|
@ -181,7 +180,6 @@ FFZ.settings_info.keywords = {
|
|||
|
||||
category: "Chat Filtering",
|
||||
no_bttv: true,
|
||||
//visible: function() { return ! this.has_bttv },
|
||||
|
||||
name: "Highlight Keywords",
|
||||
help: "Set additional keywords that will be highlighted in chat.",
|
||||
|
@ -246,6 +244,21 @@ FFZ.settings_info.link_image_hover = {
|
|||
};
|
||||
|
||||
|
||||
FFZ.settings_info.emote_image_hover = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
|
||||
category: "Chat Tooltips",
|
||||
no_mobile: true,
|
||||
|
||||
name: "Emote Preview",
|
||||
help: "Display scaled up high-DPI emoticon images in tooltips to help see details on low-resolution monitors.",
|
||||
on_update: function(val) {
|
||||
this._reset_tooltips();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
FFZ.settings_info.image_hover_all_domains = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
|
@ -269,7 +282,10 @@ FFZ.settings_info.chat_rows = {
|
|||
name: "Chat Line Backgrounds",
|
||||
help: "Display alternating background colors for lines in chat.",
|
||||
|
||||
on_update: function(val) { document.body.classList.toggle("ffz-chat-background", !this.has_bttv && val); }
|
||||
on_update: function(val) {
|
||||
this.toggle_style('chat-background', !this.has_bttv && val);
|
||||
this.toggle_style('chat-setup', !this.has_bttv && (val || this.settings.chat_separators));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -301,10 +317,12 @@ FFZ.settings_info.chat_separators = {
|
|||
help: "Display thin lines between chat messages for further visual separation.",
|
||||
|
||||
on_update: function(val) {
|
||||
document.body.classList.toggle("ffz-chat-separator", !this.has_bttv && val !== 0);
|
||||
document.body.classList.toggle("ffz-chat-separator-3d", !this.has_bttv && val === 2);
|
||||
document.body.classList.toggle("ffz-chat-separator-3d-inset", !this.has_bttv && val === 3);
|
||||
document.body.classList.toggle("ffz-chat-separator-wide", !this.has_bttv && val === 4);
|
||||
this.toggle_style('chat-setup', !this.has_bttv && (val || this.settings.chat_rows));
|
||||
|
||||
this.toggle_style('chat-separator', !this.has_bttv && val);
|
||||
this.toggle_style('chat-separator-3d', !this.has_bttv && val === 2);
|
||||
this.toggle_style('chat-separator-3d-inset', !this.has_bttv && val === 3);
|
||||
this.toggle_style('chat-separator-wide', !this.has_bttv && val === 4);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -319,7 +337,7 @@ FFZ.settings_info.chat_padding = {
|
|||
name: "Reduced Chat Line Padding",
|
||||
help: "Reduce the amount of padding around chat messages to fit more on-screen at once.",
|
||||
|
||||
on_update: function(val) { document.body.classList.toggle("ffz-chat-padding", !this.has_bttv && val); }
|
||||
on_update: function(val) { this.toggle_style('chat-padding', !this.has_bttv && val); }
|
||||
};
|
||||
|
||||
|
||||
|
@ -352,9 +370,9 @@ FFZ.settings_info.high_contrast_chat = {
|
|||
},
|
||||
|
||||
on_update: function(val) {
|
||||
document.body.classList.toggle("ffz-high-contrast-chat-text", !this.has_bttv && val[2] === '1');
|
||||
document.body.classList.toggle("ffz-high-contrast-chat-bold", !this.has_bttv && val[1] === '1');
|
||||
document.body.classList.toggle("ffz-high-contrast-chat-bg", !this.has_bttv && val[0] === '1');
|
||||
this.toggle_style('chat-hc-text', !this.has_bttv && val[2] === '1');
|
||||
this.toggle_style('chat-hc-bold', !this.has_bttv && val[1] === '1');
|
||||
this.toggle_style('chat-hc-background', !this.has_bttv && val[0] === '1');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -397,7 +415,7 @@ FFZ.settings_info.chat_font_family = {
|
|||
|
||||
var span = document.createElement('span');
|
||||
span.style.fontFamily = val;
|
||||
css = ".ember-chat .chat-messages {" + span.style.cssText + "}";
|
||||
css = ".timestamp-line,.conversation-chat-line,.conversation-system-messages,.chat-history,.ember-chat .chat-messages {" + span.style.cssText + "}";
|
||||
}
|
||||
|
||||
utils.update_css(this._chat_style, "chat_font_family", css);
|
||||
|
@ -439,7 +457,7 @@ FFZ.settings_info.chat_font_size = {
|
|||
else {
|
||||
var lh = Math.max(20, Math.round((20/12)*val)),
|
||||
pd = Math.floor((lh - 20) / 2);
|
||||
css = ".ember-chat .chat-messages .chat-line { font-size: " + val + "px !important; line-height: " + lh + "px !important; }";
|
||||
css = ".timestamp-line,.conversation-chat-line,.conversation-system-messages,.chat-history .chat-line,.ember-chat .chat-messages .chat-line { font-size: " + val + "px !important; line-height: " + lh + "px !important; }";
|
||||
if ( pd )
|
||||
css += ".ember-chat .chat-messages .chat-line .mod-icons, .ember-chat .chat-messages .chat-line .badges { padding-top: " + pd + "px; }";
|
||||
}
|
||||
|
@ -487,7 +505,7 @@ FFZ.settings_info.chat_ts_size = {
|
|||
css = "";
|
||||
else {
|
||||
var lh = Math.max(20, Math.round((20/12)*val), Math.round((20/12)*this.settings.chat_font_size));
|
||||
css = ".ember-chat .chat-messages .timestamp { font-size: " + val + "px !important; line-height: " + lh + "px !important; }";
|
||||
css = ".chat-history .timestamp,.ember-chat .chat-messages .timestamp { font-size: " + val + "px !important; line-height: " + lh + "px !important; }";
|
||||
}
|
||||
|
||||
utils.update_css(this._chat_style, "chat_ts_font_size", css);
|
||||
|
@ -526,19 +544,19 @@ FFZ.prototype.setup_line = function() {
|
|||
|
||||
|
||||
// Chat Enhancements
|
||||
document.body.classList.toggle("ffz-chat-colors", !this.has_bttv && this.settings.fix_color !== '-1');
|
||||
document.body.classList.toggle("ffz-chat-colors-gray", !this.has_bttv && this.settings.fix_color === '-1');
|
||||
this.toggle_style('chat-setup', !this.has_bttv && (this.settings.chat_rows || this.settings.chat_separators));
|
||||
this.toggle_style('chat-padding', !this.has_bttv && this.settings.chat_padding);
|
||||
|
||||
document.body.classList.toggle('ffz-chat-background', !this.has_bttv && this.settings.chat_rows);
|
||||
document.body.classList.toggle("ffz-chat-separator", !this.has_bttv && this.settings.chat_separators !== 0);
|
||||
document.body.classList.toggle("ffz-chat-separator-wide", !this.has_bttv && this.settings.chat_separators === 4);
|
||||
document.body.classList.toggle("ffz-chat-separator-3d", !this.has_bttv && this.settings.chat_separators === 2);
|
||||
document.body.classList.toggle("ffz-chat-separator-3d-inset", !this.has_bttv && this.settings.chat_separators === 3);
|
||||
document.body.classList.toggle("ffz-chat-padding", !this.has_bttv && this.settings.chat_padding);
|
||||
this.toggle_style('chat-background', !this.has_bttv && this.settings.chat_rows);
|
||||
|
||||
document.body.classList.toggle("ffz-high-contrast-chat-text", !this.has_bttv && this.settings.high_contrast_chat[2] === '1');
|
||||
document.body.classList.toggle("ffz-high-contrast-chat-bold", !this.has_bttv && this.settings.high_contrast_chat[1] === '1');
|
||||
document.body.classList.toggle("ffz-high-contrast-chat-bg", !this.has_bttv && this.settings.high_contrast_chat[0] === '1');
|
||||
this.toggle_style('chat-separator', !this.has_bttv && this.settings.chat_separators);
|
||||
this.toggle_style('chat-separator-3d', !this.has_bttv && this.settings.chat_separators === 2);
|
||||
this.toggle_style('chat-separator-3d-inset', !this.has_bttv && this.settings.chat_separators === 3);
|
||||
this.toggle_style('chat-separator-wide', !this.has_bttv && this.settings.chat_separators === 4);
|
||||
|
||||
this.toggle_style('chat-hc-text', !this.has_bttv && this.settings.high_contrast_chat[2] === '1');
|
||||
this.toggle_style('chat-hc-bold', !this.has_bttv && this.settings.high_contrast_chat[1] === '1');
|
||||
this.toggle_style('chat-hc-background', !this.has_bttv && this.settings.high_contrast_chat[0] === '1');
|
||||
|
||||
this._last_row = {};
|
||||
|
||||
|
@ -549,7 +567,6 @@ FFZ.prototype.setup_line = function() {
|
|||
this._modify_line(Whisper);
|
||||
|
||||
this.log("Hooking the Ember Message Line component.");
|
||||
|
||||
var Line = App.__container__.resolve('component:message-line');
|
||||
|
||||
if ( Line )
|
||||
|
@ -577,46 +594,13 @@ FFZ.prototype._modify_line = function(component) {
|
|||
|
||||
component.reopen({
|
||||
tokenizedMessage: function() {
|
||||
// Add our own step to the tokenization procedure.
|
||||
var tokens = this.get("msgObject.cachedTokens");
|
||||
if ( tokens )
|
||||
return tokens;
|
||||
|
||||
tokens = this._super();
|
||||
|
||||
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);
|
||||
|
||||
if ( f.settings.parse_emoji )
|
||||
tokens = f.tokenize_emoji(tokens);
|
||||
|
||||
// Store the capitalization.
|
||||
var display = this.get("msgObject.tags.display-name");
|
||||
if ( display && display.length )
|
||||
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;
|
||||
}
|
||||
try {
|
||||
return f.tokenize_chat_line(this.get('msgObject'));
|
||||
} catch(err) {
|
||||
f.error("chat-line tokenizedMessage: " + err);
|
||||
return this._super();
|
||||
}
|
||||
|
||||
var end = performance.now();
|
||||
if ( end - start > 5 )
|
||||
f.log("Tokenizing Message Took Too Long - " + (end-start) + "ms", tokens, false, true);
|
||||
|
||||
this.set("msgObject.cachedTokens", tokens);
|
||||
return tokens;
|
||||
|
||||
}.property("msgObject.message", "isChannelLinksDisabled", "currentUserNick", "msgObject.from", "msgObject.tags.emotes"),
|
||||
|
||||
ffzUpdated: Ember.observer("msgObject.ffz_deleted", "msgObject.ffz_old_messages", function() {
|
||||
|
@ -633,26 +617,16 @@ FFZ.prototype._modify_line = function(component) {
|
|||
if ( e.target && e.target.classList.contains('mod-icon') ) {
|
||||
jQuery(e.target).trigger('mouseout');
|
||||
|
||||
/*if ( e.target.classList.contains('purge') ) {
|
||||
var i = this.get('msgObject.from'),
|
||||
room_id = this.get('msgObject.room'),
|
||||
room = room_id && f.rooms[room_id] && f.rooms[room_id].room;
|
||||
|
||||
if ( room ) {
|
||||
room.send("/timeout " + i + " 1", true);
|
||||
room.clearMessages(i);
|
||||
}
|
||||
return;
|
||||
}*/
|
||||
|
||||
if ( e.target.classList.contains('custom') ) {
|
||||
var room_id = this.get('msgObject.room'),
|
||||
room = room_id && f.rooms[room_id] && f.rooms[room_id].room,
|
||||
|
||||
cmd = e.target.getAttribute('data-cmd');
|
||||
|
||||
if ( room ) {
|
||||
room.send(cmd, true);
|
||||
var lines = cmd.split("\n");
|
||||
for(var i=0; i < lines.length; i++)
|
||||
room.send(lines[i], true);
|
||||
|
||||
if ( e.target.classList.contains('is-timeout') )
|
||||
room.clearMessages(this.get('msgObject.from'));
|
||||
}
|
||||
|
@ -660,30 +634,8 @@ FFZ.prototype._modify_line = function(component) {
|
|||
}
|
||||
}
|
||||
|
||||
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons && e.target && e.target.classList.contains('emoticon') ) {
|
||||
var eid = e.target.getAttribute('data-emote');
|
||||
if ( eid )
|
||||
window.open("https://twitchemotes.com/emote/" + eid);
|
||||
else {
|
||||
eid = e.target.getAttribute("data-ffz-emote");
|
||||
var es = e.target.getAttribute("data-ffz-set"),
|
||||
set = es && f.emote_sets[es],
|
||||
url;
|
||||
|
||||
if ( ! set )
|
||||
return;
|
||||
|
||||
if ( set.hasOwnProperty('source_ext') ) {
|
||||
var api = f._apis[set.source_ext];
|
||||
if ( api && api.emote_url_generator )
|
||||
url = api.emote_url_generator(set.source_id, eid);
|
||||
} else
|
||||
url = "https://www.frankerfacez.com/emoticons/" + eid;
|
||||
|
||||
if ( url )
|
||||
window.open(url);
|
||||
}
|
||||
}
|
||||
if ( f._click_emote(e.target, e) )
|
||||
return;
|
||||
|
||||
return this._super(e);
|
||||
},
|
||||
|
@ -746,8 +698,8 @@ FFZ.prototype._modify_line = function(component) {
|
|||
|
||||
else {
|
||||
if ( typeof btn === "string" ) {
|
||||
cmd = btn.replace(/{user}/g, user);
|
||||
tip = 'Custom Command\n' + cmd;
|
||||
cmd = btn.replace(/{user}/g, user).replace(/ *<LINE> */, "\n");
|
||||
tip = 'Custom Command' + (cmd.indexOf('\n') !== -1 ? 's' : '') + '\n' + cmd;
|
||||
} else {
|
||||
cmd = "/timeout " + user + " " + btn;
|
||||
tip = "Timeout User (" + utils.duration_string(btn) + ")";
|
||||
|
@ -768,7 +720,7 @@ FFZ.prototype._modify_line = function(component) {
|
|||
else if ( this.get('isAdmin') )
|
||||
badges[0] = {klass: 'admin', title: 'Admin'};
|
||||
else if ( this.get('isGlobalMod') )
|
||||
badges[0] = {klass: 'global-mod', title: 'Global Moderator'};
|
||||
badges[0] = {klass: 'global-moderator', title: 'Global Moderator'};
|
||||
else if ( ! is_whisper && this.get('isModerator') )
|
||||
badges[0] = {klass: 'moderator', title: 'Moderator'};
|
||||
|
||||
|
@ -835,7 +787,7 @@ FFZ.prototype._modify_line = function(component) {
|
|||
if ( deleted )
|
||||
e.push('<span class="deleted"><a class="undelete" href="#"><message deleted></a></span>');
|
||||
else {
|
||||
e.push('<span class="message' + colored + '" style="' + style + '">');
|
||||
e.push('<span class="message' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">');
|
||||
e.push(f.render_tokens(this.get('tokenizedMessage'), true));
|
||||
|
||||
var old_messages = this.get('msgObject.ffz_old_messages');
|
||||
|
@ -847,7 +799,6 @@ FFZ.prototype._modify_line = function(component) {
|
|||
},
|
||||
|
||||
classNameBindings: [
|
||||
'msgObject.ffz_alternate:ffz-alternate',
|
||||
'msgObject.ffz_has_mention:ffz-mentioned',
|
||||
'ffzWasDeleted:ffz-deleted',
|
||||
'ffzHasOldMessages:clearfix',
|
||||
|
|
|
@ -182,7 +182,7 @@ FFZ.settings_info.mod_buttons = {
|
|||
old_val += ' ' + prefix + cmd;
|
||||
}
|
||||
|
||||
var new_val = prompt("Custom In-Line Moderation Icons\n\nPlease enter a list of commands to be made available as mod icons within chat lines. Commands are separated by spaces. To include spaces in a command, surround the command with double quotes (\"). Use \"{user}\" to insert the user's username into the command, otherwise it will be appended to the end.\n\nExample: !permit \"!reg add {user}\"\n\nNumeric values will become timeout buttons for that number of seconds. The text \"<BAN>\" is a special value that will act like the normal Ban button in chat.\n\nTo assign a specific letter for use as the icon, specify it at the start of the command followed by an equals sign.\n\nExample: A=\"!reg add\"\n\nDefault: <BAN> 600", old_val);
|
||||
var new_val = prompt("Custom In-Line Moderation Icons\n\nPlease enter a list of commands to be made available as mod icons within chat lines. Commands are separated by spaces. To include spaces in a command, surround the command with double quotes (\"). Use \"{user}\" to insert the user's username into the command, otherwise it will be appended to the end.\n\nExample: !permit \"!reg add {user}\"\n\nTo send multiple commands, separate them with \"<LINE>\".\n\nNumeric values will become timeout buttons for that number of seconds. The text \"<BAN>\" is a special value that will act like the normal Ban button in chat.\n\nTo assign a specific letter for use as the icon, specify it at the start of the command followed by an equals sign.\n\nExample: A=\"!reg add\"\n\nDefault: <BAN> 600", old_val);
|
||||
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
@ -253,8 +253,15 @@ FFZ.settings_info.mod_buttons = {
|
|||
} else
|
||||
had_prefix = true;
|
||||
|
||||
if ( typeof val === "string" && val.indexOf('{user}') === -1 )
|
||||
val += ' {user}';
|
||||
if ( typeof val === "string" ) {
|
||||
// Split it up for this step.
|
||||
var lines = val.split(/ *<LINE> */);
|
||||
for(var x=0; x < lines.length; x++) {
|
||||
if ( lines[x].indexOf('{user}') === -1 )
|
||||
lines[x] += ' {user}';
|
||||
}
|
||||
val = lines.join("<LINE>");
|
||||
}
|
||||
|
||||
final.push([prefix, val, had_prefix]);
|
||||
}
|
||||
|
@ -435,13 +442,20 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
return alias || this.get("cardInfo.user.display_name") || user_id.capitalize();
|
||||
}),
|
||||
|
||||
willDestroy: function() {
|
||||
if ( f._mod_card === this )
|
||||
f._mod_card = undefined;
|
||||
this._super();
|
||||
},
|
||||
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
window._card = this;
|
||||
try {
|
||||
if ( f.has_bttv )
|
||||
return;
|
||||
|
||||
f._mod_card = this;
|
||||
|
||||
var el = this.get('element'),
|
||||
controller = this.get('controller'),
|
||||
line,
|
||||
|
@ -450,11 +464,14 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
|
||||
chat = window.App && App.__container__.lookup('controller:chat'),
|
||||
user = f.get_user(),
|
||||
is_broadcaster = user && chat && chat.get('currentRoom.id') === user.login,
|
||||
room_id = chat && chat.get('currentRoom.id'),
|
||||
is_broadcaster = user && room_id === user.login,
|
||||
|
||||
user_id = controller.get('cardInfo.user.id'),
|
||||
alias = f.aliases[user_id];
|
||||
|
||||
this.ffz_room_id = room_id;
|
||||
|
||||
// Alias Display
|
||||
if ( alias ) {
|
||||
var name = el.querySelector('h3.name'),
|
||||
|
@ -465,7 +482,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
if ( name ) {
|
||||
name.classList.add('ffz-alias');
|
||||
name.title = utils.sanitize(controller.get('cardInfo.user.display_name') || user_id.capitalize());
|
||||
jQuery(name).tipsy();
|
||||
jQuery(name).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -512,7 +529,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
btn.innerHTML = utils.sanitize(title);
|
||||
btn.title = utils.sanitize(cmd.replace(/{user}/g, controller.get('cardInfo.user.id') || '{user}'));
|
||||
|
||||
jQuery(btn).tipsy();
|
||||
jQuery(btn).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
btn.addEventListener('click', add_btn_click.bind(this, cmd));
|
||||
return btn;
|
||||
};
|
||||
|
@ -591,7 +608,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
else if ( f.settings.mod_card_hotkeys && timeout === 1 )
|
||||
btn.title = "(P)urge - " + btn.title;
|
||||
|
||||
jQuery(btn).tipsy();
|
||||
jQuery(btn).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
|
||||
btn.addEventListener('click', btn_click.bind(this, timeout));
|
||||
return btn;
|
||||
|
@ -627,7 +644,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
unban_btn.innerHTML = CHECK;
|
||||
unban_btn.title = (f.settings.mod_card_hotkeys ? "(U)" : "U") + "nban User";
|
||||
|
||||
jQuery(unban_btn).tipsy();
|
||||
jQuery(unban_btn).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
unban_btn.addEventListener("click", btn_click.bind(this, -1));
|
||||
|
||||
jQuery(ban_btn).after(unban_btn);
|
||||
|
@ -651,7 +668,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
msg_btn.classList.add('message');
|
||||
|
||||
msg_btn.title = "Whisper User";
|
||||
jQuery(msg_btn).tipsy();
|
||||
jQuery(msg_btn).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
|
||||
|
||||
var real_msg = document.createElement('button');
|
||||
|
@ -714,41 +731,82 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
if ( f.settings.mod_card_history ) {
|
||||
var Chat = App.__container__.lookup('controller:chat'),
|
||||
room = Chat && Chat.get('currentRoom'),
|
||||
ffz_room = room && f.rooms && f.rooms[room.get('id')],
|
||||
user_history = ffz_room && ffz_room.user_history && ffz_room.user_history[controller.get('cardInfo.user.id')];
|
||||
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] || [],
|
||||
|
||||
if ( user_history && user_history.length ) {
|
||||
var history = document.createElement('ul'),
|
||||
alternate = false;
|
||||
history.className = 'interface clearfix chat-history';
|
||||
history = document.createElement('ul');
|
||||
|
||||
for(var i=0; i < user_history.length; i++) {
|
||||
var line = user_history[i],
|
||||
l_el = document.createElement('li');
|
||||
history.className = 'interface clearfix chat-history';
|
||||
|
||||
l_el.className = 'message-line chat-line clearfix';
|
||||
l_el.classList.toggle('ffz-alternate', alternate);
|
||||
alternate = !alternate;
|
||||
if ( user_history.length < 20 ) {
|
||||
var before = user_history.length > 0 ? user_history[0].date.getTime() : Date.now();
|
||||
f.ws_send("user_history", [room_id, user_id, 50 - user_history.length], function(success, data) {
|
||||
if ( ! success )
|
||||
return;
|
||||
|
||||
if ( line.style )
|
||||
l_el.classList.add(line.style);
|
||||
var i = data.length,
|
||||
was_at_top = history && history.scrollTop >= (history.scrollHeight - history.clientHeight),
|
||||
first = true;
|
||||
|
||||
l_el.innerHTML = (helpers ? '<span class="timestamp float-left">' + helpers.getTime(line.date) + '</span> ' : '') + '<span class="message">' + (line.style === 'action' ? '*' + line.from + ' ' : '') + f.render_tokens(line.cachedTokens) + '</span>';
|
||||
while(i--) {
|
||||
var msg = data[i];
|
||||
if ( ! msg )
|
||||
continue;
|
||||
|
||||
// Banned Links
|
||||
var bad_links = l_el.querySelectorAll('a.deleted-link');
|
||||
for(var x=0; x < bad_links.length; x++)
|
||||
bad_links[x].addEventListener("click", f._deleted_link_click);
|
||||
if ( typeof msg.date === "string" || typeof msg.date === "number" )
|
||||
msg.date = utils.parse_date(msg.date);
|
||||
|
||||
jQuery('.html-tooltip', l_el).tipsy({html:true});
|
||||
history.appendChild(l_el);
|
||||
}
|
||||
if ( ! msg.date || msg.date.getTime() >= before )
|
||||
continue;
|
||||
|
||||
el.appendChild(history);
|
||||
if ( first ) {
|
||||
first = false;
|
||||
history.insertBefore(f._build_mod_card_history({
|
||||
date: msg.date,
|
||||
from: "jtv",
|
||||
style: "admin",
|
||||
cachedTokens: ["(Server History Above)"]
|
||||
}), history.firstElementChild);
|
||||
}
|
||||
|
||||
// Lazy scroll-to-bottom
|
||||
history.scrollTop = history.scrollHeight;
|
||||
if ( ! msg.style ) {
|
||||
if ( msg.from === "jtv" )
|
||||
msg.style = "admin";
|
||||
else if ( msg.from === "twitchnotify" )
|
||||
msg.style = "notification";
|
||||
}
|
||||
|
||||
if ( msg.tags && typeof msg.tags.emotes === "string" )
|
||||
try {
|
||||
msg.tags.emotes = JSON.parse(msg.tags.emotes);
|
||||
} catch(err) {
|
||||
f.log("Error Parsing JSON Emotes: " + err);
|
||||
msg.tags.emotes = {};
|
||||
}
|
||||
|
||||
if ( ! msg.cachedTokens || ! msg.cachedTokens.length )
|
||||
f.tokenize_chat_line(msg, true, room.get('roomProperties.hide_chat_links'));
|
||||
|
||||
history.insertBefore(f._build_mod_card_history(msg), history.firstElementChild);
|
||||
if ( history.childElementCount >= 50 )
|
||||
break;
|
||||
}
|
||||
|
||||
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]));
|
||||
|
||||
el.appendChild(history);
|
||||
|
||||
// Lazy scroll-to-bottom
|
||||
history.scrollTop = history.scrollHeight;
|
||||
}
|
||||
|
||||
// Reposition the menu if it's off-screen.
|
||||
|
@ -778,6 +836,29 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
}
|
||||
|
||||
|
||||
FFZ.prototype._build_mod_card_history = function(line) {
|
||||
var l_el = document.createElement('li'),
|
||||
f = this;
|
||||
|
||||
l_el.className = 'message-line chat-line clearfix';
|
||||
|
||||
if ( line.ffz_has_mention )
|
||||
l_el.classList.add('ffz-mentioned');
|
||||
|
||||
if ( line.style )
|
||||
l_el.classList.add(line.style);
|
||||
|
||||
l_el.innerHTML = (helpers ? '<span class="timestamp float-left">' + helpers.getTime(line.date) + '</span> ' : '') + '<span class="message">' + (line.style === 'action' ? '*' + line.from + ' ' : '') + f.render_tokens(line.cachedTokens) + '</span>';
|
||||
|
||||
// Interactivity
|
||||
jQuery('a.deleted-link', l_el).click(f._deleted_link_click);
|
||||
jQuery('img.emoticon', l_el).click(function(e) { f._click_emote(this, e) });
|
||||
jQuery('.html-tooltip', l_el).tipsy({html:true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 's')});
|
||||
|
||||
return l_el;
|
||||
}
|
||||
|
||||
|
||||
// ----------------
|
||||
// Aliases
|
||||
// ----------------
|
||||
|
|
|
@ -61,7 +61,7 @@ FFZ.prototype.setup_player = function() {
|
|||
|
||||
this.players = {};
|
||||
|
||||
var Player2 = App && App.__container__.resolve('component:twitch-player2');
|
||||
var Player2 = window.App && App.__container__.resolve('component:twitch-player2');
|
||||
if ( ! Player2 )
|
||||
return this.log("Unable to find twitch-player2 component.");
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ var FFZ = window.FrankerFaceZ,
|
|||
HOSTED_SUB = / subscribed to /,
|
||||
constants = require('../constants'),
|
||||
utils = require('../utils'),
|
||||
helpers,
|
||||
|
||||
// StrimBagZ Support
|
||||
is_android = navigator.userAgent.indexOf('Android') !== -1,
|
||||
|
@ -14,7 +15,12 @@ var FFZ = window.FrankerFaceZ,
|
|||
return "";
|
||||
|
||||
return '.chat-line[data-room="' + room.id + '"] .badges .moderator:not(.ffz-badge-replacement) { background-image:url("' + room.moderator_badge + '") !important; }';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
helpers = window.require && window.require("ember-twitch-chat/helpers/chat-line-helpers");
|
||||
} catch(err) { }
|
||||
|
||||
|
||||
// --------------------
|
||||
|
@ -128,6 +134,15 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
this._super();
|
||||
},
|
||||
|
||||
ffzAlternate: function() {
|
||||
/*if ( ! this._ffz_chat_display ) {
|
||||
var el = this.get('element');
|
||||
this._ffz_chat_display = el && el.querySelector('ul.chat-lines');
|
||||
}
|
||||
|
||||
this._ffz_chat_display && this._ffz_chat_display.classList.toggle('ffz-should-alternate');*/
|
||||
},
|
||||
|
||||
ffzInit: function() {
|
||||
f._roomv = this;
|
||||
|
||||
|
@ -173,6 +188,9 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
if ( f._roomv === this )
|
||||
f._roomv = undefined;
|
||||
|
||||
if ( this._ffz_chat_display )
|
||||
this._ffz_chat_display = undefined;
|
||||
|
||||
this.ffzDisableFreeze();
|
||||
},
|
||||
|
||||
|
@ -361,12 +379,12 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
}
|
||||
},
|
||||
|
||||
ffzUnfreeze: function() {
|
||||
ffzUnfreeze: function(from_stuck) {
|
||||
this.ffz_frozen = false;
|
||||
this._ffz_last_move = 0;
|
||||
this.ffzUnwarnPaused();
|
||||
|
||||
if ( this.get('stuckToBottom') )
|
||||
if ( ! from_stuck && this.get('stuckToBottom') )
|
||||
this._scrollToBottom();
|
||||
},
|
||||
|
||||
|
@ -428,7 +446,7 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
this.set("stuckToBottom", val);
|
||||
this.get("controller.model") && this.set("controller.model.messageBufferSize", f.settings.scrollback_length + (val ? 0 : 150));
|
||||
if ( ! val )
|
||||
this.ffzUnfreeze();
|
||||
this.ffzUnfreeze(true);
|
||||
},
|
||||
|
||||
// Warnings~!
|
||||
|
@ -722,28 +740,29 @@ FFZ.prototype._insert_history = function(room_id, data) {
|
|||
tmiSession = r.tmiSession || (TMI._sessions && TMI._sessions[0]),
|
||||
tmiRoom = r.tmiRoom,
|
||||
|
||||
removed = 0,
|
||||
inserted = 0,
|
||||
purged = {},
|
||||
|
||||
last_msg = data[data.length - 1],
|
||||
now = new Date(),
|
||||
last_date = typeof last_msg.date === "string" ? utils.parse_date(last_msg.date) : last_msg.date,
|
||||
last_date = (typeof last_msg.date === "string" || typeof last_msg.date === "number") ? (last_msg.date = utils.parse_date(last_msg.date)) : last_msg.date,
|
||||
age = (now - last_date) / 1000,
|
||||
is_old = age > 300,
|
||||
|
||||
i = data.length,
|
||||
alternation = r.get('messages.0.ffz_alternate') || false;
|
||||
|
||||
if ( is_old )
|
||||
alternation = ! alternation;
|
||||
i = data.length;
|
||||
|
||||
var i = data.length;
|
||||
while(i--) {
|
||||
var msg = data[i];
|
||||
var msg = data[i],
|
||||
is_deleted = msg.ffz_deleted = purged[msg.from] || false;
|
||||
|
||||
if ( typeof msg.date === "string" )
|
||||
if ( is_deleted && ! this.settings.prevent_clear )
|
||||
msg.deleted = true;
|
||||
|
||||
if ( typeof msg.date === "string" || typeof msg.date === "number" )
|
||||
msg.date = utils.parse_date(msg.date);
|
||||
|
||||
msg.ffz_alternate = alternation = ! alternation;
|
||||
if ( ! msg.room )
|
||||
msg.room = room_id;
|
||||
|
||||
|
@ -774,6 +793,14 @@ FFZ.prototype._insert_history = function(room_id, data) {
|
|||
msg.style = "notification";
|
||||
}
|
||||
|
||||
if ( msg.tags && typeof msg.tags.emotes === "string" )
|
||||
try {
|
||||
msg.tags.emotes = JSON.parse(msg.tags.emotes);
|
||||
} catch(err) {
|
||||
f.log("Error Parsing JSON Emotes: " + err);
|
||||
msg.tags.emotes = {};
|
||||
}
|
||||
|
||||
if ( ! msg.cachedTokens || ! msg.cachedTokens.length )
|
||||
this.tokenize_chat_line(msg, true, r.get('roomProperties.hide_chat_links'));
|
||||
|
||||
|
@ -791,11 +818,18 @@ FFZ.prototype._insert_history = function(room_id, data) {
|
|||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
// If there was a CLEARCHAT, stop processing.
|
||||
if ( msg.tags && msg.tags.target === '@@' )
|
||||
break;
|
||||
|
||||
// If there was a purge, just track the name.
|
||||
else if ( msg.tags && msg.tags.target )
|
||||
purged[msg.tags.target] = true;
|
||||
}
|
||||
|
||||
if ( is_old ) {
|
||||
var msg = {
|
||||
ffz_alternate: ! alternation,
|
||||
color: "#755000",
|
||||
date: new Date(),
|
||||
from: "frankerfacez_admin",
|
||||
|
@ -807,10 +841,15 @@ FFZ.prototype._insert_history = function(room_id, data) {
|
|||
this.tokenize_chat_line(msg, true, r.get('roomProperties.hide_chat_links'));
|
||||
if ( r.shouldShowMessage(msg) ) {
|
||||
messages.insertAt(inserted, msg);
|
||||
while( messages.length > r.get('messageBufferSize') )
|
||||
while( messages.length > r.get('messageBufferSize') ) {
|
||||
messages.removeAt(0);
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( (removed % 2) && this._roomv && this._roomv.get('context.model.id') === room_id )
|
||||
this._roomv.ffzAlternate();
|
||||
}
|
||||
|
||||
|
||||
|
@ -975,7 +1014,7 @@ FFZ.prototype._modify_room = function(room) {
|
|||
var msgs = t.get('messages'),
|
||||
total = msgs.get('length'),
|
||||
i = total,
|
||||
alternate;
|
||||
removed = 0;
|
||||
|
||||
// Delete visible messages
|
||||
while(i--) {
|
||||
|
@ -983,9 +1022,8 @@ FFZ.prototype._modify_room = function(room) {
|
|||
|
||||
if ( msg.from === user ) {
|
||||
if ( f.settings.remove_deleted ) {
|
||||
if ( alternate === undefined )
|
||||
alternate = ! msg.ffz_alternate;
|
||||
msgs.removeAt(i);
|
||||
removed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -993,15 +1031,11 @@ FFZ.prototype._modify_room = function(room) {
|
|||
if ( ! f.settings.prevent_clear )
|
||||
t.set('messages.' + i + '.deleted', true);
|
||||
}
|
||||
|
||||
if ( alternate === undefined )
|
||||
alternate = msg.ffz_alternate;
|
||||
else {
|
||||
alternate = ! alternate;
|
||||
t.set('messages.' + i + '.ffz_alternate', alternate);
|
||||
}
|
||||
}
|
||||
|
||||
if ( (removed % 2) && f._roomv && f._roomv.get('context.model.id') === this.get('id') )
|
||||
f._roomv.ffzAlternate();
|
||||
|
||||
// Delete pending messages
|
||||
if (t.ffzPending) {
|
||||
msgs = t.ffzPending;
|
||||
|
@ -1053,8 +1087,11 @@ FFZ.prototype._modify_room = function(room) {
|
|||
len = messages.get("length"),
|
||||
limit = this.get("messageBufferSize");
|
||||
|
||||
if ( len > limit )
|
||||
if ( len > limit ) {
|
||||
messages.removeAt(0, len - limit);
|
||||
if ( ((len - limit) % 2) && f._roomv && f._roomv.get('context.model.id') === this.get('id') )
|
||||
f._roomv.ffzAlternate();
|
||||
}
|
||||
},
|
||||
|
||||
// Artificial chat delay
|
||||
|
@ -1075,13 +1112,6 @@ FFZ.prototype._modify_room = function(room) {
|
|||
|
||||
ffzActualPushMessage: function (msg) {
|
||||
if ( this.shouldShowMessage(msg) && this.ffzShouldShowMessage(msg) ) {
|
||||
var row_type = msg.ffz_alternate;
|
||||
if ( row_type === undefined ) {
|
||||
var room_id = this.get('id');
|
||||
row_type = f._last_row[room_id] = f._last_row.hasOwnProperty(room_id) ? !f._last_row[room_id] : false;
|
||||
msg.ffz_alternate = row_type;
|
||||
}
|
||||
|
||||
this.get("messages").pushObject(msg);
|
||||
this.trimMessages();
|
||||
|
||||
|
@ -1155,10 +1185,10 @@ FFZ.prototype._modify_room = function(room) {
|
|||
return;
|
||||
|
||||
var is_whisper = msg.style === 'whisper';
|
||||
if ( f.settings.group_tabs && f.settings.whisper_room ) {
|
||||
if ( ( is_whisper && ! this.ffz_whisper_room ) || ( ! is_whisper && this.ffz_whisper_room ) )
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore whispers if conversations are enabled.
|
||||
if ( is_whisper && App.__container__.lookup('route:application').controller.get('isConversationsEnabled') )
|
||||
return;
|
||||
|
||||
if ( ! is_whisper )
|
||||
msg.room = this.get('id');
|
||||
|
@ -1190,6 +1220,22 @@ FFZ.prototype._modify_room = function(room) {
|
|||
|
||||
if ( user_history.length > 20 )
|
||||
user_history.shift();
|
||||
|
||||
if ( f._mod_card && f._mod_card.ffz_room_id === msg.room && f._mod_card.get('cardInfo.user.id') === msg.from ) {
|
||||
var el = f._mod_card.get('element'),
|
||||
history = el && el.querySelector('.chat-history'),
|
||||
was_at_top = history && history.scrollTop >= (history.scrollHeight - history.clientHeight);
|
||||
|
||||
if ( history ) {
|
||||
history.appendChild(f._build_mod_card_history(msg));
|
||||
if ( was_at_top )
|
||||
setTimeout(function() { history.scrollTop = history.scrollHeight; })
|
||||
|
||||
// Don't do infinite scrollback.
|
||||
if ( history.childElementCount > 50 )
|
||||
history.removeChild(history.firstElementChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
159
src/emoticons.js
159
src/emoticons.js
|
@ -4,46 +4,11 @@ var FFZ = window.FrankerFaceZ,
|
|||
constants = require('./constants'),
|
||||
utils = require('./utils'),
|
||||
|
||||
|
||||
/*check_margins = function(margins, height) {
|
||||
var mlist = margins.split(/ +/);
|
||||
if ( mlist.length != 2 )
|
||||
return margins;
|
||||
|
||||
mlist[0] = parseFloat(mlist[0]);
|
||||
mlist[1] = parseFloat(mlist[1]);
|
||||
|
||||
if ( mlist[0] == (height - 18) / -2 && mlist[1] == 0 )
|
||||
return null;
|
||||
|
||||
return margins;
|
||||
},
|
||||
|
||||
|
||||
build_legacy_css = function(emote) {
|
||||
var margin = emote.margins, srcset = "";
|
||||
if ( ! margin )
|
||||
margin = ((emote.height - 18) / -2) + "px 0";
|
||||
|
||||
if ( emote.urls[2] || emote.urls[4] ) {
|
||||
srcset = 'url("' + emote.urls[1] + '") 1x';
|
||||
if ( emote.urls[2] )
|
||||
srcset += ', url("' + emote.urls[2] + '") 2x';
|
||||
if ( emote.urls[4] )
|
||||
srcset += ', url("' + emote.urls[4] + '") 4x';
|
||||
|
||||
srcset = '-webkit-image-set(' + srcset + '); image-set(' + srcset + ');';
|
||||
}
|
||||
|
||||
return ".ffz-emote-" + emote.id + ' { background-image: url("' + emote.urls[1] + '"); height: ' + emote.height + "px; width: " + emote.width + "px; margin: " + margin + (srcset ? '; ' + srcset : '') + (emote.css ? "; " + emote.css : "") + "}\n";
|
||||
},*/
|
||||
|
||||
|
||||
build_css = function(emote) {
|
||||
if ( ! emote.margins && ! emote.css )
|
||||
return ""; //build_legacy_css(emote);
|
||||
return "";
|
||||
|
||||
return /*build_legacy_css(emote) +*/ 'img[src="' + emote.urls[1] + '"] { ' + (emote.margins ? "margin: " + emote.margins + ";" : "") + (emote.css || "") + " }\n";
|
||||
return 'img[src="' + emote.urls[1] + '"] { ' + (emote.margins ? "margin: " + emote.margins + ";" : "") + (emote.css || "") + " }\n";
|
||||
},
|
||||
|
||||
|
||||
|
@ -120,8 +85,15 @@ FFZ.prototype.setup_emoticons = function() {
|
|||
// Emote Usage
|
||||
// ------------------------
|
||||
|
||||
FFZ.prototype.add_usage = function(room_id, emote_id, count) {
|
||||
var rooms = this.emote_usage[emote_id] = this.emote_usage[emote_id] || {};
|
||||
FFZ.prototype.add_usage = function(room_id, emote, count) {
|
||||
// Only report usage from FFZ emotes. Not extensions to FFZ.
|
||||
var emote_set = this.emote_sets[emote.set_id];
|
||||
if ( ! emote_set || emote_set.source_ext )
|
||||
return;
|
||||
|
||||
var emote_id = emote.id,
|
||||
rooms = this.emote_usage[emote_id] = this.emote_usage[emote_id] || {};
|
||||
|
||||
rooms[room_id] = (rooms[room_id] || 0) + (count || 1);
|
||||
|
||||
if ( this._emote_report_scheduled )
|
||||
|
@ -141,6 +113,41 @@ FFZ.prototype._report_emotes = function() {
|
|||
}
|
||||
|
||||
|
||||
// ------------------------
|
||||
// Emote Click Handler
|
||||
// ------------------------
|
||||
|
||||
FFZ.prototype._click_emote = function(target, event) {
|
||||
if ( ! this.settings.clickable_emoticons || (event && !((event.shiftKey || event.shiftLeft) && target && target.classList.contains('emoticon'))) )
|
||||
return;
|
||||
|
||||
var eid = target.getAttribute('data-emote');
|
||||
if ( eid )
|
||||
window.open("https://twitchemotes.com/emote/" + eid);
|
||||
else {
|
||||
eid = target.getAttribute("data-ffz-emote");
|
||||
var es = target.getAttribute("data-ffz-set"),
|
||||
emote_set = es && this.emote_sets[es],
|
||||
url;
|
||||
|
||||
if ( ! emote_set )
|
||||
return;
|
||||
|
||||
if ( emote_set.hasOwnProperty('source_ext') ) {
|
||||
var api = this._apis[emote_set.source_ext];
|
||||
if ( api && api.emote_url_generator )
|
||||
url = api.emote_url_generator(emote_set.source_id, eid);
|
||||
} else
|
||||
url = "https://www.frankerfacez.com/emoticons/" + eid;
|
||||
|
||||
if ( url ) {
|
||||
window.open(url);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------
|
||||
// Twitch Emoticon Checker
|
||||
// ------------------------
|
||||
|
@ -225,12 +232,82 @@ FFZ.prototype._emote_tooltip = function(emote) {
|
|||
var set = this.emote_sets[emote.set_id],
|
||||
owner = emote.owner,
|
||||
title = set && set.title || "Global",
|
||||
source = set && set.source || "FFZ";
|
||||
source = set && set.source || "FFZ",
|
||||
|
||||
emote._tooltip = "Emoticon: " + (emote.hidden ? "???" : emote.name) + "\n" + source + " " + title + (owner ? "\nBy: " + owner.display_name : "");
|
||||
preview_url = this.settings.emote_image_hover ? (emote.urls[4] || emote.urls[2]) : null,
|
||||
image = preview_url ? '<img class="emoticon ffz-image-hover" src="' + preview_url + '?_=preview">' : '';
|
||||
|
||||
emote._tooltip = image + "Emoticon: " + (emote.hidden ? "???" : emote.name) + "<br>" + source + " " + title + (owner ? "<br>By: " + owner.display_name : "");
|
||||
return emote._tooltip;
|
||||
}
|
||||
|
||||
FFZ.prototype._reset_tooltips = function(twitch_only) {
|
||||
for(var emote_id in this._twitch_emotes) {
|
||||
var data = this._twitch_emotes[emote_id];
|
||||
if ( data && data.tooltip )
|
||||
data.tooltip = null;
|
||||
}
|
||||
|
||||
if ( ! twitch_only ) {
|
||||
for(var set_id in this.emote_sets) {
|
||||
var emote_set = this.emote_sets[set_id];
|
||||
for(var emote_id in emote_set.emoticons) {
|
||||
var emote = emote_set.emoticons[emote_id];
|
||||
if ( emote._tooltip )
|
||||
emote._tooltip = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var emotes = document.querySelectorAll('img.emoticon');
|
||||
for(var i=0; i < emotes.length; i++) {
|
||||
var emote = emotes[i];
|
||||
if ( emote.classList.contains('ffz-image-hover') )
|
||||
continue;
|
||||
|
||||
var set_id,
|
||||
emote_id = emote.getAttribute('data-emote');
|
||||
|
||||
if ( emote_id ) {
|
||||
// Twitch Emotes
|
||||
if ( this.has_bttv )
|
||||
continue;
|
||||
|
||||
emote.setAttribute('original-title', utils.build_tooltip.bind(this)(emote_id, false, emote.alt));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( twitch_only )
|
||||
continue;
|
||||
|
||||
// FFZ Emoji
|
||||
emote_id = emote.getAttribute('data-ffz-emoji');
|
||||
if ( emote_id ) {
|
||||
var emoji = this.emoji_data && this.emoji_data[emote_id],
|
||||
setting = this.settings.parse_emoji,
|
||||
|
||||
src = emoji ? (setting === 2 ? emoji.noto_src : emoji.tw_src) : null,
|
||||
image = '';
|
||||
|
||||
if ( src && this.settings.emote_image_hover )
|
||||
image = '<img class="emoticon ffz-image-hover emoji" src="' + src + '">';
|
||||
|
||||
emote.setAttribute('original-title', emoji ? (image + 'Emoji: ' + emote.alt + '<br>Name: ' + emoji.name + (emoji.short_name ? "<br>Short Name: :" + emoji.short_name + ":" : "")) : emote.alt);
|
||||
continue;
|
||||
}
|
||||
|
||||
// FFZ Emotes
|
||||
emote_id = emote.getAttribute('data-ffz-emote');
|
||||
set_id = emote.getAttribute('data-ffz-set');
|
||||
|
||||
var emote_set = this.emote_sets[set_id];
|
||||
if ( ! emote_set || ! emote_set.emoticons || ! emote_set.emoticons[emote_id] )
|
||||
continue;
|
||||
|
||||
emote.setAttribute('original-title', this._emote_tooltip(emote_set.emoticons[emote_id]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------
|
||||
// Emoji Loading
|
||||
|
|
|
@ -25,8 +25,6 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
this.log("BetterTTV was detected after " + delay + "ms. Hooking.");
|
||||
this.has_bttv = true;
|
||||
|
||||
// this.track('setCustomVariable', '3', 'BetterTTV', BetterTTV.info.versionString());
|
||||
|
||||
// Disable Dark if it's enabled.
|
||||
document.body.classList.remove("ffz-dark");
|
||||
if ( this._dark_style ) {
|
||||
|
@ -59,22 +57,28 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
this._roomv.ffzUpdateStatus();
|
||||
}
|
||||
|
||||
// Disable style blocks.
|
||||
this.toggle_style('chat-setup');
|
||||
this.toggle_style('chat-padding');
|
||||
this.toggle_style('chat-background');
|
||||
|
||||
this.toggle_style('chat-separator');
|
||||
this.toggle_style('chat-separator-3d');
|
||||
this.toggle_style('chat-separator-3d-inset');
|
||||
this.toggle_style('chat-separator-wide');
|
||||
|
||||
this.toggle_style('chat-hc-text');
|
||||
this.toggle_style('chat-hc-bold');
|
||||
this.toggle_style('chat-hc-background');
|
||||
|
||||
this.toggle_style('chat-colors-gray');
|
||||
this.toggle_style('badges-transparent');
|
||||
|
||||
// Disable other features too.
|
||||
document.body.classList.remove("ffz-chat-colors");
|
||||
document.body.classList.remove("ffz-chat-colors-gray");
|
||||
document.body.classList.remove("ffz-chat-background");
|
||||
document.body.classList.remove("ffz-chat-padding");
|
||||
document.body.classList.remove("ffz-chat-separator");
|
||||
document.body.classList.remove("ffz-chat-separator-3d");
|
||||
document.body.classList.remove("ffz-chat-separator-wide");
|
||||
document.body.classList.remove("ffz-chat-separator-3d-inset");
|
||||
document.body.classList.remove('ffz-transparent-badges');
|
||||
document.body.classList.remove("ffz-sidebar-swap");
|
||||
document.body.classList.remove("ffz-portrait");
|
||||
document.body.classList.remove("ffz-flip-dashboard");
|
||||
document.body.classList.remove("ffz-transparent-badges");
|
||||
document.body.classList.remove("ffz-high-contrast-chat-text");
|
||||
document.body.classList.remove("ffz-high-contrast-chat-bg");
|
||||
document.body.classList.remove("ffz-high-contrast-chat-bold");
|
||||
|
||||
// Remove Following Count
|
||||
if ( this.settings.following_count ) {
|
||||
|
@ -218,14 +222,14 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
// Why is emote parsing so bad? ;_;
|
||||
_.each(emotes, function(emote) {
|
||||
var tooltip = f._emote_tooltip(emote),
|
||||
eo = ['<img class="emoticon" data-ffz-emote="' + emote.id + '" srcset="' + (emote.srcSet || "") + '" src="' + emote.urls[1] + '" data-regex="' + emote.name + '" title="' + tooltip + '" />'],
|
||||
eo = ['<img class="emoticon html-tooltip" data-ffz-emote="' + emote.id + '" srcset="' + utils.quote_attr(emote.srcSet || "") + '" src="' + utils.quote_attr(emote.urls[1]) + '" data-regex="' + utils.quote_attr(emote.name) + '" title="' + utils.quote_attr(tooltip) + '">'],
|
||||
old_tokens = tokens;
|
||||
|
||||
tokens = [];
|
||||
|
||||
for(var i=0; i < old_tokens.length; i++) {
|
||||
var token = old_tokens[i];
|
||||
if ( typeof token != "string" ) {
|
||||
if ( typeof token !== "string" ) {
|
||||
tokens.push(token);
|
||||
continue;
|
||||
}
|
||||
|
@ -242,7 +246,7 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
tokens.push(eo);
|
||||
|
||||
if ( mine && l_room )
|
||||
f.add_usage(l_room, emote.id);
|
||||
f.add_usage(l_room, emote);
|
||||
|
||||
} else
|
||||
tokens.push(bit);
|
||||
|
@ -252,9 +256,10 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
}
|
||||
|
||||
// Sneak in Emojicon Processing
|
||||
/*
|
||||
if ( f.settings.parse_emoji && f.emoji_data ) {
|
||||
var old_tokens = tokens;
|
||||
var old_tokens = tokens,
|
||||
setting = f.settings.parse_emoji;
|
||||
|
||||
tokens = [];
|
||||
|
||||
for(var i=0; i < old_tokens.length; i++) {
|
||||
|
@ -274,20 +279,25 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
variant = tbits.shift();
|
||||
|
||||
if ( variant === '\uFE0E' )
|
||||
bits.push(match);
|
||||
tokens.push(match);
|
||||
else {
|
||||
var eid = utils.emoji_to_codepoint(match, variant),
|
||||
data = f.emoji_data[eid];
|
||||
data = f.emoji_data[eid],
|
||||
src = data && (setting === 2 ? data.noto_src : data.tw_src);
|
||||
|
||||
if ( data ) {
|
||||
tokens.push(['<img class="emoticon" height="18px" srcset="' + (data.srcSet || "") + '" src="' + data.src + '" alt="' + alt + '" title="Emoji: ' + data.raw + '\nName: :' + data.short_name + ':">']);
|
||||
if ( data && src ) {
|
||||
var image = src && f.settings.emote_image_hover ? '<img class="emoticon ffz-image-hover" src="' + src + '">' : '',
|
||||
tooltip = image + "Emoji: " + data.raw + "<br>Name: " + data.name + (data.short_name ? "<br>Short Name: :" + data.short_name + ":" : ""),
|
||||
code = utils.quote_attr(data.raw);
|
||||
|
||||
tokens.push(['<img class="emoticon emoji html-tooltip" height="18px" data-ffz-emoji="' + eid + '" src="' + utils.quote_attr(src) + '" data-regex="' + code + '" alt="' + code + '" title="' + utils.quote_attr(tooltip) + '">']);
|
||||
} else
|
||||
tokens.push(match + (variant || ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
|
277
src/ext/rechat.js
Normal file
277
src/ext/rechat.js
Normal file
|
@ -0,0 +1,277 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
constants = require('../constants'),
|
||||
utils = require('../utils');
|
||||
|
||||
|
||||
// --------------------
|
||||
// Initialization
|
||||
// --------------------
|
||||
|
||||
FFZ.prototype.setup_rechat = function() {
|
||||
if ( this.has_bttv || navigator.userAgent.indexOf('Android') !== -1 )
|
||||
return;
|
||||
|
||||
this._rechat_listening = false;
|
||||
|
||||
this.log("Installing ReChat mutation observer.");
|
||||
|
||||
var f = this;
|
||||
this._rechat_observer = new MutationObserver(function(mutations) {
|
||||
for(var i=0; i < mutations.length; i++) {
|
||||
var mutation = mutations[i];
|
||||
if ( mutation.type !== "childList" )
|
||||
continue;
|
||||
|
||||
for(var x=0; x < mutation.addedNodes.length; x++) {
|
||||
var added = mutation.addedNodes[x];
|
||||
if ( added.nodeType !== added.ELEMENT_NODE || added.tagName !== "DIV" )
|
||||
continue;
|
||||
|
||||
// Is this a ReChat line?
|
||||
if ( added.classList.contains('rechat-chat-line') && ! added.classList.contains('ffz-processed') )
|
||||
f.process_rechat_line(added);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.log("Starting ReChat check loop.");
|
||||
this._rechat_interval = setInterval(this.find_rechat.bind(this), 1000);
|
||||
this.find_rechat();
|
||||
}
|
||||
|
||||
|
||||
// --------------------
|
||||
// ReChat Detection
|
||||
// --------------------
|
||||
|
||||
FFZ.prototype.find_rechat = function() {
|
||||
var el = !this.has_bttv ? document.querySelector('.rechat-chat-line') : null;
|
||||
|
||||
// If there's no change, don't continue.
|
||||
if ( !!el === this._rechat_listening )
|
||||
return;
|
||||
|
||||
// If we're no longer listening, stop the observer and quit.
|
||||
if ( ! el ) {
|
||||
this._rechat_observer.disconnect();
|
||||
this._rechat_listening = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// We're newly listening. Process all existing ReChat chat lines
|
||||
// and darken the container if required, also enable the observer.
|
||||
var container = jQuery(el).parents('.chat-container');
|
||||
if ( ! container.length )
|
||||
return;
|
||||
|
||||
container = container[0];
|
||||
|
||||
// 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');
|
||||
|
||||
// Tooltips
|
||||
jQuery(container).find('.tooltip').tipsy({live: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
jQuery(container).find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
|
||||
// Load the room data.
|
||||
var room_id = el.getAttribute('data-room');
|
||||
if ( room_id && ! this.rooms[room_id] )
|
||||
this.load_room(room_id, this._reprocess_rechat.bind(this, container));
|
||||
|
||||
// Do stuff.
|
||||
var lines = container.querySelectorAll('.rechat-chat-line');
|
||||
for(var i=0; i < lines.length; i++) {
|
||||
var line = lines[i];
|
||||
if ( line.classList.contains('ffz-processed') )
|
||||
continue;
|
||||
|
||||
this.process_rechat_line(line);
|
||||
}
|
||||
|
||||
// Start observing.
|
||||
this._rechat_observer.observe(container, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
|
||||
this._rechat_listening = true;
|
||||
}
|
||||
|
||||
|
||||
// --------------------
|
||||
// ReChat Lines
|
||||
// --------------------
|
||||
|
||||
FFZ.prototype._reprocess_rechat = function(container) {
|
||||
var lines = container.querySelectorAll('.rechat-chat-line');
|
||||
for(var i=0; i < lines.length; i++)
|
||||
this.process_rechat_line(lines[i], true);
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.process_rechat_line = function(line, reprocess) {
|
||||
if ( ! reprocess && line.classList.contains('ffz-processed') )
|
||||
return;
|
||||
|
||||
line.classList.add('ffz-processed');
|
||||
|
||||
var user_id = line.getAttribute('data-sender'),
|
||||
room_id = line.getAttribute('data-room'),
|
||||
|
||||
Layout = App.__container__.lookup('controller:layout'),
|
||||
Settings = App.__container__.lookup('controller:settings'),
|
||||
is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('model.darkMode')),
|
||||
|
||||
badges_el = line.querySelector('.badges'),
|
||||
from_el = line.querySelector('.from'),
|
||||
message_el = line.querySelector('.message'),
|
||||
|
||||
badges = {},
|
||||
had_badges = !!badges_el,
|
||||
|
||||
raw_color = from_el && FFZ.Color.RGB.fromCSS(from_el.style.color),
|
||||
colors = raw_color && this._handle_color(raw_color),
|
||||
|
||||
alias = this.aliases[user_id];
|
||||
|
||||
|
||||
if ( ! badges_el ) {
|
||||
badges_el = document.createElement('span');
|
||||
badges_el.className = 'badges float-left';
|
||||
line.insertBefore(badges_el, from_el || line.firstElementChild);
|
||||
}
|
||||
|
||||
if ( ! reprocess || ! had_badges ) {
|
||||
// Read existing known badges.
|
||||
var existing = badges_el.querySelectorAll('.badge');
|
||||
for(var i=0; i < existing.length; i++) {
|
||||
var badge = existing[i];
|
||||
if ( badge.classList.contains('broadcaster') )
|
||||
badges[0] = {klass: 'broadcaster', title: 'Broadcaster'};
|
||||
else if ( badge.classList.contains('staff') )
|
||||
badges[0] = {klass: 'staff', title: 'Staff'};
|
||||
else if ( badge.classList.contains('admin') )
|
||||
badges[0] = {klass: 'admin', title: 'Admin'};
|
||||
else if ( badge.classList.contains('global-moderator') )
|
||||
badges[0] = {klass: 'global-moderator', title: 'Global Moderator'};
|
||||
else if ( badge.classList.contains('moderator') )
|
||||
badges[0] = {klass: 'moderator', title: 'Moderator'};
|
||||
else if ( badge.classList.contains('subscriber') )
|
||||
badges[10] = {klass: 'subscriber', title: 'Subscriber'};
|
||||
else if ( badge.classList.contains('turbo') )
|
||||
badges[15] = {klass: 'turbo', title: 'Turbo'};
|
||||
}
|
||||
|
||||
if ( user_id && user_id === room_id )
|
||||
badges[0] = {klass: 'broadcaster', title: 'Broadcaster'};
|
||||
|
||||
if ( user_id )
|
||||
badges = this._render_badges(user_id, room_id, badges);
|
||||
|
||||
var output = '';
|
||||
for(var key in badges) {
|
||||
var badge = badges[key],
|
||||
css = badge.iamge ? 'background-image:url("' + badge.image + '");' : '';
|
||||
|
||||
if ( badge.color )
|
||||
css += 'background-color:' + badge.color + ';';
|
||||
|
||||
if ( badge.extra_css )
|
||||
css += badge.extra_css;
|
||||
|
||||
output += '<div class="badge float-left tooltip ' + badge.klass + '"' + (css ? ' style="' + css + '"' : '') + ' title="' + badge.title + '"></div>';
|
||||
}
|
||||
|
||||
badges_el.innerHTML = output;
|
||||
}
|
||||
|
||||
if ( ! reprocess && from_el ) {
|
||||
from_el.style.fontWeight = "";
|
||||
if ( colors ) {
|
||||
from_el.classList.add('has_color');
|
||||
from_el.style.color = is_dark ? colors[1] : colors[0];
|
||||
}
|
||||
|
||||
if ( alias ) {
|
||||
from_el.classList.add('ffz-alias');
|
||||
from_el.title = from_el.textContent;
|
||||
from_el.textContent = alias;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! message_el )
|
||||
return;
|
||||
|
||||
if ( ! reprocess && message_el.style.color ) {
|
||||
message_el.classList.add('has-color');
|
||||
message_el.style.color = is_dark ? colors[1] : colors[0];
|
||||
}
|
||||
|
||||
var raw_tokens = line.getAttribute('data-tokens'),
|
||||
tokens = raw_tokens ? JSON.parse(raw_tokens) : [];
|
||||
|
||||
if ( ! raw_tokens ) {
|
||||
for(var i=0; i < message_el.childNodes.length; i++) {
|
||||
var node = message_el.childNodes[i];
|
||||
|
||||
if ( node.nodeType === node.TEXT_NODE )
|
||||
tokens.push(node.textContent);
|
||||
|
||||
else if ( node.nodeType === node.ELEMENT_NODE ) {
|
||||
if ( node.tagName === 'IMG' )
|
||||
tokens.push({
|
||||
altText: node.alt,
|
||||
emoticonSrc: node.src
|
||||
});
|
||||
|
||||
else if ( node.tagName === 'A' )
|
||||
tokens.push({
|
||||
isLink: true,
|
||||
href: node.textContent
|
||||
});
|
||||
|
||||
else if ( node.tagName === 'SPAN' )
|
||||
tokens.push({
|
||||
mentionedUser: node.textContent,
|
||||
own: node.classList.contains('mentioning')
|
||||
});
|
||||
|
||||
else
|
||||
this.log("Unknown Tag Type: " + node.tagName);
|
||||
} else
|
||||
this.log("Unknown Node Type Tokenizing Message: " + node.nodeType);
|
||||
}
|
||||
}
|
||||
|
||||
line.setAttribute('data-tokens', JSON.stringify(tokens));
|
||||
|
||||
// Further tokenization~!
|
||||
if ( this.settings.replace_bad_emotes )
|
||||
tokens = this.tokenize_replace_emotes(tokens);
|
||||
|
||||
tokens = this._remove_banned(tokens);
|
||||
tokens = this.tokenize_emotes(user_id, room_id, tokens, false);
|
||||
|
||||
if ( this.settings.parse_emoji )
|
||||
tokens = this.tokenize_emoji(tokens);
|
||||
|
||||
tokens = this.tokenize_mentions(tokens);
|
||||
|
||||
// Check for a mention
|
||||
if ( ! line.classList.contains('ffz-mentioend') )
|
||||
for(var i=0; i < tokens.length; i++)
|
||||
if ( tokens[i].mentionedUser ) {
|
||||
line.classList.add('ffz-mentioned');
|
||||
break;
|
||||
}
|
||||
|
||||
// Now, put the content back into the element.
|
||||
message_el.innerHTML = this.render_tokens(tokens);
|
||||
}
|
38
src/main.js
38
src/main.js
|
@ -22,7 +22,7 @@ FFZ.get = function() { return FFZ.instance; }
|
|||
|
||||
// Version
|
||||
var VER = FFZ.version_info = {
|
||||
major: 3, minor: 5, revision: 50,
|
||||
major: 3, minor: 5, revision: 77,
|
||||
toString: function() {
|
||||
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
|
||||
}
|
||||
|
@ -90,12 +90,22 @@ FFZ.prototype._pastebin = function(data, callback) {
|
|||
// -------------------
|
||||
|
||||
FFZ.prototype.get_user = function() {
|
||||
if ( window.PP && PP.login ) {
|
||||
return PP;
|
||||
} else if ( window.App ) {
|
||||
var nc = App.__container__.lookup("controller:login");
|
||||
return nc ? nc.get("userData") : undefined;
|
||||
if ( this.__user )
|
||||
return this.__user;
|
||||
|
||||
var user;
|
||||
if ( window.App ) {
|
||||
var nc = App.__container__.lookup('controller:login');
|
||||
user = nc ? nc.get('userData') : undefined;
|
||||
}
|
||||
|
||||
if ( ! user && window.PP && PP.login )
|
||||
user = PP;
|
||||
|
||||
if ( user )
|
||||
this.__user = user;
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
|
@ -123,14 +133,17 @@ require('./ember/room');
|
|||
require('./ember/layout');
|
||||
require('./ember/line');
|
||||
require('./ember/chatview');
|
||||
require('./ember/conversations');
|
||||
require('./ember/viewers');
|
||||
require('./ember/moderation-card');
|
||||
require('./ember/chat-input');
|
||||
//require('./ember/teams');
|
||||
require('./ember/directory');
|
||||
require('./ember/following');
|
||||
|
||||
require('./debug');
|
||||
|
||||
require('./ext/rechat');
|
||||
require('./ext/betterttv');
|
||||
require('./ext/emote_menu');
|
||||
|
||||
|
@ -138,6 +151,7 @@ require('./featurefriday');
|
|||
|
||||
require('./ui/styles');
|
||||
require('./ui/dark');
|
||||
require('./ui/tooltips');
|
||||
require('./ui/notifications');
|
||||
require('./ui/viewer_count');
|
||||
require('./ui/sub_count');
|
||||
|
@ -241,6 +255,7 @@ FFZ.prototype.init_normal = function(delay, no_socket) {
|
|||
|
||||
// Start this early, for quick loading.
|
||||
this.setup_dark();
|
||||
this.setup_css();
|
||||
|
||||
if ( ! no_socket )
|
||||
this.ws_create();
|
||||
|
@ -251,9 +266,9 @@ FFZ.prototype.init_normal = function(delay, no_socket) {
|
|||
|
||||
this.setup_notifications();
|
||||
this.setup_following_count(false);
|
||||
this.setup_css();
|
||||
this.setup_menu();
|
||||
|
||||
this.fix_tooltips();
|
||||
this.find_bttv(10);
|
||||
|
||||
var end = (window.performance && performance.now) ? performance.now() : Date.now(),
|
||||
|
@ -278,6 +293,7 @@ FFZ.prototype.init_dashboard = function(delay) {
|
|||
|
||||
// Start this early, for quick loading.
|
||||
this.setup_dark();
|
||||
this.setup_css();
|
||||
|
||||
this.ws_create();
|
||||
this.setup_colors();
|
||||
|
@ -287,7 +303,6 @@ FFZ.prototype.init_dashboard = function(delay) {
|
|||
this.setup_tokenization();
|
||||
this.setup_notifications();
|
||||
this.setup_following_count(false);
|
||||
this.setup_css();
|
||||
this.setup_menu();
|
||||
|
||||
this._update_subscribers();
|
||||
|
@ -295,6 +310,7 @@ FFZ.prototype.init_dashboard = function(delay) {
|
|||
// Set up the FFZ message passer.
|
||||
this.setup_message_event();
|
||||
|
||||
this.fix_tooltips();
|
||||
this.find_bttv(10);
|
||||
|
||||
var end = (window.performance && performance.now) ? performance.now() : Date.now(),
|
||||
|
@ -319,6 +335,7 @@ FFZ.prototype.init_ember = function(delay) {
|
|||
|
||||
// Start this early, for quick loading.
|
||||
this.setup_dark();
|
||||
this.setup_css();
|
||||
|
||||
this.ws_create();
|
||||
this.setup_emoticons();
|
||||
|
@ -335,23 +352,26 @@ FFZ.prototype.init_ember = function(delay) {
|
|||
this.setup_line();
|
||||
this.setup_layout();
|
||||
this.setup_chatview();
|
||||
this.setup_conversations();
|
||||
this.setup_viewers();
|
||||
this.setup_mod_card();
|
||||
this.setup_chat_input();
|
||||
this.setup_directory();
|
||||
this.setup_profile_following();
|
||||
|
||||
//this.setup_teams();
|
||||
|
||||
this.setup_notifications();
|
||||
this.setup_css();
|
||||
this.setup_menu();
|
||||
this.setup_my_emotes();
|
||||
this.setup_following();
|
||||
this.setup_following_count(true);
|
||||
this.setup_races();
|
||||
|
||||
this.fix_tooltips();
|
||||
this.connect_extra_chat();
|
||||
|
||||
this.setup_rechat();
|
||||
this.find_bttv(10);
|
||||
this.find_emote_menu(10);
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ FFZ.settings_info.socket_server_pool = {
|
|||
2: "Development"
|
||||
},
|
||||
|
||||
value: ffz_socket_seed > 0.65 ? 1 : 0,
|
||||
value: ffz_socket_seed > 0.4 ? 1 : 0,
|
||||
|
||||
process_value: function(val) {
|
||||
if ( typeof val === "string" )
|
||||
|
|
3
src/styles/badges-blank.css
Normal file
3
src/styles/badges-blank.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
.badges .badge:not(.subscriber) {
|
||||
background-size: 0px;
|
||||
}
|
5
src/styles/badges-circular-small.css
Normal file
5
src/styles/badges-circular-small.css
Normal file
|
@ -0,0 +1,5 @@
|
|||
.badges .badge:not(.subscriber) {
|
||||
height: 10px;
|
||||
min-width: 10px;
|
||||
margin: 5px 3px 5px 0;
|
||||
}
|
6
src/styles/badges-circular.css
Normal file
6
src/styles/badges-circular.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
.badges .badge:not(.subscriber) {
|
||||
border-radius: 9px;
|
||||
background-size: 16px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
4
src/styles/badges-legacy-mod.css
Normal file
4
src/styles/badges-legacy-mod.css
Normal file
|
@ -0,0 +1,4 @@
|
|||
.badges .moderator {
|
||||
background-color: #068c10;
|
||||
background-image: url('//cdn.frankerfacez.com/script/legacy-mod.png');
|
||||
}
|
4
src/styles/badges-legacy-turbo.css
Normal file
4
src/styles/badges-legacy-turbo.css
Normal file
|
@ -0,0 +1,4 @@
|
|||
.badges .turbo {
|
||||
background-color: #6441a3;
|
||||
background-image: url('//cdn.frankerfacez.com/script/legacy-turbo.png');
|
||||
}
|
14
src/styles/badges-legacy.css
Normal file
14
src/styles/badges-legacy.css
Normal file
|
@ -0,0 +1,14 @@
|
|||
.badges .staff {
|
||||
background-color: #6441a5;
|
||||
background-image: url('//cdn.frankerfacez.com/script/legacy-staff.png');
|
||||
}
|
||||
|
||||
.badges .broadcaster {
|
||||
background-color: #000;
|
||||
background-image: url('//cdn.frankerfacez.com/script/legacy-broadcaster.png');
|
||||
}
|
||||
|
||||
.badges .admin {
|
||||
background-color: #ff0303;
|
||||
background-image: url('//cdn.frankerfacez.com/script/legacy-admin.png');
|
||||
}
|
4
src/styles/badges-rounded.css
Normal file
4
src/styles/badges-rounded.css
Normal file
|
@ -0,0 +1,4 @@
|
|||
/* Rounded Badges */
|
||||
.badges .badge:not(.subscriber) {
|
||||
border-radius: 2px;
|
||||
}
|
12
src/styles/badges-transparent.css
Normal file
12
src/styles/badges-transparent.css
Normal file
|
@ -0,0 +1,12 @@
|
|||
.badges .badge {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
|
||||
/* Invert Some Badges */
|
||||
body:not(.ffz-dark) .app-main:not(.theatre) .conversation-window .badges .badge:not(.subscriber):not(.ffz-badge-0),
|
||||
.app-main:not(.theatre) .chat-container:not(.dark):not(.force-dark) .badges .badge:not(.subscriber):not(.ffz-badge-0),
|
||||
.app-main:not(.theatre) .ember-chat-container:not(.dark):not(.force-dark) .badges .badge:not(.subscriber):not(.ffz-badge-0) {
|
||||
filter: invert(100%);
|
||||
-webkit-filter: invert(100%);
|
||||
}
|
101
src/styles/chat-background.css
Normal file
101
src/styles/chat-background.css
Normal file
|
@ -0,0 +1,101 @@
|
|||
/* Regular Alternating Background */
|
||||
.conversation-chat-lines > div:nth-child(2n+0):before,
|
||||
.chat-history .chat-line:nth-child(2n+0):before,
|
||||
.ember-chat .chat-lines > div:nth-child(2n+0) .chat-line:before,
|
||||
|
||||
/* ReChat */
|
||||
.ember-chat.chat-messages > .rechat-chat-line:nth-child(2n+0):before {
|
||||
background-color: rgba(0,0,0, 0.1);
|
||||
}
|
||||
|
||||
|
||||
/* Dark: Alternating Background */
|
||||
.ffz-dark .conversation-chat-lines > div:nth-child(2n+0):before,
|
||||
.ffz-dark .chat-history .chat-line:nth-child(2n+0):before,
|
||||
|
||||
.theatre .conversation-chat-lines > div:nth-child(2n+0):before,
|
||||
.theatre .chat-history .chat-line:nth-child(2n+0):before,
|
||||
.theatre .ember-chat .chat-lines > div:nth-child(2n+0) .chat-line:before,
|
||||
|
||||
.dark .chat-history .chat-line:nth-child(2n+0):before,
|
||||
.force-dark .chat-history .chat-line:nth-child(2n+0):before,
|
||||
.dark .chat-lines > div:nth-child(2n+0) .chat-line:before,
|
||||
.force-dark .chat-lines > div:nth-child(2n+0) .chat-line:before,
|
||||
|
||||
/* ReChat */
|
||||
.theatre .chat-lines > .rechat-chat-line:nth-child(2n+0):before,
|
||||
.dark .chat-lines > .rechat-chat-line:nth-child(2n+0):before,
|
||||
.force-dark .chat-lines > .rechat-chat-line:nth-child(2n+0):before {
|
||||
background-color: rgba(255,255,255, 0.05);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DEPRECIATED: Mention Backgrounds */
|
||||
.chat-history .chat-line.ffz-mentioned:before,
|
||||
.ember-chat .chat-line.ffz-mentioned:before {
|
||||
background-color: rgba(255,127,127,0.2);
|
||||
}
|
||||
|
||||
.chat-history .chat-line.ffz-mentioned:nth-child(2n+0):before,
|
||||
.ember-chat .chat-lines > div:nth-child(2n+0) .chat-line.ffz-mentioned:before {
|
||||
background-color: rgba(255,127,127, 0.4);
|
||||
}
|
||||
|
||||
|
||||
/* DEPRECIATED: DARK THEME: Mention Backgrounds */
|
||||
.ffz-dark .chat-history .chat-line.ffz-mentioned:before,
|
||||
|
||||
.theatre .chat-line.ffz-mentioned:before,
|
||||
.dark .chat-line.ffz-mentioned:before,
|
||||
.force-dark .chat-line.ffz-mentioned:before {
|
||||
background-color: rgba(255,0,0, 0.2) !important;
|
||||
}
|
||||
|
||||
|
||||
.theatre .chat-lines > div:nth-child(2n+0) .chat-line.ffz-mentioned:before,
|
||||
|
||||
.dark .chat-history .chat-line.ffz-mentioned:nth-child(2n+0):before,
|
||||
.force-dark .chat-history .chat-line.ffz-mentioned:nth-child(2n+0):before,
|
||||
|
||||
.dark .chat-lines > div:nth-child(2n+0) .chat-line.ffz-mentioned:before,
|
||||
.force-dark .chat-lines > div:nth-child(2n+0) .chat-line.ffz-mentioned:before {
|
||||
background-color: rgba(255,0,0, 0.3) !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Chat Mentions */
|
||||
/* TODO: Move this by itself */
|
||||
|
||||
.mentioned:empty,
|
||||
.mentioning:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chat-line .mentioned,
|
||||
.chat-line .mentioning {
|
||||
border-radius: 10px;
|
||||
padding: 3px 7px;
|
||||
font-weight: bold;
|
||||
color: #323232;
|
||||
background-color: rgba(255,255,255, 0.75);
|
||||
}
|
||||
|
||||
/* DARK THEME: Chat Mentions */
|
||||
.ffz-dark .chat-history .mentioned,
|
||||
.ffz-dark .chat-history .mentioning,
|
||||
|
||||
.app-main.theatre .chat-container .chat-line .mentioned,
|
||||
.app-main.theatre .chat-container .chat-line .mentioning,
|
||||
.chat-container.dark .chat-line .mentioned,
|
||||
.chat-container.dark .chat-line .mentioning,
|
||||
.chat-container.force-dark .chat-line .mentioned,
|
||||
.chat-container.force-dark .chat-line .mentioning,
|
||||
.ember-chat-container.dark .chat-line .mentioned,
|
||||
.ember-chat-container.dark .chat-line .mentioning,
|
||||
.ember-chat-container.force-dark .chat-line .mentioned,
|
||||
.ember-chat-container.force-dark .chat-line .mentioning {
|
||||
color: #8c8c8c;
|
||||
background-color: rgba(16,16,16, 0.75);
|
||||
}
|
4
src/styles/chat-colors-gray.css
Normal file
4
src/styles/chat-colors-gray.css
Normal file
|
@ -0,0 +1,4 @@
|
|||
.chat-line:not(.admin):not(.notification) span.from,
|
||||
.chat-line:not(.admin):not(.notification) span.message {
|
||||
color: inherit !important
|
||||
}
|
16
src/styles/chat-hc-background.css
Normal file
16
src/styles/chat-hc-background.css
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* High-Contrast Background */
|
||||
.chat-container,
|
||||
.ember-chat-container {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
|
||||
/* Dark: High-Contrast Background */
|
||||
.theatre .chat-container,
|
||||
.theatre .ember-chat-container,
|
||||
.chat-container.dark,
|
||||
.chat-container.force-dark,
|
||||
.ember-chat-container.dark,
|
||||
.ember-chat-container.force-dark {
|
||||
background-color: #000 !important;
|
||||
}
|
6
src/styles/chat-hc-bold.css
Normal file
6
src/styles/chat-hc-bold.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
/* High-Contrast Bold */
|
||||
.chat-line .from,
|
||||
.chat-line .colon,
|
||||
.chat-line .message {
|
||||
font-weight: bold;
|
||||
}
|
19
src/styles/chat-hc-text.css
Normal file
19
src/styles/chat-hc-text.css
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* High-Contrast Text */
|
||||
.chat-container,
|
||||
.ember-chat-container {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
/* Dark: High-Contrast Text */
|
||||
.theatre .chat-container,
|
||||
.theatre .ember-chat-container,
|
||||
.chat-container.dark,
|
||||
.chat-container.force-dark,
|
||||
.ember-chat-container.dark,
|
||||
.ember-chat-container.force-dark,
|
||||
|
||||
.ffz-dark .ember-chat-container.dark .chat-line,
|
||||
.ffz-dark .chat-container.dark .chat-line
|
||||
{
|
||||
color: #fff !important;
|
||||
}
|
15
src/styles/chat-padding.css
Normal file
15
src/styles/chat-padding.css
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* Chat Line Padding */
|
||||
.ember-chat .chat-messages .chat-line,
|
||||
.ember-chat .chat-messages .chat-line.admin,
|
||||
|
||||
.conversation-window .conversation-system-messages,
|
||||
.conversation-window .conversation-chat-line,
|
||||
.conversation-window .timestamp-line {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
|
||||
/* Remove Extra Conversation Padding */
|
||||
.conversation-window .conversation-chat-lines {
|
||||
padding-top: 0;
|
||||
}
|
17
src/styles/chat-separator-3d-inset.css
Normal file
17
src/styles/chat-separator-3d-inset.css
Normal file
|
@ -0,0 +1,17 @@
|
|||
/* 3D Inset Separators */
|
||||
.conversation-chat-lines > div:before,
|
||||
.chat-line:before {
|
||||
border-top: 1px solid #aaa;
|
||||
border-bottom-color: rgba(255,255,255, 0.5);
|
||||
}
|
||||
|
||||
/* Dark: 3D Inset Separators */
|
||||
.ffz-dark .conversation-chat-lines > div:before,
|
||||
.theatre .conversation-chat-lines > div:before,
|
||||
|
||||
.theatre .chat-line:before,
|
||||
.dark .chat-line:before,
|
||||
.force-dark .chat-line:before {
|
||||
border-top-color: #000;
|
||||
border-bottom-color: rgba(255,255,255, 0.1);
|
||||
}
|
15
src/styles/chat-separator-3d.css
Normal file
15
src/styles/chat-separator-3d.css
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* 3D Separators */
|
||||
.conversation-chat-lines > div:before,
|
||||
.chat-line:before {
|
||||
border-top: 1px solid rgba(255,255,255, 0.5);
|
||||
}
|
||||
|
||||
/* Dark: 3D Separators */
|
||||
.ffz-dark .conversation-chat-lines > div:before,
|
||||
.theatre .conversation-chat-lines > div:before,
|
||||
|
||||
.theatre .chat-line:before,
|
||||
.dark .chat-line:before,
|
||||
.force-dark .chat-line:before {
|
||||
border-top-color: rgba(255,255,255, 0.1);
|
||||
}
|
15
src/styles/chat-separator-wide.css
Normal file
15
src/styles/chat-separator-wide.css
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* Wide Separators */
|
||||
.conversation-chat-lines > div:before,
|
||||
.chat-line:before {
|
||||
border-top: 1px solid #aaa;
|
||||
}
|
||||
|
||||
/* Dark: Wide Separators */
|
||||
.ffz-dark .conversation-chat-lines > div:before,
|
||||
.theatre .conversation-chat-lines > div:before,
|
||||
|
||||
.theatre .chat-line:before,
|
||||
.dark .chat-line:before,
|
||||
.force-dark .chat-line:before {
|
||||
border-top-color: #000;
|
||||
}
|
29
src/styles/chat-separator.css
Normal file
29
src/styles/chat-separator.css
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* Simple Separators */
|
||||
.conversation-chat-lines > div:before,
|
||||
.chat-line:before {
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
/* Dark: Simple Separators */
|
||||
.ffz-dark .conversation-chat-lines > div:before,
|
||||
.theatre .conversation-chat-lines > div:before,
|
||||
|
||||
.theatre .chat-line:before,
|
||||
.dark .chat-line:before,
|
||||
.force-dark .chat-line:before {
|
||||
border-bottom-color: #000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Hide First Line */
|
||||
.conversation-chat-lines > div:first-child:before,
|
||||
.chat-lines > div:first-child .chat-line:before {
|
||||
border-top-color: transparent;
|
||||
}
|
||||
|
||||
/* Hide Last Line */
|
||||
.conversation-chat-lines > div:last-child:nth-child(odd):before,
|
||||
.chat-lines > div:last-child:nth-child(odd) .chat-line:before {
|
||||
border-bottom-color: transparent;
|
||||
}
|
18
src/styles/chat-setup.css
Normal file
18
src/styles/chat-setup.css
Normal file
|
@ -0,0 +1,18 @@
|
|||
.conversation-chat-lines > div,
|
||||
.chat-line {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.conversation-chat-lines > div:before,
|
||||
.chat-line:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
left: 0; right: 0;
|
||||
top: 2px; bottom: 1px;
|
||||
}
|
||||
|
||||
.chat-history .chat-line:before {
|
||||
top: 0; bottom: 0;
|
||||
}
|
176
src/tokenize.js
176
src/tokenize.js
|
@ -1,76 +1,11 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require("./utils"),
|
||||
constants = require("./constants"),
|
||||
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
|
||||
helpers,
|
||||
conv_helpers,
|
||||
|
||||
EXPLANATION_TRAIL = '<hr>FFZ is hiding this link because this url shortener is known to be used by Twitch spam bots posting malicious links. Please use caution when visiting shortened links.',
|
||||
|
||||
SRCSETS = {};
|
||||
build_srcset = function(id) {
|
||||
if ( SRCSETS[id] )
|
||||
return SRCSETS[id];
|
||||
var out = SRCSETS[id] = TWITCH_BASE + id + "/1.0 1x, " + TWITCH_BASE + id + "/2.0 2x, " + TWITCH_BASE + id + "/3.0 4x";
|
||||
return out;
|
||||
},
|
||||
|
||||
|
||||
data_to_tooltip = function(data) {
|
||||
var set = data.set,
|
||||
set_type = data.set_type,
|
||||
owner = data.owner;
|
||||
|
||||
if ( set_type === undefined )
|
||||
set_type = "Channel";
|
||||
|
||||
if ( ! set )
|
||||
return data.code;
|
||||
|
||||
else if ( set === "--global--" ) {
|
||||
set = "Twitch Global";
|
||||
set_type = null;
|
||||
|
||||
} else if ( set == "--twitch-turbo--" || set == "turbo" || set == "--turbo-faces--" ) {
|
||||
set = "Twitch Turbo";
|
||||
set_type = null;
|
||||
}
|
||||
|
||||
return "Emoticon: " + data.code + "\n" + (set_type ? set_type + ": " : "") + set + (owner ? "\nBy: " + owner.display_name : "");
|
||||
},
|
||||
|
||||
build_tooltip = function(id) {
|
||||
var emote_data = this._twitch_emotes[id],
|
||||
set = emote_data ? emote_data.set : null;
|
||||
|
||||
if ( ! emote_data )
|
||||
return "???";
|
||||
|
||||
if ( typeof emote_data == "string" )
|
||||
return emote_data;
|
||||
|
||||
if ( emote_data.tooltip )
|
||||
return emote_data.tooltip;
|
||||
|
||||
return emote_data.tooltip = data_to_tooltip(emote_data);
|
||||
},
|
||||
|
||||
load_emote_data = function(id, code, success, data) {
|
||||
if ( ! success )
|
||||
return code;
|
||||
|
||||
if ( code )
|
||||
data.code = code;
|
||||
|
||||
this._twitch_emotes[id] = data;
|
||||
var tooltip = build_tooltip.bind(this)(id);
|
||||
|
||||
var images = document.querySelectorAll('img[data-emote="' + id + '"]');
|
||||
for(var x=0; x < images.length; x++)
|
||||
images[x].title = tooltip;
|
||||
|
||||
return tooltip;
|
||||
},
|
||||
|
||||
|
||||
reg_escape = function(str) {
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||
|
@ -105,7 +40,7 @@ var FFZ = window.FrankerFaceZ,
|
|||
return IMGUR_PATH.test(path);
|
||||
|
||||
return any_domain ? IMAGE_EXT.test(path) : IMAGE_DOMAINS.indexOf(domain) !== -1;
|
||||
}
|
||||
},
|
||||
|
||||
image_iframe = function(href, extra_class) {
|
||||
return '<iframe class="ffz-image-hover' + (extra_class ? ' ' + extra_class : '') + '" allowtransparency="true" src="' + constants.SERVER + 'script/img-proxy.html#' + utils.quote_attr(href) + '"></iframe>';
|
||||
|
@ -248,12 +183,12 @@ FFZ._emote_mirror_swap = function(img) {
|
|||
img.setAttribute('data-alt-attempts', attempts + 1);
|
||||
var id = img.getAttribute('data-emote');
|
||||
|
||||
if ( img.src.substr(0, TWITCH_BASE.length) === TWITCH_BASE ) {
|
||||
if ( img.src.substr(0, constants.TWITCH_BASE.length) === constants.TWITCH_BASE ) {
|
||||
img.src = constants.EMOTE_MIRROR_BASE + id + ".png";
|
||||
img.srcset = "";
|
||||
} else {
|
||||
img.src = TWITCH_BASE + id + "/1.0";
|
||||
img.srcset = build_srcset(id);
|
||||
img.src = constants.TWITCH_BASE + id + "/1.0";
|
||||
img.srcset = utils.build_srcset(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,6 +252,10 @@ FFZ.prototype.setup_tokenization = function() {
|
|||
if ( ! helpers )
|
||||
return this.log("Unable to get chat helper functions.");
|
||||
|
||||
conv_helpers = window.require && window.require("ember-twitch-conversations/helpers/conversation-line-helpers");
|
||||
if ( ! conv_helpers )
|
||||
this.log("Unable to get conversation helper functions.");
|
||||
|
||||
this.log("Hooking Ember chat line helpers.");
|
||||
|
||||
var f = this;
|
||||
|
@ -386,6 +325,8 @@ FFZ.prototype.load_twitch_emote_data = function(tries) {
|
|||
this._twitch_set_to_channel[33] = "--turbo-faces--";
|
||||
this._twitch_set_to_channel[42] = "--turbo-faces--";
|
||||
|
||||
this._reset_tooltips(true);
|
||||
|
||||
}).fail(function(data) {
|
||||
if ( data.status === 404 )
|
||||
return;
|
||||
|
@ -401,14 +342,61 @@ FFZ.prototype.load_twitch_emote_data = function(tries) {
|
|||
// Tokenization
|
||||
// ---------------------
|
||||
|
||||
FFZ.prototype.tokenize_conversation_line = function(message, prevent_notification) {
|
||||
var msg = message.get('body'),
|
||||
user = this.get_user(),
|
||||
from_user = message.get('from.username'),
|
||||
from_me = user && from_user === user.login,
|
||||
|
||||
emotes = message.get('tags.emotes'),
|
||||
tokens = [msg];
|
||||
|
||||
if ( conv_helpers && conv_helpers.checkActionMessage )
|
||||
tokens = conv_helpers.checkActionMessage(tokens);
|
||||
|
||||
// Standard Tokenization
|
||||
if ( helpers && helpers.linkifyMessage )
|
||||
tokens = helpers.linkifyMessage(tokens);
|
||||
|
||||
if ( user && user.login && helpers && helpers.mentionizeMessage )
|
||||
tokens = helpers.mentionizeMessage(tokens, user.login, from_me);
|
||||
|
||||
if ( helpers && helpers.emoticonizeMessage && emotes )
|
||||
tokens = helpers.emoticonizeMessage(tokens, emotes);
|
||||
|
||||
if ( this.settings.replace_bad_emotes )
|
||||
tokens = this.tokenize_replace_emotes(tokens);
|
||||
|
||||
// FrankerFaceZ Extras
|
||||
tokens = this._remove_banned(tokens);
|
||||
tokens = this.tokenize_emotes(from_user, undefined, tokens, from_me);
|
||||
|
||||
if ( this.settings.parse_emoji )
|
||||
tokens = this.tokenize_emoji(tokens);
|
||||
|
||||
// Capitalization
|
||||
var display_name = message.get('from.displayName');
|
||||
if ( display_name && display_name.length )
|
||||
FFZ.capitalization[from_user] = [display_name.trim(), Date.now()];
|
||||
|
||||
// Mentions!
|
||||
if ( ! from_me )
|
||||
tokens = this.tokenize_mentions(tokens);
|
||||
|
||||
// TODO: Notifications?
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, delete_links) {
|
||||
if ( msgObject.cachedTokens )
|
||||
return msgObject.cachedTokens;
|
||||
|
||||
var msg = msgObject.message,
|
||||
var msg = msgObject.message || msgObject.get('body'),
|
||||
user = this.get_user(),
|
||||
room_id = msgObject.room,
|
||||
from_me = user && msgObject.from === user.login,
|
||||
from_user = msgObject.from,
|
||||
from_me = user && from_user === user.login,
|
||||
emotes = msgObject.tags && msgObject.tags.emotes,
|
||||
|
||||
tokens = [msg];
|
||||
|
@ -438,7 +426,7 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
|
|||
|
||||
// FrankerFaceZ Extras
|
||||
tokens = this._remove_banned(tokens);
|
||||
tokens = this.tokenize_emotes(msgObject.from, room_id, tokens, from_me);
|
||||
tokens = this.tokenize_emotes(from_user, room_id, tokens, from_me);
|
||||
|
||||
if ( this.settings.parse_emoji )
|
||||
tokens = this.tokenize_emoji(tokens);
|
||||
|
@ -446,7 +434,7 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
|
|||
// Capitalization
|
||||
var display = msgObject.tags && msgObject.tags['display-name'];
|
||||
if ( display && display.length )
|
||||
FFZ.capitalization[msgObject.from] = [display.trim(), Date.now()];
|
||||
FFZ.capitalization[from_user] = [display.trim(), Date.now()];
|
||||
|
||||
|
||||
// Mentions!
|
||||
|
@ -482,7 +470,7 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
|
|||
else
|
||||
room_name = FFZ.get_capitalization(room_id);
|
||||
|
||||
display = display || Twitch.display.capitalize(msgObject.from);
|
||||
display = display || Twitch.display.capitalize(from_user);
|
||||
|
||||
if ( msgObject.style === 'action' )
|
||||
msg = '* ' + display + ' ' + msg;
|
||||
|
@ -553,39 +541,48 @@ FFZ.prototype.tokenize_line = function(user, room, message, no_emotes, no_emoji)
|
|||
FFZ.prototype.render_tokens = function(tokens, render_links) {
|
||||
var f = this;
|
||||
return _.map(tokens, function(token) {
|
||||
if ( token.hidden )
|
||||
return "";
|
||||
|
||||
if ( token.emoticonSrc ) {
|
||||
var tooltip, src = token.emoticonSrc, srcset, extra;
|
||||
var tooltip, src = token.emoticonSrc, srcset, cls, extra;
|
||||
if ( token.ffzEmote ) {
|
||||
var emote_set = f.emote_sets && f.emote_sets[token.ffzEmoteSet],
|
||||
emote = emote_set && emote_set.emoticons && emote_set.emoticons[token.ffzEmote];
|
||||
|
||||
tooltip = emote ? utils.sanitize(f._emote_tooltip(emote)) : token.altText;
|
||||
tooltip = emote ? f._emote_tooltip(emote) : token.altText;
|
||||
srcset = emote ? emote.srcSet : token.srcSet;
|
||||
extra = (emote ? ' data-ffz-emote="' + emote.id + '"' : '') + (emote_set ? ' data-ffz-set="' + emote_set.id + '"' : '');
|
||||
|
||||
} else if ( token.ffzEmoji ) {
|
||||
var eid = token.ffzEmoji,
|
||||
emoji = f.emoji_data && f.emoji_data[eid],
|
||||
setting = f.settings.parse_emoji;
|
||||
setting = f.settings.parse_emoji,
|
||||
image = '';
|
||||
|
||||
if ( setting === 0 || (setting === 1 && ! emoji.tw) || (setting === 2 && ! emoji.noto) )
|
||||
return token.altText;
|
||||
|
||||
tooltip = emoji ? "Emoji: " + token.altText + "\nName: " + emoji.name + (emoji.short_name ? "\nShort Name: :" + emoji.short_name + ":" : "") : token.altText;
|
||||
extra = ' data-ffz-emoji="' + eid + '" height="18px"';
|
||||
src = setting === 2 ? token.noto_src : token.tw_src;
|
||||
|
||||
if ( emoji && f.settings.emote_image_hover )
|
||||
image = '<img class="emoticon ffz-image-hover" src="' + src + '">';
|
||||
|
||||
tooltip = emoji ? image + "Emoji: " + token.altText + "<br>Name: " + emoji.name + (emoji.short_name ? "<br>Short Name: :" + emoji.short_name + ":" : "") : token.altText;
|
||||
extra = ' data-ffz-emoji="' + eid + '" height="18px"';
|
||||
cls = ' emoji';
|
||||
|
||||
} else {
|
||||
var id = token.replacedId || FFZ.src_to_id(token.emoticonSrc),
|
||||
data = id && f._twitch_emotes && f._twitch_emotes[id];
|
||||
|
||||
if ( data )
|
||||
tooltip = data.tooltip ? data.tooltip : token.altText;
|
||||
tooltip = data.tooltip ? data.tooltip : utils.build_tooltip.bind(f)(id, false, token.altText);
|
||||
else {
|
||||
try {
|
||||
var set_id = f._twitch_emote_to_set[id];
|
||||
if ( set_id ) {
|
||||
tooltip = load_emote_data.bind(f)(id, token.altText, true, {
|
||||
tooltip = utils.load_emote_data.bind(f)(id, token.altText, true, {
|
||||
code: token.altText,
|
||||
id: id,
|
||||
set: f._twitch_set_to_channel[set_id],
|
||||
|
@ -593,22 +590,21 @@ FFZ.prototype.render_tokens = function(tokens, render_links) {
|
|||
});
|
||||
} else {
|
||||
tooltip = f._twitch_emotes[id] = token.altText;
|
||||
f.ws_send("twitch_emote", id, load_emote_data.bind(f, id, token.altText));
|
||||
f.ws_send("twitch_emote", id, utils.load_emote_data.bind(f, id, token.altText));
|
||||
}
|
||||
} catch(err) {
|
||||
f.error("Error Generating Emote Tooltip: " + err);
|
||||
}
|
||||
}
|
||||
|
||||
var mirror_url = utils.quote_attr(constants.EMOTE_MIRROR_BASE + id + '.png');
|
||||
|
||||
extra = ' data-emote="' + id + '" onerror="FrankerFaceZ._emote_mirror_swap(this)"';
|
||||
//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.
|
||||
|
||||
if ( ! constants.EMOTE_REPLACEMENTS[id] )
|
||||
srcset = build_srcset(id);
|
||||
srcset = utils.build_srcset(id);
|
||||
}
|
||||
|
||||
return '<img class="emoticon tooltip' + (cls||"") + '"' + (extra||"") + ' src="' + utils.quote_attr(src) + '" ' + (srcset ? 'srcset="' + utils.quote_attr(srcset) + '" ' : '') + 'alt="' + utils.quote_attr(token.altText) + '" title="' + utils.quote_attr(tooltip) + '">';
|
||||
return '<img class="emoticon html-tooltip' + (cls||"") + '"' + (extra||"") + ' src="' + utils.quote_attr(src) + '" ' + (srcset ? 'srcset="' + utils.quote_attr(srcset) + '" ' : '') + 'alt="' + utils.quote_attr(token.altText) + '" title="' + utils.quote_attr(tooltip) + '">';
|
||||
}
|
||||
|
||||
if ( token.isLink ) {
|
||||
|
@ -753,7 +749,7 @@ FFZ.prototype.tokenize_title_emotes = function(tokens) {
|
|||
data = data.emoticon_sets[0];
|
||||
for(var i=0; i < data.length; i++) {
|
||||
var em = data[i];
|
||||
emotes.push({regex: em.code, url: TWITCH_BASE + em.id + "/1.0"});
|
||||
emotes.push({regex: em.code, url: utils.TWITCH_BASE + em.id + "/1.0"});
|
||||
}
|
||||
|
||||
if ( f._cindex )
|
||||
|
@ -849,7 +845,7 @@ FFZ.prototype.tokenize_emotes = function(user, room, tokens, do_report) {
|
|||
bits.push(eo);
|
||||
|
||||
if ( do_report && room )
|
||||
f.add_usage(room, emote.id);
|
||||
f.add_usage(room, emote);
|
||||
|
||||
} else
|
||||
bits.push(bit);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
constants = require("../constants"),
|
||||
styles = require("../styles");
|
||||
constants = require("../constants");
|
||||
//styles = require("../styles");
|
||||
|
||||
|
||||
// ---------------------
|
||||
|
@ -144,6 +144,9 @@ FFZ.settings_info.dark_twitch = {
|
|||
model && model.set('darkMode', true);
|
||||
} else
|
||||
model && model.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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -211,7 +214,6 @@ FFZ.prototype._load_dark_css = function() {
|
|||
|
||||
s.id = "ffz-dark-css";
|
||||
s.setAttribute('rel', 'stylesheet');
|
||||
s.setAttribute('href', constants.SERVER + "script/dark.css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
|
||||
s.onerror = "this.href = this.href + '_';"
|
||||
s.setAttribute('href', constants.DIRECT_SERVER + "script/dark.css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
|
||||
document.head.appendChild(s);
|
||||
}
|
|
@ -52,7 +52,7 @@ FFZ.prototype.setup_following_count = function(has_ember) {
|
|||
|
||||
// If we don't have Ember, no point in trying this stuff.
|
||||
if ( ! has_ember )
|
||||
return this._update_following_count();
|
||||
return this._following_get_me();
|
||||
|
||||
this.log("Connecting to Live Streams model.");
|
||||
var Stream = window.App && App.__container__.resolve('model:stream');
|
||||
|
@ -80,6 +80,27 @@ FFZ.prototype.setup_following_count = function(has_ember) {
|
|||
}
|
||||
|
||||
|
||||
FFZ.prototype._following_get_me = function(tries) {
|
||||
// get_user doesn't properly return an oauth token any longer, so we need to get me manually.
|
||||
if ( ! window.Twitch )
|
||||
// Wait around till the API shows up.
|
||||
return setTimeout(this._following_get_me.bind(this, tries), Math.floor(2000*Math.random()) + 500);
|
||||
|
||||
var f = this;
|
||||
Twitch.api.get("/api/me").done(function(data) {
|
||||
f.log("Fetched User Data -- " + (data.name || data.login));
|
||||
f.__user = data;
|
||||
f._update_following_count();
|
||||
|
||||
}).fail(function() {
|
||||
tries = (tries||0) + 1;
|
||||
if ( tries < 5 )
|
||||
return setTimeout(f._following_get_me.bind(f, tries), Math.floor(2000*Math.random()) + 500);
|
||||
f.log("Failed to get proper user object.");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._schedule_following_count = function() {
|
||||
if ( ! this.settings.following_count ) {
|
||||
if ( this._following_count_timer ) {
|
||||
|
@ -111,8 +132,13 @@ FFZ.prototype._update_following_count = function() {
|
|||
|
||||
if ( Live )
|
||||
Live.load();
|
||||
else
|
||||
Twitch.api && Twitch.api.get("streams/followed", {limit:5, offset:0}, {version:3})
|
||||
else {
|
||||
var a = {},
|
||||
u = this.get_user();
|
||||
|
||||
a.Authorization = "OAuth " + u.chat_oauth_token;
|
||||
|
||||
Twitch.api && Twitch.api.get("streams/followed", {limit:20, offset:0}, {version:3, headers: a})
|
||||
.done(function(data) {
|
||||
f._draw_following_count(data._total);
|
||||
f._draw_following_channels(data.streams, data._total);
|
||||
|
@ -120,6 +146,7 @@ FFZ.prototype._update_following_count = function() {
|
|||
f._draw_following_count();
|
||||
f._draw_following_channels();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,8 +2,6 @@ var FFZ = window.FrankerFaceZ,
|
|||
constants = require('../constants'),
|
||||
utils = require('../utils'),
|
||||
|
||||
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
|
||||
|
||||
fix_menu_position = function(container) {
|
||||
var swapped = document.body.classList.contains('ffz-sidebar-swap') && ! document.body.classList.contains('ffz-portrait');
|
||||
|
||||
|
@ -63,7 +61,8 @@ FFZ.prototype.setup_menu = function() {
|
|||
|
||||
this.log("Hooking the Ember Chat Settings view.");
|
||||
|
||||
var Settings = window.App && App.__container__.resolve('view:settings');
|
||||
var Settings = window.App && App.__container__.resolve('view:settings'),
|
||||
Layout = App.__container__.lookup('controller:layout');
|
||||
|
||||
if ( ! Settings )
|
||||
return;
|
||||
|
@ -155,6 +154,12 @@ FFZ.prototype.setup_menu = function() {
|
|||
|
||||
menu.appendChild(header);
|
||||
menu.appendChild(content);
|
||||
|
||||
// Maximum Height
|
||||
var e = el.querySelector('.chat-settings');
|
||||
if ( Layout && e )
|
||||
e.style.maxHeight = (Layout.get('windowHeight') - 90) + 'px';
|
||||
|
||||
},
|
||||
|
||||
ffzTeardown: function() {
|
||||
|
@ -162,6 +167,15 @@ FFZ.prototype.setup_menu = function() {
|
|||
}
|
||||
});
|
||||
|
||||
// Maximum height~!
|
||||
if ( Layout )
|
||||
Layout.addObserver('windowHeight', function() {
|
||||
var el = document.querySelector('.ember-chat .chat-settings');
|
||||
if ( el )
|
||||
el.style.maxHeight = (Layout.get('windowHeight') - 90) + 'px';
|
||||
});
|
||||
|
||||
|
||||
// For some reason, this doesn't work unless we create an instance of the
|
||||
// chat settings view and then destroy it immediately.
|
||||
try {
|
||||
|
@ -224,6 +238,9 @@ FFZ.prototype.build_ui_popup = function(view) {
|
|||
|
||||
container.classList.toggle('dark', dark);
|
||||
|
||||
// Stuff
|
||||
jQuery(inner).find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 's')});
|
||||
|
||||
|
||||
// Menu Container
|
||||
var sub_container = document.createElement('div');
|
||||
|
@ -313,7 +330,7 @@ FFZ.prototype.build_ui_popup = function(view) {
|
|||
link.title = page.name;
|
||||
link.innerHTML = page.icon;
|
||||
|
||||
jQuery(link).tipsy();
|
||||
jQuery(link).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
|
||||
link.addEventListener("click", this._ui_change_page.bind(this, view, inner, menu, sub_container, key));
|
||||
|
||||
|
@ -435,11 +452,11 @@ FFZ.menu_pages.channel = {
|
|||
|
||||
var s = document.createElement('span'),
|
||||
can_use = is_subscribed || !emote.subscriber_only,
|
||||
img_set = 'image-set(url("' + TWITCH_BASE + emote.id + '/1.0") 1x, url("' + TWITCH_BASE + emote.id + '/2.0") 2x, url("' + TWITCH_BASE + emote.id + '/3.0") 4x)';
|
||||
img_set = 'image-set(url("' + constants.TWITCH_BASE + emote.id + '/1.0") 1x, url("' + constants.TWITCH_BASE + emote.id + '/2.0") 2x), url("' + constants.TWITCH_BASE + emote.id + '/3.0") 4x)';
|
||||
|
||||
s.className = 'emoticon tooltip' + (!can_use ? " locked" : "");
|
||||
s.className = 'emoticon html-tooltip' + (!can_use ? " locked" : "");
|
||||
|
||||
s.style.backgroundImage = 'url("' + TWITCH_BASE + emote.id + '/1.0")';
|
||||
s.style.backgroundImage = 'url("' + constants.TWITCH_BASE + emote.id + '/1.0")';
|
||||
s.style.backgroundImage = '-webkit-' + img_set;
|
||||
s.style.backgroundImage = '-moz-' + img_set;
|
||||
s.style.backgroundImage = '-ms-' + img_set;
|
||||
|
@ -447,7 +464,7 @@ FFZ.menu_pages.channel = {
|
|||
|
||||
s.style.width = emote.width + "px";
|
||||
s.style.height = emote.height + "px";
|
||||
s.title = emote.regex;
|
||||
s.title = (this.settings.emote_image_hover ? '<img class="emoticon ffz-image-hover" src="' + constants.TWITCH_BASE + emote.id + '/3.0?_=preview">' : '') + emote.regex;
|
||||
|
||||
s.addEventListener('click', function(can_use, id, code, e) {
|
||||
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons )
|
||||
|
@ -599,7 +616,7 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub
|
|||
|
||||
c++;
|
||||
var s = document.createElement('span');
|
||||
s.className = 'emoticon tooltip';
|
||||
s.className = 'emoticon html-tooltip';
|
||||
s.style.backgroundImage = 'url("' + emote.urls[1] + '")';
|
||||
|
||||
if ( srcset ) {
|
||||
|
|
|
@ -2,7 +2,6 @@ var FFZ = window.FrankerFaceZ,
|
|||
constants = require("../constants"),
|
||||
utils = require("../utils"),
|
||||
|
||||
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
|
||||
BANNED_SETS = {"00000turbo":true};
|
||||
|
||||
|
||||
|
@ -166,11 +165,14 @@ FFZ.menu_pages.myemotes = {
|
|||
if ( (settings === 1 && ! emoji.tw) || (settings === 2 && ! emoji.noto) )
|
||||
continue;
|
||||
|
||||
em.className = 'emoticon tooltip';
|
||||
em.title = 'Emoji: ' + emoji.raw + '\nName: ' + emoji.name + (emoji.short_name ? '\nShort Name: :' + emoji.short_name + ':' : '');
|
||||
var src = settings === 2 ? emoji.noto_src : emoji.tw_src,
|
||||
image = this.settings.emote_image_hover ? '<img class="emoticon ffz-image-hover" src="' + src + '">' : '';
|
||||
|
||||
em.className = 'emoticon emoji html-tooltip';
|
||||
em.title = image + 'Emoji: ' + emoji.raw + '<br>Name: ' + emoji.name + (emoji.short_name ? '<br>Short Name: :' + emoji.short_name + ':' : '');
|
||||
em.addEventListener('click', this._add_emote.bind(this, view, emoji.raw));
|
||||
|
||||
em.style.backgroundImage = 'url("' + (settings === 2 ? emoji.noto_src : emoji.tw_src) + '")';
|
||||
em.style.backgroundImage = 'url("' + src + '")';
|
||||
em.style.backgroundSize = "18px";
|
||||
|
||||
menu.appendChild(em);
|
||||
|
@ -237,21 +239,21 @@ FFZ.menu_pages.myemotes = {
|
|||
code = constants.KNOWN_CODES[emote.code] || emote.code,
|
||||
|
||||
em = document.createElement('span'),
|
||||
img_set = 'image-set(url("' + TWITCH_BASE + emote.id + '/1.0") 1x, url("' + TWITCH_BASE + emote.id + '/2.0") 2x, url("' + TWITCH_BASE + emote.id + '/3.0") 4x)';
|
||||
img_set = 'image-set(url("' + constants.TWITCH_BASE + emote.id + '/1.0") 1x, url("' + constants.TWITCH_BASE + emote.id + '/2.0") 2x, url("' + constants.TWITCH_BASE + emote.id + '/3.0") 4x)';
|
||||
|
||||
em.className = 'emoticon tooltip';
|
||||
em.className = 'emoticon html-tooltip';
|
||||
|
||||
if ( this.settings.replace_bad_emotes && constants.EMOTE_REPLACEMENTS[emote.id] ) {
|
||||
em.style.backgroundImage = 'url("' + constants.EMOTE_REPLACEMENT_BASE + constants.EMOTE_REPLACEMENTS[emote.id] + '")';
|
||||
} else {
|
||||
em.style.backgroundImage = 'url("' + TWITCH_BASE + emote.id + '/1.0")';
|
||||
em.style.backgroundImage = 'url("' + constants.TWITCH_BASE + emote.id + '/1.0")';
|
||||
em.style.backgroundImage = '-webkit-' + img_set;
|
||||
em.style.backgroundImage = '-moz-' + img_set;
|
||||
em.style.backgroundImage = '-ms-' + img_set;
|
||||
em.style.backgroudnImage = img_set;
|
||||
}
|
||||
|
||||
em.title = code;
|
||||
em.title = (this.settings.emote_image_hover ? '<img class="emoticon ffz-image-hover" src="' + constants.TWITCH_BASE + emote.id + '/3.0?_=preview">' : '') + code;
|
||||
em.addEventListener("click", function(id, c, e) {
|
||||
e.preventDefault();
|
||||
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons )
|
||||
|
@ -316,7 +318,7 @@ FFZ.menu_pages.myemotes = {
|
|||
|
||||
img_set += ')';
|
||||
|
||||
em.className = 'emoticon tooltip';
|
||||
em.className = 'emoticon html-tooltip';
|
||||
em.style.backgroundImage = 'url("' + emote.urls[1] + '")';
|
||||
em.style.backgroundImage = '-webkit-' + img_set;
|
||||
em.style.backgroundImage = '-moz-' + img_set;
|
||||
|
|
|
@ -181,6 +181,11 @@ FFZ.prototype.show_notification = function(message, title, tag, timeout, on_clic
|
|||
// ---------------------
|
||||
|
||||
FFZ.prototype.show_message = function(message) {
|
||||
if ( ! window.jQuery || ! window.jQuery.noty || ! jQuery.noty.themes.ffzTheme ) {
|
||||
setTimeout(this.show_message.bind(this, message), 50);
|
||||
return;
|
||||
}
|
||||
|
||||
window.noty({
|
||||
text: message,
|
||||
theme: "ffzTheme",
|
||||
|
|
|
@ -1,18 +1,33 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
constants = require('../constants'),
|
||||
styles = require('../styles');
|
||||
utils = require('../utils'),
|
||||
constants = require('../constants');
|
||||
styles = require('../compiled_styles');
|
||||
|
||||
FFZ.prototype.setup_css = function() {
|
||||
document.body.classList.toggle('ffz-flip-dashboard', this.settings.flip_dashboard);
|
||||
|
||||
this.log("Injecting main FrankerFaceZ CSS.");
|
||||
|
||||
var s = this._main_style = document.createElement('style');
|
||||
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.css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
|
||||
document.head.appendChild(s);
|
||||
|
||||
this.log("Readying toggleable styles.");
|
||||
this._toggle_style_state = {};
|
||||
|
||||
s = this._toggle_style = document.createElement('style');
|
||||
s.type = "text/css";
|
||||
s.id = "ffz-toggle-css";
|
||||
document.head.appendChild(s);
|
||||
|
||||
/*var s = this._main_style = document.createElement('style');
|
||||
|
||||
s.textContent = styles.style;
|
||||
s.id = "ffz-ui-css";
|
||||
s.id = "ffz-main-css";
|
||||
|
||||
document.head.appendChild(s);
|
||||
document.head.appendChild(s);*/
|
||||
|
||||
if ( window.jQuery && jQuery.noty )
|
||||
jQuery.noty.themes.ffzTheme = {
|
||||
|
@ -25,4 +40,15 @@ FFZ.prototype.setup_css = function() {
|
|||
onClose: function() {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.toggle_style = function(key, enabled) {
|
||||
var state = this._toggle_style_state[key];
|
||||
if ( (enabled && state) || (!enabled && !state) )
|
||||
return;
|
||||
|
||||
this._toggle_style_state[key] = enabled;
|
||||
|
||||
utils.update_css(this._toggle_style, key, enabled ? styles[key] || null : null);
|
||||
}
|
|
@ -84,7 +84,7 @@ FFZ.prototype._update_subscribers = function() {
|
|||
});
|
||||
|
||||
cont.appendChild(stat);
|
||||
jQuery(stat).tipsy(f.is_dashboard ? {"gravity":"s"} : undefined);
|
||||
jQuery(stat).tipsy({gravity: f.is_dashboard ? "s" : utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
|
||||
el.innerHTML = sub_count;
|
||||
|
|
38
src/ui/tooltips.js
Normal file
38
src/ui/tooltips.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require('../utils'),
|
||||
constants = require('../constants');
|
||||
|
||||
|
||||
// ---------------------
|
||||
// Initialization
|
||||
// ---------------------
|
||||
|
||||
FFZ.prototype.fix_tooltips = function() {
|
||||
// First, override the tooltip mixin.
|
||||
var TipsyTooltip = window.App && App.__container__.resolve('component:tipsy-tooltip');
|
||||
if ( TipsyTooltip ) {
|
||||
this.log("Modifying Tipsy-Tooltip component to use gravity.");
|
||||
TipsyTooltip.reopen({
|
||||
didInsertElement: function() {
|
||||
var gravity = this.get("gravity");
|
||||
if ( ! gravity || typeof gravity === "string" )
|
||||
gravity = utils.tooltip_placement(constants.TOOLTIP_DISTANCE, gravity || 's');
|
||||
|
||||
this.$().tipsy({
|
||||
gravity: gravity
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Iterate all existing tipsy stuff~!
|
||||
this.log('Fixing already existing tooltips.');
|
||||
if ( ! window.jQuery || ! jQuery.cache )
|
||||
return;
|
||||
|
||||
for(var obj_id in jQuery.cache) {
|
||||
var obj = jQuery.cache[obj_id];
|
||||
if ( obj && obj.data && obj.data.tipsy && obj.data.tipsy.options && typeof obj.data.tipsy.options.gravity !== "function" )
|
||||
obj.data.tipsy.options.gravity = utils.tooltip_placement(constants.TOOLTIP_DISTANCE, obj.data.tipsy.options.gravity || 's');
|
||||
}
|
||||
}
|
|
@ -66,6 +66,6 @@ FFZ.ws_commands.viewers = function(data) {
|
|||
view_count.innerHTML = content;
|
||||
|
||||
parent.appendChild(view_count);
|
||||
jQuery(view_count).tipsy(this.is_dashboard ? {"gravity":"s"} : undefined);
|
||||
jQuery(view_count).tipsy({gravity: this.is_dashboard ? "s" : utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
}
|
110
src/utils.js
110
src/utils.js
|
@ -42,6 +42,9 @@ var sanitize_el = document.createElement('span'),
|
|||
date_regex = /^(\d{4}|\+\d{6})(?:-?(\d{2})(?:-?(\d{2})(?:T(\d{2})(?::?(\d{2})(?::?(\d{2})(?:(?:\.|,)(\d{1,}))?)?)?(Z|([\-+])(\d{2})(?::?(\d{2}))?)?)?)?)?$/,
|
||||
|
||||
parse_date = function(str) {
|
||||
if ( typeof str === "number" )
|
||||
return new Date(str);
|
||||
|
||||
var parts = str.match(date_regex);
|
||||
if ( ! parts )
|
||||
return null;
|
||||
|
@ -179,10 +182,98 @@ var sanitize_el = document.createElement('span'),
|
|||
out = es[variant] = r.join("-");
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
// Twitch Emote Tooltips
|
||||
|
||||
SRCSETS = {},
|
||||
build_srcset = function(id) {
|
||||
if ( SRCSETS[id] )
|
||||
return SRCSETS[id];
|
||||
var out = SRCSETS[id] = constants.TWITCH_BASE + id + "/1.0 1x, " + constants.TWITCH_BASE + id + "/2.0 2x, " + constants.TWITCH_BASE + id + "/3.0 4x";
|
||||
return out;
|
||||
},
|
||||
|
||||
|
||||
data_to_tooltip = function(data) {
|
||||
var emote_set = data.set,
|
||||
set_type = data.set_type,
|
||||
|
||||
f = FFZ.get(),
|
||||
image = '';
|
||||
|
||||
if ( data.id && f.settings.emote_image_hover )
|
||||
image = '<img class="emoticon ffz-image-hover" src="' + constants.TWITCH_BASE + data.id + '/3.0?_=preview">';
|
||||
|
||||
if ( set_type === undefined )
|
||||
set_type = "Channel";
|
||||
|
||||
if ( ! emote_set )
|
||||
return image + data.code;
|
||||
|
||||
else if ( emote_set === "--global--" ) {
|
||||
emote_set = "Twitch Global";
|
||||
set_type = null;
|
||||
|
||||
} else if ( emote_set == "--twitch-turbo--" || emote_set == "turbo" || emote_set == "--turbo-faces--" ) {
|
||||
emote_set = "Twitch Turbo";
|
||||
set_type = null;
|
||||
}
|
||||
|
||||
return image + "Emoticon: " + data.code + "<br>" + (set_type ? set_type + ": " : "") + emote_set;
|
||||
},
|
||||
|
||||
build_tooltip = function(id, force_update, code) {
|
||||
var emote_data = this._twitch_emotes[id];
|
||||
|
||||
if ( ! emote_data && code ) {
|
||||
var set_id = this._twitch_emote_to_set[id];
|
||||
if ( set_id ) {
|
||||
emote_data = this._twitch_emotes[id] = {
|
||||
code: code,
|
||||
id: id,
|
||||
set: this._twitch_set_to_channel[set_id],
|
||||
set_id: set_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! emote_data )
|
||||
return "???";
|
||||
|
||||
if ( typeof emote_data == "string" )
|
||||
return emote_data;
|
||||
|
||||
if ( ! force_update && emote_data.tooltip )
|
||||
return emote_data.tooltip;
|
||||
|
||||
return emote_data.tooltip = data_to_tooltip(emote_data);
|
||||
},
|
||||
|
||||
load_emote_data = function(id, code, success, data) {
|
||||
if ( ! success )
|
||||
return code;
|
||||
|
||||
if ( code )
|
||||
data.code = code;
|
||||
|
||||
this._twitch_emotes[id] = data;
|
||||
var tooltip = build_tooltip.bind(this)(id);
|
||||
|
||||
var images = document.querySelectorAll('img[data-emote="' + id + '"]');
|
||||
for(var x=0; x < images.length; x++)
|
||||
images[x].title = tooltip;
|
||||
|
||||
return tooltip;
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
build_srcset: build_srcset,
|
||||
build_tooltip: build_tooltip,
|
||||
load_emote_data: load_emote_data,
|
||||
|
||||
|
||||
update_css: function(element, id, css) {
|
||||
var all = element.innerHTML,
|
||||
start = "/*BEGIN " + id + "*/",
|
||||
|
@ -204,6 +295,25 @@ module.exports = {
|
|||
},
|
||||
|
||||
|
||||
tooltip_placement: function(margin, prefer) {
|
||||
return function() {
|
||||
var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)},
|
||||
$this = $(this),
|
||||
half_width = $this.width() / 2,
|
||||
half_height = $this.height() / 2,
|
||||
boundTop = $(document).scrollTop() + half_height + (margin*2),
|
||||
boundLeft = $(document).scrollLeft() + half_width + margin;
|
||||
|
||||
if ($this.offset().top < boundTop) dir.ns = 'n';
|
||||
if ($this.offset().left < boundLeft) dir.ew = 'w';
|
||||
if ($(window).width() + $(document).scrollLeft() - ($this.offset().left + half_width) < margin) dir.ew = 'e';
|
||||
if ($(window).height() + $(document).scrollTop() - ($this.offset().top + half_height) < (2*margin)) dir.ns = 's';
|
||||
|
||||
return dir.ns + (dir.ew ? dir.ew : '');
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
splitIRCMessage: splitIRCMessage,
|
||||
parseIRCTags: parseIRCTags,
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue