1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-29 07:45:33 +00:00

Refactored colors. Fixed scrolling on mobile. Added image preview on hover.

This commit is contained in:
SirStendec 2015-07-18 21:10:27 -04:00
parent 2bc2b7003b
commit 02b0a95bd0
22 changed files with 2121 additions and 425 deletions

View file

@ -206,12 +206,18 @@
.ffz-dark input.text,
.ffz-dark input.string,
.ffz-dark textarea,
.ffz-dark select,
.ffz-dark option,
.ffz-dark .directory_header #custom_filter input {
background-color: rgba(255,255,255,0.05);
border-color: rgba(255,255,255,0.1);
color: #fff;
}
.ffz-dark option {
background-color: #191919;
}
/* Other stuff */

38
image-proxy.html Normal file
View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<style>
html,body{overflow:hidden; margin:0; padding:0; background:transparent; height: 100%; position: relative }
img {
max-width: 186px; max-height: 186px;
position: absolute;
top: 50%; left: 50%;
transform: translateX(-50%) translateY(-50%);
}
</style>
<script type="text/javascript">window.onload=function(){
var IMGUR_REGEX = /(?:https?:\/\/)?(?:i\.)?imgur\.com\/(?:gallery\/)?([A-Za-z0-9]+)(?:\.(.*))?/,
GYAZO_REGEX = /(?:https?:\/\/)?(?:i\.)?gyazo\.com\/([a-z0-9]+)/,
YOUTUBE_REGEX = /^(?:https?:\/\/)?(?:m\.|www\.)?youtu(?:be\.com|\.be)\/(?:v\/|watch\/|.*?(?:embed|watch).*?v=)?([a-zA-Z0-9\-_]+)$/;
var url = location.search.substr(1),
image_url = url;
imgur = url.match(IMGUR_REGEX);
if ( imgur )
image_url = 'http://i.imgur.com/' + imgur[1] + 't.' + (imgur[2] || "png");
else {
var yt = url.match(YOUTUBE_REGEX);
if ( yt ) {
image_url = 'http://img.youtube.com/vi/' + yt[1] + '/1.jpg'
} else {
var gyazo = url.match(GYAZO_REGEX);
if ( gyazo )
image_url = 'http://i.gyazo.com/' + gyazo[1] + '.png';
}
}
var el = document.createElement('img');
el.src = image_url;
document.body.appendChild(el);
}</script>

1216
script.js

File diff suppressed because it is too large Load diff

14
script.min.js vendored

File diff suppressed because one or more lines are too long

634
src/colors.js Normal file
View file

@ -0,0 +1,634 @@
var FFZ = window.FrankerFaceZ,
hue2rgb = function(p, q, t) {
if ( t < 0 ) t += 1;
if ( t > 1 ) t -= 1;
if ( t < 1/6 )
return p + (q-p) * 6 * t;
if ( t < 1/2 )
return q;
if ( t < 2/3 )
return p + (q-p) * (2/3 - t) * 6;
return p;
};
// ---------------------
// Settings
// ---------------------
FFZ.settings_info.fix_color = {
type: "select",
options: {
'-1': "Disabled",
0: "Default Colors",
1: "Luv Adjustment",
2: "HSL Adjustment (Depreciated)",
3: "HSV Adjustment (Depreciated)",
4: "RGB Adjustment (Depreciated)"
},
value: '1',
category: "Chat Appearance",
no_bttv: true,
name: "Username Colors - Brightness",
help: "Ensure that username colors contrast with the background enough to be readable.",
process_value: function(val) {
// Load legacy setting.
if ( val === false )
return '0';
else if ( val === true )
return '1';
return val;
},
on_update: function(val) {
document.body.classList.toggle("ffz-chat-colors", !this.has_bttv && val !== '-1');
document.body.classList.toggle("ffz-chat-colors-gray", !this.has_bttv && (val === '-1'));
if ( ! this.has_bttv && val !== '-1' )
this._rebuild_colors();
}
};
FFZ.settings_info.color_blind = {
type: "select",
options: {
0: "Disabled",
protanope: "Protanope",
deuteranope: "Deuteranope",
tritanope: "Tritanope"
},
value: '0',
category: "Chat Appearance",
no_bttv: true,
name: "Username Colors - Color Blindness",
help: "Adjust username colors in an attempt to make them more distinct for people with color blindness.",
on_update: function(val) {
if ( ! this.has_bttv && this.settings.fix_color !== '-1' )
this._rebuild_colors();
}
};
// --------------------
// Initialization
// --------------------
FFZ.prototype.setup_colors = function() {
this.log("Preparing color-alteration style element.");
this._colors = {};
var s = this._color_style = document.createElement('style');
s.id = 'ffz-style-username-colors';
s.type = 'text/css';
document.head.appendChild(s);
}
// -----------------------
// Color Handling Classes
// -----------------------
FFZ.Color = {};
FFZ.Color.CVDMatrix = {
protanope: [ // reds are greatly reduced (1% men)
0.0, 2.02344, -2.52581,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0
],
deuteranope: [ // greens are greatly reduced (1% men)
1.0, 0.0, 0.0,
0.494207, 0.0, 1.24827,
0.0, 0.0, 1.0
],
tritanope: [ // blues are greatly reduced (0.003% population)
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
-0.395913, 0.801109, 0.0
]
}
var RGBColor = FFZ.Color.RGB = function(r, g, b) {
this.r = r||0; this.g = g||0; this.b = b||0;
};
var HSVColor = FFZ.Color.HSV = function(h, s, v) {
this.h = h||0; this.s = s||0; this.v = v||0;
};
var HSLColor = FFZ.Color.HSL = function(h, s, l) {
this.h = h||0; this.s = s||0; this.l = l||0;
};
var XYZColor = FFZ.Color.XYZ = function(x, y, z) {
this.x = x||0; this.y = y||0; this.z = z||0;
};
var LUVColor = FFZ.Color.LUV = function(l, u, v) {
this.l = l||0; this.u = u||0; this.v = v||0;
};
// RGB Colors
RGBColor.prototype.eq = function(rgb) {
return rgb.r === this.r && rgb.g === this.g && rgb.b === this.b;
}
RGBColor.fromHex = function(code) {
var raw = parseInt(code.charAt(0) === '#' ? code.substr(1) : code, 16);
return new RGBColor(
(raw >> 16), // Red
(raw >> 8 & 0x00FF), // Green
(raw & 0x0000FF) // Blue
)
}
RGBColor.fromHSV = function(h, s, v) {
var r, g, b,
i = Math.floor(h * 6),
f = h * 6 - i,
p = v * (1 - s),
q = v * (1 - f * s),
t = v * (1 - (1 - f) * s);
switch(i % 6) {
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q;
}
return new RGBColor(
Math.round(Math.min(Math.max(0, r*255), 255)),
Math.round(Math.min(Math.max(0, g*255), 255)),
Math.round(Math.min(Math.max(0, b*255), 255))
);
}
RGBColor.fromXYZ = function(x, y, z) {
var R = 3.240479 * x - 1.537150 * y - 0.498535 * z,
G = -0.969256 * x + 1.875992 * y + 0.041556 * z,
B = 0.055648 * x - 0.204043 * y + 1.057311 * z;
// Make sure we end up in a real color space
return new RGBColor(
Math.max(0, Math.min(255, 255 * XYZColor.channelConverter(R))),
Math.max(0, Math.min(255, 255 * XYZColor.channelConverter(G))),
Math.max(0, Math.min(255, 255 * XYZColor.channelConverter(B)))
);
}
RGBColor.fromHSL = function(h, s, l) {
if ( s === 0 ) {
var v = Math.round(Math.min(Math.max(0, 255*l), 255));
return new RGBColor(v, v, v);
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s,
p = 2 * l - q;
return new RGBColor(
Math.round(Math.min(Math.max(0, 255 * hue2rgb(p, q, h + 1/3)), 255)),
Math.round(Math.min(Math.max(0, 255 * hue2rgb(p, q, h)), 255)),
Math.round(Math.min(Math.max(0, 255 * hue2rgb(p, q, h - 1/3)), 255))
);
}
RGBColor.prototype.toHSV = function() { return HSVColor.fromRGB(this.r, this.g, this.b); }
RGBColor.prototype.toHSL = function() { return HSLColor.fromRGB(this.r, this.g, this.b); }
RGBColor.prototype.toCSS = function() { return "rgb(" + Math.round(this.r) + "," + Math.round(this.g) + "," + Math.round(this.b) + ")"; }
RGBColor.prototype.toXYZ = function() { return XYZColor.fromRGB(this.r, this.g, this.b); }
RGBColor.prototype.toLUV = function() { return this.toXYZ().toLUV(); }
RGBColor.prototype.luminance = function() {
var rgb = [this.r / 255, this.g / 255, this.b / 255];
for (var i =0, l = rgb.length; i < l; i++) {
if (rgb[i] <= 0.03928) {
rgb[i] = rgb[i] / 12.92;
} else {
rgb[i] = Math.pow( ((rgb[i]+0.055)/1.055), 2.4 );
}
}
return (0.2126 * rgb[0]) + (0.7152 * rgb[1]) + (0.0722 * rgb[2]);
}
RGBColor.prototype.brighten = function(amount) {
amount = typeof amount === "number" ? amount : 1;
amount = Math.round(255 * (amount / 100));
return new RGBColor(
Math.max(0, Math.min(255, this.r + amount)),
Math.max(0, Math.min(255, this.g + amount)),
Math.max(0, Math.min(255, this.b + amount))
);
}
RGBColor.prototype.daltonize = function(type, amount) {
amount = typeof amount === "number" ? amount : 1.0;
var cvd;
if ( typeof type === "string" ) {
if ( FFZ.Color.CVDMatrix.hasOwnProperty(type) )
cvd = FFZ.Color.CVDMatrix[type];
else
throw "Invalid CVD matrix.";
} else
cvd = type;
var cvd_a = cvd[0], cvd_b = cvd[1], cvd_c = cvd[2],
cvd_d = cvd[3], cvd_e = cvd[4], cvd_f = cvd[5],
cvd_g = cvd[6], cvd_h = cvd[7], cvd_i = cvd[8],
L, M, S, l, m, s, R, G, B, RR, GG, BB;
// RGB to LMS matrix conversion
L = (17.8824 * this.r) + (43.5161 * this.g) + (4.11935 * this.b);
M = (3.45565 * this.r) + (27.1554 * this.g) + (3.86714 * this.b);
S = (0.0299566 * this.r) + (0.184309 * this.g) + (1.46709 * this.b);
// Simulate color blindness
l = (cvd_a * L) + (cvd_b * M) + (cvd_c * S);
m = (cvd_d * L) + (cvd_e * M) + (cvd_f * S);
s = (cvd_g * L) + (cvd_h * M) + (cvd_i * S);
// LMS to RGB matrix conversion
R = (0.0809444479 * l) + (-0.130504409 * m) + (0.116721066 * s);
G = (-0.0102485335 * l) + (0.0540193266 * m) + (-0.113614708 * s);
B = (-0.000365296938 * l) + (-0.00412161469 * m) + (0.693511405 * s);
// Isolate invisible colors to color vision deficiency (calculate error matrix)
R = this.r - R;
G = this.g - G;
B = this.b - B;
// Shift colors towards visible spectrum (apply error modifications)
RR = (0.0 * R) + (0.0 * G) + (0.0 * B);
GG = (0.7 * R) + (1.0 * G) + (0.0 * B);
BB = (0.7 * R) + (0.0 * G) + (1.0 * B);
// Add compensation to original values
R = Math.min(Math.max(0, RR + this.r), 255);
G = Math.min(Math.max(0, GG + this.g), 255);
B = Math.min(Math.max(0, BB + this.b), 255);
return new RGBColor(R, G, B);
}
RGBColor.prototype._r = function(r) { return new RGBColor(r, this.g, this.b); }
RGBColor.prototype._g = function(g) { return new RGBColor(this.r, g, this.b); }
RGBColor.prototype._b = function(b) { return new RGBColor(this.r, this.g, b); }
// HSL Colors
HSLColor.prototype.eq = function(hsl) {
return hsl.h === this.h && hsl.s === this.s && hsl.l === this.l;
}
HSLColor.fromRGB = function(r, g, b) {
r /= 255; g /= 255; b /= 255;
var max = Math.max(r,g,b),
min = Math.min(r,g,b),
h, s, l = Math.min(Math.max(0, (max+min) / 2), 1),
d = Math.min(Math.max(0, max - min), 1);
if ( d === 0 )
h = s = 0;
else {
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
}
h /= 6;
}
return new HSLColor(h, s, l);
}
HSLColor.prototype.toRGB = function() { return RGBColor.fromHSL(this.h, this.s, this.l); }
HSLColor.prototype.toCSS = function() { return "hsl(" + Math.round(this.h*360) + "," + Math.round(this.s*100) + "%," + Math.round(this.l*100) + "%)"; }
HSLColor.prototype.toHSV = function() { return RGBColor.fromHSL(this.h, this.s, this.l).toHSV(); }
HSLColor.prototype.toXYZ = function() { return RGBColor.fromHSL(this.h, this.s, this.l).toXYZ(); }
HSLColor.prototype.toLUV = function() { return RGBColor.fromHSL(this.h, this.s, this.l).toLUV(); }
HSLColor.prototype._h = function(h) { return new HSLColor(h, this.s, this.l); }
HSLColor.prototype._s = function(s) { return new HSLColor(this.h, s, this.l); }
HSLColor.prototype._l = function(l) { return new HSLColor(this.h, this.s, l); }
// HSV Colors
HSVColor.prototype.eq = function(hsv) { return hsv.h === this.h && hsv.s === this.s && hsv.v === this.v; }
HSVColor.fromRGB = function(r, g, b) {
r /= 255; g /= 255; b /= 255;
var max = Math.max(r, g, b),
min = Math.min(r, g, b),
d = Math.min(Math.max(0, max - min), 1),
h,
s = max === 0 ? 0 : d / max,
v = max;
if ( d === 0 )
h = 0;
else {
switch(max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
}
h /= 6;
}
return new HSVColor(h, s, v);
}
HSVColor.prototype.toRGB = function() { return RGBColor.fromHSV(this.h, this.s, this.v); }
HSVColor.prototype.toHSL = function() { return RGBColor.fromHSV(this.h, this.s, this.v).toHSL(); }
HSVColor.prototype.toXYZ = function() { return RGBColor.fromHSV(this.h, this.s, this.v).toXYZ(); }
HSVColor.prototype.toLUV = function() { return RGBColor.fromHSV(this.h, this.s, this.v).toLUV(); }
HSVColor.prototype._h = function(h) { return new HSVColor(h, this.s, this.v); }
HSVColor.prototype._s = function(s) { return new HSVColor(this.h, s, this.v); }
HSVColor.prototype._v = function(v) { return new HSVColor(this.h, this.s, v); }
// XYZ Colors
RGBColor.channelConverter = function (channel) {
// http://www.brucelindbloom.com/Eqn_RGB_to_XYZ.html
// This converts rgb 8bit to rgb linear, lazy because the other algorithm is really really dumb
return Math.pow(channel, 2.2);
// CSS Colors Level 4 says 0.03928, Bruce Lindbloom who cared to write all algos says 0.04045, used bruce because whynawt
return (channel <= 0.04045) ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);
};
XYZColor.channelConverter = function (channel) {
// Using lazy conversion in the other direction as well
return Math.pow(channel, 1/2.2);
// I'm honestly not sure about 0.0031308, I've only seen it referenced on Bruce Lindbloom's site
return (channel <= 0.0031308) ? channel * 12.92 : Math.pow(1.055 * channel, 1/2.4) - 0.055;
};
XYZColor.prototype.eq = function(xyz) { return xyz.x === this.x && xyz.y === this.y && xyz.z === this.z; }
XYZColor.fromRGB = function(r, g, b) {
var R = RGBColor.channelConverter(r / 255),
G = RGBColor.channelConverter(g / 255),
B = RGBColor.channelConverter(b / 255);
return new XYZColor(
0.412453 * R + 0.357580 * G + 0.180423 * B,
0.212671 * R + 0.715160 * G + 0.072169 * B,
0.019334 * R + 0.119193 * G + 0.950227 * B
);
}
XYZColor.fromLUV = function(l, u, v) {
var deltaGammaFactor = 1 / (XYZColor.WHITE.x + 15 * XYZColor.WHITE.y + 3 * XYZColor.WHITE.z);
var uDeltaGamma = 4 * XYZColor.WHITE.x * deltaGammaFactor;
var vDeltagamma = 9 * XYZColor.WHITE.y * deltaGammaFactor;
// XYZColor.EPSILON * XYZColor.KAPPA = 8
var Y = (l > 8) ? Math.pow((l + 16) / 116, 3) : l / XYZColor.KAPPA;
var a = 1/3 * (((52 * l) / (u + 13 * l * uDeltaGamma)) - 1);
var b = -5 * Y;
var c = -1/3;
var d = Y * (((39 * l) / (v + 13 * l * vDeltagamma)) - 5);
var X = (d - b) / (a - c);
var Z = X * a + b;
return new XYZColor(X, Y, Z);
}
XYZColor.prototype.toRGB = function() { return RGBColor.fromXYZ(this.x, this.y, this.z); }
XYZColor.prototype.toLUV = function() { return LUVColor.fromXYZ(this.x, this.y, this.z); }
XYZColor.prototype.toHSL = function() { return RGBColor.fromXYZ(this.x, this.y, this.z).toHSL(); }
XYZColor.prototype.toHSV = function() { return RGBColor.fromXYZ(this.x, this.y, this.z).toHSV(); }
XYZColor.prototype._x = function(x) { return new XYZColor(x, this.y, this.z); }
XYZColor.prototype._y = function(y) { return new XYZColor(this.x, y, this.z); }
XYZColor.prototype._z = function(z) { return new XYZColor(this.x, this.y, z); }
// LUV Colors
XYZColor.EPSILON = Math.pow(6 / 29, 3);
XYZColor.KAPPA = Math.pow(29 / 3, 3);
XYZColor.WHITE = (new RGBColor(255, 255, 255)).toXYZ();
LUVColor.prototype.eq = function(luv) { return luv.l === this.l && luv.u === this.u && luv.v === this.v; }
LUVColor.fromXYZ = function(X, Y, Z) {
var deltaGammaFactor = 1 / (XYZColor.WHITE.x + 15 * XYZColor.WHITE.y + 3 * XYZColor.WHITE.z);
var uDeltaGamma = 4 * XYZColor.WHITE.x * deltaGammaFactor;
var vDeltagamma = 9 * XYZColor.WHITE.y * deltaGammaFactor;
var yGamma = Y / XYZColor.WHITE.y;
var deltaDivider = (X + 15 * Y + 3 * Z);
if (deltaDivider === 0) {
deltaDivider = 1;
}
var deltaFactor = 1 / deltaDivider;
var uDelta = 4 * X * deltaFactor;
var vDelta = 9 * Y * deltaFactor;
var L = (yGamma > XYZColor.EPSILON) ? 116 * Math.pow(yGamma, 1/3) - 16 : XYZColor.KAPPA * yGamma;
var u = 13 * L * (uDelta - uDeltaGamma);
var v = 13 * L * (vDelta - vDeltagamma);
return new LUVColor(L, u, v);
}
LUVColor.prototype.toXYZ = function() { return XYZColor.fromLUV(this.l, this.u, this.v); }
LUVColor.prototype.toRGB = function() { return XYZColor.fromLUV(this.l, this.u, this.v).toRGB(); }
LUVColor.prototype.toHSL = function() { return XYZColor.fromLUV(this.l, this.u, this.v).toHSL(); }
LUVColor.prototype.toHSV = function() { return XYZColor.fromLUV(this.l, this.u, this.v).toHSV(); }
LUVColor.prototype._l = function(l) { return new LUVColor(l, this.u, this.v); }
LUVColor.prototype._u = function(u) { return new LUVColor(this.l, u, this.v); }
LUVColor.prototype._v = function(v) { return new LUVColor(this.l, this.u, v); }
// Required Colors
var REQUIRED_CONTRAST = 4.5,
REQUIRED_BRIGHT = new XYZColor(0, (REQUIRED_CONTRAST * (new RGBColor(35,35,35).toXYZ().y + 0.05) - 0.05), 0).toLUV().l,
REQUIRED_DARK = new XYZColor(0, ((new RGBColor(217,217,217).toXYZ().y + 0.05) / REQUIRED_CONTRAST - 0.05), 0).toLUV().l;
// --------------------
// Rebuild Colors
// --------------------
FFZ.prototype._rebuild_colors = function() {
if ( ! this._color_style || this.has_bttv )
return;
this._color_style.innerHTML = '';
var colors = Object.keys(this._colors);
this._colors = {};
for(var i=0, l=colors.length; i<l; i++)
this._handle_color(colors[i]);
}
FFZ.prototype._handle_color = function(color) {
if ( ! color || this._colors.hasOwnProperty(color) )
return;
this._colors[color] = true;
var rgb = RGBColor.fromHex(color),
clr = color,
light_color = color,
dark_color = color,
matched = false,
rule = 'span[style="color:' + color + '"]';
// Color Blindness Handling
if ( this.settings.color_blind !== '0' ) {
var new_color = rgb.daltonize(this.settings.color_blind);
if ( ! rgb.eq(new_color) ) {
rgb = new_color;
clr = light_color = dark_color = rgb.toCSS();
matched = true;
}
}
// Color Processing - RGB
if ( this.settings.fix_color === '4' ) {
var lum = rgb.luminance();
if ( lum > 0.3 ) {
var s = 127, nc = rgb;
while(s--) {
nc = nc.brighten(-1);
if ( nc.luminance() <= 0.3 )
break;
}
matched = true;
light_color = nc.toCSS();
}
if ( lum < 0.15 ) {
var s = 127, nc = rgb;
while(s--) {
nc = nc.brighten();
if ( nc.luminance() >= 0.15 )
break;
}
matched = true;
dark_color = nc.toCSS();
}
}
// Color Processing - HSL
if ( this.settings.fix_color === '2' ) {
var hsl = rgb.toHSL();
matched = true;
light_color = hsl._l(Math.min(Math.max(0, 0.7 * hsl.l), 1)).toCSS();
dark_color = hsl._l(Math.min(Math.max(0, 0.3 + (0.7 * hsl.l)), 1)).toCSS();
}
// Color Processing - HSV
if ( this.settings.fix_color === '3' ) {
var hsv = rgb.toHSV();
matched = true;
if ( hsv.s === 0 ) {
// Black and White
light_color = hsv._v(Math.min(Math.max(0.5, 0.5 * hsv.v), 1)).toRGB().toCSS();
dark_color = hsv._v(Math.min(Math.max(0.5, 0.5 + (0.5 * hsv.v)), 1)).toRGB().toCSS();
} else {
light_color = RGBColor.fromHSV(hsv.h, Math.min(Math.max(0.7, 0.7 + (0.3 * hsv.s)), 1), Math.min(0.7, hsv.v)).toCSS();
dark_color = RGBColor.fromHSV(hsv.h, Math.min(0.7, hsv.s), Math.min(Math.max(0.7, 0.7 + (0.3 * hsv.v)), 1)).toCSS();
}
}
// Color Processing - LUV
if ( this.settings.fix_color === '1' ) {
var luv = rgb.toLUV();
if ( luv.l > REQUIRED_DARK ) {
matched = true;
light_color = luv._l(REQUIRED_DARK).toRGB().toCSS();
}
if ( luv.l < REQUIRED_BRIGHT ) {
matched = true;
dark_color = luv._l(REQUIRED_BRIGHT).toRGB().toCSS();
}
}
// Output
if ( ! matched )
return;
var output = '';
if ( light_color !== clr )
output += 'body.ffz-chat-colors .chat-line ' + rule + ' { color: ' + light_color + ' !important; }';
if ( dark_color !== light_color ) {
output += 'body.ffz-chat-colors .theatre .chat-container .chat-line ' + rule +
', body.ffz-chat-colors .ember-chat-container.dark .chat-line ' + rule +
', body.ffz-chat-colors .ember-chat-container.force-dark .chat-line ' + rule +
', body.ffz-chat-colors .chat-container.dark .chat-line ' + rule +
', body.ffz-chat-colors .chat-container.force-dark .chat-line ' + rule + ' { color: ' + dark_color + ' !important; }';
}
this._color_style.innerHTML += output;
}

View file

@ -464,6 +464,7 @@ FFZ.prototype._modify_cindex = function(view) {
FFZ.settings_info.chatter_count = {
type: "boolean",
value: false,
no_mobile: true,
category: "Channel Metadata",
@ -487,6 +488,7 @@ FFZ.settings_info.chatter_count = {
FFZ.settings_info.channel_views = {
type: "boolean",
value: true,
no_mobile: true,
category: "Channel Metadata",
name: "Channel Views",
@ -500,6 +502,7 @@ FFZ.settings_info.channel_views = {
FFZ.settings_info.hosted_channels = {
type: "boolean",
value: true,
no_mobile: true,
category: "Channel Metadata",
name: "Channel Hosting",
@ -526,6 +529,7 @@ FFZ.settings_info.hosted_channels = {
FFZ.settings_info.stream_host_button = {
type: "boolean",
value: true,
no_mobile: true,
category: "Channel Metadata",
name: "Host This Channel Button",
@ -540,6 +544,7 @@ FFZ.settings_info.stream_host_button = {
FFZ.settings_info.stream_uptime = {
type: "boolean",
value: false,
no_mobile: true,
category: "Channel Metadata",
name: "Stream Uptime",
@ -555,6 +560,7 @@ FFZ.settings_info.stream_title = {
type: "boolean",
value: true,
no_bttv: true,
no_mobile: true,
category: "Channel Metadata",
name: "Title Links",

View file

@ -13,6 +13,7 @@ FFZ.settings_info.swap_sidebars = {
value: false,
category: "Appearance",
no_mobile: true,
no_bttv: true,
name: "Swap Sidebar Positions",
@ -30,6 +31,7 @@ FFZ.settings_info.minimal_chat = {
value: false,
category: "Chat Appearance",
name: "Minimalistic Chat",
help: "Hide all of the chat user interface, only showing messages and an input box.",

View file

@ -5,15 +5,6 @@
SEPARATORS = "[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]",
SPLITTER = new RegExp(SEPARATORS + "*," + SEPARATORS + "*"),
quote_attr = function(attr) {
return (attr + '')
.replace(/&/g, "&amp;")
.replace(/'/g, "&apos;")
.replace(/"/g, "&quot;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
},
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
SRCSETS = {};
@ -25,6 +16,36 @@
},
LINK_SPLIT = /^(?:(https?):\/\/)?(?:(.*?)@)?([^\/:]+)(?::(\d+))?(.*?)(?:\?(.*?))?(?:\#(.*?))?$/,
YOUTUBE_CHECK = /^(?:https?:\/\/)?(?:m\.|www\.)?youtu(?:be\.com|\.be)\/(?:v\/|watch\/|.*?(?:embed|watch).*?v=)?([a-zA-Z0-9\-_]+)$/,
IMGUR_PATH = /^\/(?:gallery\/)?[A-Za-z0-9]+(?:\.(?:png|jpg|jpeg|gif|gifv|bmp))?$/,
IMAGE_EXT = /\.(?:png|jpg|jpeg|gif|bmp)$/i,
IMAGE_DOMAINS = [],
is_image = function(href, any_domain) {
var match = href.match(LINK_SPLIT);
if ( ! match )
return;
var domain = match[3].toLowerCase(), port = match[4],
path = match[5];
// Don't allow non-standard ports.
if ( port && port !== '80' && port !== '443' )
return false;
// imgur-specific checks.
if ( domain === 'i.imgur.com' || domain === 'imgur.com' || domain === 'www.imgur.com' || domain === 'm.imgur.com' )
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/image-proxy.html?' + utils.quote_attr(href) + '"></iframe>';
},
data_to_tooltip = function(data) {
var set = data.set,
set_type = data.set_type,
@ -86,7 +107,8 @@
return link_data.tooltip;
if ( link_data.type == "youtube" ) {
tooltip = "<b>YouTube: " + utils.sanitize(link_data.title) + "</b><hr>";
tooltip = this.settings.link_image_hover ? image_iframe(link_data.full || href, 'ffz-yt-thumb') : '';
tooltip += "<b>YouTube: " + utils.sanitize(link_data.title) + "</b><hr>";
tooltip += "Channel: " + utils.sanitize(link_data.channel) + " | " + utils.time_to_string(link_data.duration) + "<br>";
tooltip += utils.number_commas(link_data.views||0) + " Views | &#128077; " + utils.number_commas(link_data.likes||0) + " &#128078; " + utils.number_commas(link_data.dislikes||0);
@ -126,7 +148,8 @@
} else if ( link_data.type == "reputation" ) {
tooltip = '<span style="word-wrap: break-word">' + utils.sanitize(link_data.full.toLowerCase()) + '</span>';
tooltip = (this.settings.link_image_hover && is_image(link_data.full || href, this.settings.image_hover_all_domains)) ? image_iframe(link_data.full || href) : '';
tooltip += '<span style="word-wrap: break-word">' + utils.sanitize(link_data.full.toLowerCase()) + '</span>';
if ( link_data.trust < 50 || link_data.safety < 50 || (link_data.tags && link_data.tags.length > 0) ) {
tooltip += "<hr>";
var had_extra = false;
@ -144,8 +167,10 @@
}
} else if ( link_data.full )
tooltip = '<span style="word-wrap: break-word">' + utils.sanitize(link_data.full.toLowerCase()) + '</span>';
} else if ( link_data.full ) {
tooltip = (this.settings.link_image_hover && is_image(link_data.full || href, this.settings.image_hover_all_domains)) ? image_iframe(link_data.full || href) : '';
tooltip += '<span style="word-wrap: break-word">' + utils.sanitize(link_data.full.toLowerCase()) + '</span>';
}
if ( ! tooltip )
tooltip = '<span style="word-wrap: break-word">' + utils.sanitize(href.toLowerCase()) + '</span>';
@ -361,32 +386,44 @@ FFZ.settings_info.keywords = {
};
FFZ.settings_info.fix_color = {
type: "boolean",
value: true,
category: "Chat Appearance",
no_bttv: true,
name: "Adjust Username Colors",
help: "Ensure that username colors contrast with the background enough to be readable.",
on_update: function(val) { document.body.classList.toggle("ffz-chat-colors", !this.has_bttv && val); }
};
FFZ.settings_info.link_info = {
type: "boolean",
value: true,
category: "Chat Appearance",
category: "Chat Tooltips",
no_bttv: true,
name: "Link Tooltips <span>Beta</span>",
name: "Link Information <span>Beta</span>",
help: "Check links against known bad websites, unshorten URLs, and show YouTube info."
};
FFZ.settings_info.link_image_hover = {
type: "boolean",
value: false,
category: "Chat Tooltips",
no_bttv: true,
no_mobile: true,
name: "Image Preview",
help: "Display image thumbnails for links to Imgur and YouTube."
};
FFZ.settings_info.image_hover_all_domains = {
type: "boolean",
value: false,
category: "Chat Tooltips",
no_bttv: true,
no_mobile: true,
name: "Image Preview - All Domains",
help: "<i>Requires Image Preview.</i> Attempt to show an image preview for any URL ending in the appropriate extension. <b>Warning: This may be used to leak your IP address to malicious users.</b>"
};
FFZ.settings_info.legacy_badges = {
type: "boolean",
value: false,
@ -415,18 +452,35 @@ FFZ.settings_info.chat_rows = {
FFZ.settings_info.chat_separators = {
type: "boolean",
value: false,
type: "select",
options: {
0: "Disabled",
1: "Basic Line (1px solid)",
2: "3D Line (2px groove)"
},
value: '0',
category: "Chat Appearance",
no_bttv: true,
process_value: function(val) {
if ( val === false )
return '0';
else if ( val === true )
return '1';
return val;
},
name: "Chat Line 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); }
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');
}
};
FFZ.settings_info.chat_padding = {
type: "boolean",
value: false,
@ -495,6 +549,52 @@ FFZ.settings_info.chat_font_size = {
}
utils.update_css(this._chat_style, "chat_font_size", css);
FFZ.settings_info.chat_ts_size.on_update.bind(this)(this.settings.chat_ts_size);
}
};
FFZ.settings_info.chat_ts_size = {
type: "button",
value: null,
category: "Chat Appearance",
no_bttv: true,
name: "Timestamp Font Size",
help: "Make the chat timestamp font bigger or smaller.",
method: function() {
var old_val = this.settings.chat_ts_size;
if ( old_val === null )
old_val = this.settings.chat_font_size;
var new_val = prompt("Chat Timestamp Font Size\n\nPlease enter a new size for the chat timestamp font. The default is to match the regular chat font size.", old_val);
if ( new_val === null || new_val === undefined )
return;
var parsed = parseInt(new_val);
if ( parsed === NaN || parsed < 1 )
parsed = null;
this.settings.set("chat_ts_size", parsed);
},
on_update: function(val) {
if ( this.has_bttv || ! this._chat_style )
return;
var css;
if ( val === null )
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; }";
}
utils.update_css(this._chat_style, "chat_ts_font_size", css);
}
};
@ -521,23 +621,19 @@ FFZ.prototype.setup_line = function() {
// Chat Enhancements
document.body.classList.toggle("ffz-chat-colors", !this.has_bttv && this.settings.fix_color);
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');
document.body.classList.toggle("ffz-legacy-badges", this.settings.legacy_badges);
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);
document.body.classList.toggle("ffz-chat-separator", !this.has_bttv && this.settings.chat_separators !== '0');
document.body.classList.toggle("ffz-chat-separator-3d", !this.has_bttv && this.settings.chat_separators === '2');
document.body.classList.toggle("ffz-chat-padding", !this.has_bttv && this.settings.chat_padding);
document.body.classList.toggle("ffz-chat-purge-icon", !this.has_bttv && this.settings.line_purge_icon);
document.body.classList.toggle("ffz-high-contrast-chat", !this.has_bttv && this.settings.high_contrast_chat);
this._colors = {};
this._last_row = {};
s = this._fix_color_style = document.createElement('style');
s.id = "ffz-style-username-colors";
s.type = 'text/css';
document.head.appendChild(s);
// Emoticon Data
this._twitch_emotes = {};
this._link_data = {};
@ -657,13 +753,8 @@ FFZ.prototype._modify_line = function(component) {
var el = this.get('element'),
user = this.get('msgObject.from'),
room = this.get('msgObject.room') || App.__container__.lookup('controller:chat').get('currentRoom.id'),
color = this.get('msgObject.color'),
row_type = this.get('msgObject.ffz_alternate');
// Color Processing
if ( color )
f._handle_color(color);
// Row Alternation
if ( row_type === undefined ) {
row_type = f._last_row[room] = f._last_row.hasOwnProperty(room) ? !f._last_row[room] : false;
@ -733,7 +824,7 @@ FFZ.prototype._modify_line = function(component) {
// Link Tooltips
if ( f.settings.link_info ) {
if ( f.settings.link_info || f.settings.link_image_hover ) {
var links = el.querySelectorAll("span.message a");
for(var i=0; i < links.length; i++) {
var link = links[i],
@ -746,6 +837,7 @@ FFZ.prototype._modify_line = function(component) {
}
// Check the cache.
if ( f.settings.link_info ) {
var link_data = f._link_data[href];
if ( link_data ) {
if ( !deleted && typeof link_data != "boolean" )
@ -757,9 +849,16 @@ FFZ.prototype._modify_line = function(component) {
} else if ( ! /^mailto:/.test(href) ) {
f._link_data[href] = true;
f.ws_send("get_link", href, load_link_data.bind(f, href));
if ( ! deleted && f.settings.link_image_hover && is_image(href, f.settings.image_hover_all_domains) )
link.title = image_iframe(href);
}
}
// Now, Images
else if ( ! deleted && f.settings.link_image_hover && is_image(href, f.settings.image_hover_all_domains) )
link.title = image_iframe(href);
}
jQuery(links).tipsy({html:true});
}
@ -834,68 +933,6 @@ FFZ.prototype._modify_line = function(component) {
}
// ---------------------
// Fix Name Colors
// ---------------------
FFZ.prototype._handle_color = function(color) {
if ( ! color || this._colors[color] )
return;
this._colors[color] = true;
// Parse the color.
var raw = parseInt(color.substr(1), 16),
rgb = [
(raw >> 16),
(raw >> 8 & 0x00FF),
(raw & 0x0000FF)
],
lum = utils.get_luminance(rgb),
output = "",
rule = 'span[style="color:' + color + '"]',
matched = false;
if ( lum > 0.3 ) {
// Color Too Bright. We need a lum of 0.3 or less.
matched = true;
var s = 127,
nc = rgb;
while(s--) {
nc = utils.darken(nc);
if ( utils.get_luminance(nc) <= 0.3 )
break;
}
output += '.ffz-chat-colors .ember-chat-container:not(.dark) .chat-line ' + rule + ', .ffz-chat-colors .chat-container:not(.dark) .chat-line ' + rule + ' { color: ' + utils.rgb_to_css(nc) + ' !important; }\n';
} else
output += '.ffz-chat-colors .ember-chat-container:not(.dark) .chat-line ' + rule + ', .ffz-chat-colors .chat-container:not(.dark) .chat-line ' + rule + ' { color: ' + color + ' !important; }\n';
if ( lum < 0.15 ) {
// Color Too Dark. We need a lum of 0.1 or more.
matched = true;
var s = 127,
nc = rgb;
while(s--) {
nc = utils.brighten(nc);
if ( utils.get_luminance(nc) >= 0.15 )
break;
}
output += '.ffz-chat-colors .theatre .chat-container .chat-line ' + rule + ', .ffz-chat-colors .chat-container.dark .chat-line ' + rule + ', .ffz-chat-colors .ember-chat-container.dark .chat-line ' + rule + ' { color: ' + utils.rgb_to_css(nc) + ' !important; }\n';
} else
output += '.ffz-chat-colors .theatre .chat-container .chat-line ' + rule + ', .ffz-chat-colors .chat-container.dark .chat-line ' + rule + ', .ffz-chat-colors .ember-chat-container.dark .chat-line ' + rule + ' { color: ' + color + ' !important; }\n';
if ( matched )
this._fix_color_style.innerHTML += output;
}
// ---------------------
// Capitalization
// ---------------------
@ -953,7 +990,7 @@ FFZ.prototype._remove_banned = function(tokens) {
new_tokens.push(token.altText.replace(regex, "$1***"));
else if ( token.isLink && regex.test(token.href) )
new_tokens.push({
mentionedUser: '</span><a class="deleted-link" title="' + quote_attr(token.href.replace(regex, "$1***")) + '" data-url="' + quote_attr(token.href) + '" href="#">&lt;banned link&gt;</a><span class="mentioning">',
mentionedUser: '</span><a class="deleted-link" title="' + utils.quote_attr(token.href.replace(regex, "$1***")) + '" data-url="' + utils.quote_attr(token.href) + '" href="#">&lt;banned link&gt;</a><span class="mentioning">',
own: true
});
else

View file

@ -98,7 +98,7 @@ FFZ.settings_info.mod_card_hotkeys = {
FFZ.settings_info.mod_card_info = {
type: "boolean",
value: false,
value: true,
no_bttv: true,
category: "Chat Moderation",

View file

@ -5,6 +5,9 @@ var FFZ = window.FrankerFaceZ,
constants = require('../constants'),
utils = require('../utils'),
// StrimBagZ Support
is_android = navigator.userAgent.indexOf('Android') !== -1,
moderator_css = function(room) {
if ( ! room.moderator_badge )
@ -130,6 +133,15 @@ FFZ.prototype._modify_rview = function(view) {
this.ffz_frozen = false;
// Fix scrolling.
this._ffz_mouse_down = this.ffzMouseDown.bind(this);
if ( is_android )
// We don't unbind scroll because that messes with the scrollbar. ;_;
this._$chatMessagesScroller.bind('scroll', this._ffz_mouse_down);
this._$chatMessagesScroller.unbind('mousedown');
this._$chatMessagesScroller.bind('mousedown', this._ffz_mouse_down);
if ( f.settings.chat_hover_pause )
this.ffzEnableFreeze();
@ -257,12 +269,9 @@ FFZ.prototype._modify_rview = function(view) {
this._ffz_mouse_move = this.ffzMouseMove.bind(this);
this._ffz_mouse_out = this.ffzMouseOut.bind(this);
this._ffz_mouse_down = this.ffzMouseDown.bind(this);
this._$chatMessagesScroller.unbind('mousedown');
this._$chatMessagesScroller.bind('mousedown', this._ffz_mouse_down);
messages.addEventListener('mousemove', this._ffz_mouse_move);
messages.addEventListener('touchmove', this._ffz_mouse_move);
messages.addEventListener('mouseout', this._ffz_mouse_out);
document.addEventListener('mouseout', this._ffz_mouse_out);
},
@ -311,7 +320,7 @@ FFZ.prototype._modify_rview = function(view) {
ffzMouseDown: function(event) {
var t = this._$chatMessagesScroller;
if ( ! this.ffz_frozen && t && t[0] && (event.which > 0 || "mousedown" === event.type || "mousewheel" === event.type) ) {
if ( t && t[0] && (event.which > 0 || (!this.ffz_frozne && "mousedown" === event.type) || "mousewheel" === event.type || (is_android && "scroll" === event.type) ) ) {
var r = t[0].scrollHeight - t[0].scrollTop - t[0].offsetHeight;
this._setStuckToBottom(10 >= r);
}
@ -939,6 +948,7 @@ FFZ.prototype._modify_room = function(room) {
if ( ! is_whisper )
msg.room = this.get('id');
// Tokenization
f.tokenize_chat_line(msg);
// Keep the history.
@ -974,11 +984,17 @@ FFZ.prototype._modify_room = function(room) {
}
}
}
} catch(err) {
f.error("Room addMessage: " + err);
}
} catch(err) { f.error("Room addMessage: " + err); }
return this._super(msg);
var out = this._super(msg);
try {
// Color processing.
var color = msg.color;
if ( color )
f._handle_color(color);
} catch(err) { f.error("Room addMessage2: " + err); }
return out;
},
setHostMode: function(e) {

View file

@ -56,9 +56,11 @@ FFZ.prototype.setup_bttv = function(delay) {
// 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-sidebar-swap");
document.body.classList.remove("ffz-transparent-badges");
document.body.classList.remove("ffz-high-contrast-chat");

View file

@ -21,7 +21,7 @@ FFZ.get = function() { return FFZ.instance; }
// Version
var VER = FFZ.version_info = {
major: 3, minor: 4, revision: 19,
major: 3, minor: 4, revision: 25,
toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
}
@ -107,7 +107,7 @@ require('./ui/menu');
require('./settings');
require('./socket');
require('./colors');
require('./emoticons');
require('./badges');
require('./tokenize');
@ -211,6 +211,7 @@ FFZ.prototype.setup_normal = function(delay, no_socket) {
if ( ! no_socket )
this.ws_create();
this.setup_colors();
this.setup_emoticons();
this.setup_badges();
@ -245,9 +246,11 @@ FFZ.prototype.setup_dashboard = function(delay) {
this.setup_dark();
this.ws_create();
this.setup_colors();
this.setup_emoticons();
this.setup_badges();
this.setup_tokenization();
this.setup_notifications();
this.setup_css();
@ -288,6 +291,8 @@ FFZ.prototype.setup_ember = function(delay) {
//this.setup_piwik();
//this.setup_router();
this.setup_colors();
this.setup_tokenization();
this.setup_channel();
this.setup_room();
this.setup_line();

View file

@ -10,6 +10,10 @@ var FFZ = window.FrankerFaceZ,
var val = ! this.settings.get(key);
this.settings.set(key, val);
swit.classList.toggle('active', val);
},
option_setting = function(select, key) {
this.settings.set(key, JSON.parse(select.options[select.selectedIndex].value));
};
@ -41,6 +45,9 @@ FFZ.prototype.load_settings = function() {
}
}
if ( info.process_value )
val = info.process_value.bind(this)(val);
this.settings[key] = val;
}
@ -61,7 +68,9 @@ FFZ.prototype.load_settings = function() {
FFZ.menu_pages.settings = {
render: function(view, container) {
var settings = {},
categories = [];
categories = [],
is_android = navigator.userAgent.indexOf('Android') !== -1;
for(var key in FFZ.settings_info) {
if ( ! FFZ.settings_info.hasOwnProperty(key) )
continue;
@ -79,6 +88,9 @@ FFZ.menu_pages.settings = {
continue;
}
if ( is_android && info.no_mobile )
continue;
if ( ! cs ) {
categories.push(cat);
cs = settings[cat] = [];
@ -139,8 +151,8 @@ FFZ.menu_pages.settings = {
var a = a[1],
b = b[1],
at = a.type,
bt = b.type,
at = a.type === "button" ? 2 : 1,
bt = b.type === "button" ? 2 : 1,
an = a.name.toLowerCase(),
bn = b.name.toLowerCase();
@ -193,6 +205,27 @@ FFZ.menu_pages.settings = {
swit.addEventListener("click", toggle_setting.bind(this, swit, key));
} else if ( info.type === "select" ) {
var select = document.createElement('select'),
label = document.createElement('span');
label.className = 'option-label';
label.innerHTML = info.name;
for(var ok in info.options) {
var op = document.createElement('option');
op.value = JSON.stringify(ok);
if ( val === ok )
op.setAttribute('selected', true);
op.innerHTML = info.options[ok];
select.appendChild(op);
}
select.addEventListener('change', option_setting.bind(this, select, key));
el.appendChild(label);
el.appendChild(select);
} else {
el.classList.add("option");
var link = document.createElement('a');

View file

@ -8,15 +8,12 @@ var FFZ = window.FrankerFaceZ,
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
LINK = /(?:https?:\/\/)?(?:[-a-zA-Z0-9@:%_\+~#=]+\.)+[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#!?&//=]*)/g,
SEPARATORS = "[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]",
SPLITTER = new RegExp(SEPARATORS + "*," + SEPARATORS + "*");
try {
helpers = window.require && window.require("ember-twitch-chat/helpers/chat-line-helpers");
} catch(err) { }
FFZ.SRC_IDS = {},
FFZ.src_to_id = function(src) {
if ( FFZ.SRC_IDS.hasOwnProperty(src) )
@ -34,7 +31,7 @@ FFZ.src_to_id = function(src) {
// ---------------------
// Time Format
// Settings
// ---------------------
var ts = new Date(0).toLocaleTimeString().toUpperCase();
@ -51,20 +48,73 @@ FFZ.settings_info.twenty_four_timestamps = {
};
if ( helpers )
FFZ.settings_info.show_deleted_links = {
type: "boolean",
value: false,
category: "Chat Moderation",
no_bttv: true,
name: "Show Deleted Links",
help: "Do not delete links based on room settings or link length."
};
// ---------------------
// Setup
// ---------------------
FFZ.prototype.setup_tokenization = function() {
helpers = window.require && window.require("ember-twitch-chat/helpers/chat-line-helpers");
if ( ! helpers )
return this.log("Unable to get chat helper functions.");
this.log("Hooking Ember chat line helpers.");
var f = this;
// Timestamp Display
helpers.getTime = function(e) {
if ( e === undefined || e === null )
return '?:??';
var hours = e.getHours(),
minutes = e.getMinutes();
if ( hours > 12 && ! FFZ.get().settings.twenty_four_timestamps )
if ( hours > 12 && ! f.settings.twenty_four_timestamps )
hours -= 12;
else if ( hours === 0 && ! FFZ.get().settings.twenty_four_timestamps )
else if ( hours === 0 && ! f.settings.twenty_four_timestamps )
hours = 12;
return hours + ':' + (minutes < 10 ? '0' : '') + minutes;
};
// Linkify Messages
helpers.linkifyMessage = function(tokens, delete_links) {
var show_deleted = f.settings.show_deleted_links;
return _.chain(tokens).map(function(token) {
if ( ! _.isString(token) )
return token;
var matches = token.match(LINK);
if ( ! matches || ! matches.length )
return [token];
return _.zip(
token.split(LINK),
_.map(matches, function(e) {
if ( ! show_deleted && (delete_links || e.length > 255) )
return {mentionedUser: '</span><a class="deleted-link" title="' + utils.quote_attr(e) + '" data-url="' + utils.quote_attr(e) + '" href="#">&lt;' + (e.length > 255 ? 'long link' : 'deleted link') + '&gt;</a><span class="mentioning">', own: true}
return {isLink: true, href: e};
})
);
}).flatten().compact().value();
};
}
// ---------------------
// Tokenization
// ---------------------
@ -73,6 +123,8 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
if ( msgObject.cachedTokens )
return msgObject.cachedTokens;
try {
var msg = msgObject.message,
user = this.get_user(),
room_id = msgObject.room,
@ -82,11 +134,15 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
tokens = [msg];
// Standard tokenization
if ( helpers && helpers.linkifyMessage )
tokens = helpers.linkifyMessage(tokens);
if ( user && user.login )
if ( user && user.login && helpers && helpers.mentionizeMessage )
tokens = helpers.mentionizeMessage(tokens, user.login, from_me);
if ( helpers && helpers.emoticonizeMessage )
tokens = helpers.emoticonizeMessage(tokens, emotes);
if ( this.settings.replace_bad_emotes )
tokens = this.tokenize_replace_emotes(tokens);
@ -149,7 +205,7 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
msg,
"Twitch Chat Whisper",
"ffz_whisper_notice",
60000,
(this.settings.notification_timeout*1000),
function() {
window.focus();
}
@ -159,7 +215,7 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
msg,
"Twitch Chat Mention in " + room_name,
room_id,
60000,
(this.settings.notification_timeout*1000),
function() {
window.focus();
var cont = App.__container__.lookup('controller:chat');
@ -174,6 +230,10 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
}
msgObject.cachedTokens = tokens;
} catch(err) {
this.error("Tokenization Error: " + err);
}
return tokens;
}

View file

@ -67,6 +67,7 @@ FFZ.settings_info.hide_recent_past_broadcast = {
value: false,
//no_bttv: true,
no_mobile: true,
category: "Channel Metadata",
name: "Hide \"Watch Last Broadcast\"",

View file

@ -8,6 +8,7 @@ FFZ.settings_info.following_count = {
value: true,
no_bttv: true,
no_mobile: true,
category: "Appearance",
name: "Sidebar Following Count",

View file

@ -20,6 +20,7 @@ FFZ.prototype.setup_following = function() {
FFZ.settings_info.follow_buttons = {
type: "boolean",
value: true,
no_mobile: true,
category: "Channel Metadata",
name: "Relevant Follow Buttons",

View file

@ -255,7 +255,7 @@ FFZ.prototype.build_ui_popup = function(view) {
// Add the menu to the DOM.
this._popup = container;
sub_container.style.maxHeight = Math.max(200, view.$().height() - 172) + "px";
sub_container.style.maxHeight = Math.max(200, view.$().height() - 472) + "px";
view.$('.chat-interface').append(container);
}

View file

@ -21,6 +21,7 @@ FFZ.settings_info.highlight_notifications = {
category: "Chat Filtering",
no_bttv: true,
no_mobile: true,
//visible: function() { return ! this.has_bttv },
name: "Highlight Notifications",
@ -51,6 +52,33 @@ FFZ.settings_info.highlight_notifications = {
};
FFZ.settings_info.notification_timeout = {
type: "button",
value: 60,
category: "Chat Filtering",
no_bttv: true,
no_mobile: true,
name: "Notification Timeout",
help: "Specify how long notifications should be displayed before automatically closing.",
method: function() {
var old_val = this.settings.notification_timeout,
new_val = prompt("Notification Timeout\n\nPlease enter the time you'd like notifications to be displayed before automatically closing, in seconds.\n\nDefault is: 60", old_val);
if ( new_val === null || new_val === undefined )
return;
var parsed = parseInt(new_val);
if ( parsed === NaN || parsed < 1 )
parsed = 60;
this.settings.set("notification_timeout", parsed);
}
};
// ---------------------
// Socket Commands
// ---------------------
@ -88,7 +116,7 @@ FFZ.prototype.show_notification = function(message, title, tag, timeout, on_clic
if ( perm === "granted" ) {
title = title || "FrankerFaceZ";
timeout = timeout || 10000;
timeout = timeout || (this.settings.notification_timeout*1000);
var options = {
lang: "en-US",

View file

@ -19,6 +19,7 @@ FFZ.prototype.setup_races = function() {
FFZ.settings_info.srl_races = {
type: "boolean",
value: true,
no_mobile: true,
category: "Channel Metadata",
name: "SRL Race Information",

View file

@ -19,38 +19,6 @@ var sanitize_cache = {},
return num + "th";
},
brighten = function(rgb, amount) {
amount = (amount === 0) ? 0 : (amount || 1);
amount = Math.round(255 * -(amount / 100));
var r = Math.max(0, Math.min(255, rgb[0] - amount)),
g = Math.max(0, Math.min(255, rgb[1] - amount)),
b = Math.max(0, Math.min(255, rgb[2] - amount));
return [r,g,b];
},
rgb_to_css = function(rgb) {
return "rgb(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ")";
},
darken = function(rgb, amount) {
amount = (amount === 0) ? 0 : (amount || 1);
return brighten(rgb, -amount);
},
get_luminance = function(rgb) {
rgb = [rgb[0]/255, rgb[1]/255, rgb[2]/255];
for (var i =0; i<rgb.length; i++) {
if (rgb[i] <= 0.03928) {
rgb[i] = rgb[i] / 12.92;
} else {
rgb[i] = Math.pow( ((rgb[i]+0.055)/1.055), 2.4 );
}
}
return (0.2126 * rgb[0]) + (0.7152 * rgb[1]) + (0.0722 * rgb[2]);
},
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) {
@ -221,11 +189,6 @@ module.exports = {
emoji_to_codepoint: emoji_to_codepoint,
get_luminance: get_luminance,
brighten: brighten,
darken: darken,
rgb_to_css: rgb_to_css,
parse_date: parse_date,
number_commas: function(x) {
@ -253,6 +216,15 @@ module.exports = {
return m;
},
quote_attr: function(attr) {
return (attr + '')
.replace(/&/g, "&amp;")
.replace(/'/g, "&apos;")
.replace(/"/g, "&quot;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
},
date_string: function(date) {
return date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate();
},

View file

@ -504,10 +504,19 @@ body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + s
.ffz-ui-menu-page p.disabled span.switch-label,
.ffz-ui-menu-page span.help,
.ffz-ui-menu-page span.option-label,
.ffz-ui-menu-page p.option a {
margin-left: 50px;
}
.ffz-ui-menu-page span.option-label {
line-height: 25px;
}
.ffz-ui-menu-page select {
margin: 0 10px 5px;
}
.ffz-ui-popup ul.menu {
list-style-type: none;
border-top: 1px solid rgba(0,0,0,0.2);
@ -600,6 +609,7 @@ body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + s
.ffz-ui-popup ul.menu svg path { fill: #333; }
.ffz-ui-popup .option-label span,
.ffz-ui-popup .switch-label span {
opacity: 0.8;
font-size: 10px;
@ -653,6 +663,11 @@ body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + s
display: none;
}
.ffz-chat-colors-gray .chat-line:not(.admin):not(.notification) span.from,
.ffz-chat-colors-gray .chat-line:not(.admin):not(.notification) span.message {
color: inherit !important
}
.ffz-chat-background .ember-chat .mentioning,
.ffz-chat-background .ember-chat .mentioned {
border-radius: 10px;
@ -836,6 +851,15 @@ body:not(.ffz-chat-purge-icon) .ember-chat .mod-icons .purge { display: none; }
border-bottom: 1px solid #aaa;
}
.ffz-chat-separator-3d .chat-line:before {
border-top: 1px solid rgba(255,255,255,0.5);
}
.ffz-chat-separator-3d ul.chat-lines div:first-of-type .chat-line:before {
border-top: none;
}
.ffz-chat-separator:not(.ffz-chat-background) ul.chat-lines div:last-of-type .chat-line:before,
.ffz-chat-separator ul.chat-lines div:last-of-type .chat-line:not(.ffz-alternate):before {
border-bottom: none;
}
@ -848,6 +872,13 @@ body:not(.ffz-chat-purge-icon) .ember-chat .mod-icons .purge { display: none; }
border-bottom-color: #000;
}
.ffz-chat-separator-3d .app-main.theatre .chat-line:before,
.ffz-chat-separator-3d .chat-container.dark .chat-line:before,
.ffz-chat-separator-3d .chat-container.force-dark .chat-line:before,
.ffz-chat-separator-3d .ember-chat-container.dark .chat-line:before,
.ffz-chat-separator-3d .ember-chat-container.force-dark .chat-line:before {
border-top-color: rgba(255,255,255,0.1);
}
.ffz-chat-background .chat-history .chat-line.ffz-alternate:before,
.ffz-chat-background .ember-chat .chat-messages .chat-line.ffz-alternate:before {
@ -1655,6 +1686,28 @@ li[data-name="following"] a {
}
.ffz-high-contrast-chat .chat-line .mentioned {
color: inherit !important;
background-color: transparent !important;
color: #fff !important;
background-color: #000 !important;
}
.ffz-high-contrast-chat .chat-container.dark .chat-line .mentioned,
.ffz-high-contrast-chat .chat-container.force-dark .chat-line .mentioned,
.ffz-high-contrast-chat .ember-chat-container.dark .chat-line .mentioned,
.ffz-high-contrast-chat .ember-chat-container.force-dark .chat-line .mentioned,
.ffz-high-contrast-chat .app-main.theatre .chat-container .chat-line .mentioned,
.ffz-high-contrast-chat.ffz-dark .ember-chat-container.dark .chat-line .chat-line .mentioned,
.ffz-high-contrast-chat.ffz-dark .chat-container.dark .chat-line .chat-line .mentioned {
color: #000 !important;
background-color: #fff !important;
}
.ffz-image-hover {
border:none;
max-width: 186px;
max-height: 186px;
overflow: hidden;
}
.ffz-yt-thumb {
max-height: 90px;
}