mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-29 07:45:33 +00:00
Basic settings. Pull in FileSaver as a requirement. Menus can have sub-pages now, so that's cool.
This commit is contained in:
parent
9ece18ec0f
commit
771e290197
15 changed files with 2590 additions and 694 deletions
16
script.min.js
vendored
16
script.min.js
vendored
File diff suppressed because one or more lines are too long
256
src/FileSaver.js
Normal file
256
src/FileSaver.js
Normal file
|
@ -0,0 +1,256 @@
|
|||
/* FileSaver.js
|
||||
* A saveAs() FileSaver implementation.
|
||||
* 1.1.20150716
|
||||
*
|
||||
* By Eli Grey, http://eligrey.com
|
||||
* License: X11/MIT
|
||||
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/*global self */
|
||||
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
|
||||
|
||||
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
|
||||
|
||||
var saveAs = saveAs || (function(view) {
|
||||
"use strict";
|
||||
// IE <10 is explicitly unsupported
|
||||
if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
|
||||
return;
|
||||
}
|
||||
var
|
||||
doc = view.document
|
||||
// only get URL when necessary in case Blob.js hasn't overridden it yet
|
||||
, get_URL = function() {
|
||||
return view.URL || view.webkitURL || view;
|
||||
}
|
||||
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
|
||||
, can_use_save_link = "download" in save_link
|
||||
, click = function(node) {
|
||||
var event = new MouseEvent("click");
|
||||
node.dispatchEvent(event);
|
||||
}
|
||||
, webkit_req_fs = view.webkitRequestFileSystem
|
||||
, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
|
||||
, throw_outside = function(ex) {
|
||||
(view.setImmediate || view.setTimeout)(function() {
|
||||
throw ex;
|
||||
}, 0);
|
||||
}
|
||||
, force_saveable_type = "application/octet-stream"
|
||||
, fs_min_size = 0
|
||||
// See https://code.google.com/p/chromium/issues/detail?id=375297#c7 and
|
||||
// https://github.com/eligrey/FileSaver.js/commit/485930a#commitcomment-8768047
|
||||
// for the reasoning behind the timeout and revocation flow
|
||||
, arbitrary_revoke_timeout = 500 // in ms
|
||||
, revoke = function(file) {
|
||||
var revoker = function() {
|
||||
if (typeof file === "string") { // file is an object URL
|
||||
get_URL().revokeObjectURL(file);
|
||||
} else { // file is a File
|
||||
file.remove();
|
||||
}
|
||||
};
|
||||
if (view.chrome) {
|
||||
revoker();
|
||||
} else {
|
||||
setTimeout(revoker, arbitrary_revoke_timeout);
|
||||
}
|
||||
}
|
||||
, dispatch = function(filesaver, event_types, event) {
|
||||
event_types = [].concat(event_types);
|
||||
var i = event_types.length;
|
||||
while (i--) {
|
||||
var listener = filesaver["on" + event_types[i]];
|
||||
if (typeof listener === "function") {
|
||||
try {
|
||||
listener.call(filesaver, event || filesaver);
|
||||
} catch (ex) {
|
||||
throw_outside(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
, auto_bom = function(blob) {
|
||||
// prepend BOM for UTF-8 XML and text/* types (including HTML)
|
||||
if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
|
||||
return new Blob(["\ufeff", blob], {type: blob.type});
|
||||
}
|
||||
return blob;
|
||||
}
|
||||
, FileSaver = function(blob, name, no_auto_bom) {
|
||||
if (!no_auto_bom) {
|
||||
blob = auto_bom(blob);
|
||||
}
|
||||
// First try a.download, then web filesystem, then object URLs
|
||||
var
|
||||
filesaver = this
|
||||
, type = blob.type
|
||||
, blob_changed = false
|
||||
, object_url
|
||||
, target_view
|
||||
, dispatch_all = function() {
|
||||
dispatch(filesaver, "writestart progress write writeend".split(" "));
|
||||
}
|
||||
// on any filesys errors revert to saving with object URLs
|
||||
, fs_error = function() {
|
||||
// don't create more object URLs than needed
|
||||
if (blob_changed || !object_url) {
|
||||
object_url = get_URL().createObjectURL(blob);
|
||||
}
|
||||
if (target_view) {
|
||||
target_view.location.href = object_url;
|
||||
} else {
|
||||
var new_tab = view.open(object_url, "_blank");
|
||||
if (new_tab == undefined && typeof safari !== "undefined") {
|
||||
//Apple do not allow window.open, see http://bit.ly/1kZffRI
|
||||
view.location.href = object_url
|
||||
}
|
||||
}
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
dispatch_all();
|
||||
revoke(object_url);
|
||||
}
|
||||
, abortable = function(func) {
|
||||
return function() {
|
||||
if (filesaver.readyState !== filesaver.DONE) {
|
||||
return func.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
, create_if_not_found = {create: true, exclusive: false}
|
||||
, slice
|
||||
;
|
||||
filesaver.readyState = filesaver.INIT;
|
||||
if (!name) {
|
||||
name = "download";
|
||||
}
|
||||
if (can_use_save_link) {
|
||||
object_url = get_URL().createObjectURL(blob);
|
||||
save_link.href = object_url;
|
||||
save_link.download = name;
|
||||
setTimeout(function() {
|
||||
click(save_link);
|
||||
dispatch_all();
|
||||
revoke(object_url);
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Object and web filesystem URLs have a problem saving in Google Chrome when
|
||||
// viewed in a tab, so I force save with application/octet-stream
|
||||
// http://code.google.com/p/chromium/issues/detail?id=91158
|
||||
// Update: Google errantly closed 91158, I submitted it again:
|
||||
// https://code.google.com/p/chromium/issues/detail?id=389642
|
||||
if (view.chrome && type && type !== force_saveable_type) {
|
||||
slice = blob.slice || blob.webkitSlice;
|
||||
blob = slice.call(blob, 0, blob.size, force_saveable_type);
|
||||
blob_changed = true;
|
||||
}
|
||||
// Since I can't be sure that the guessed media type will trigger a download
|
||||
// in WebKit, I append .download to the filename.
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=65440
|
||||
if (webkit_req_fs && name !== "download") {
|
||||
name += ".download";
|
||||
}
|
||||
if (type === force_saveable_type || webkit_req_fs) {
|
||||
target_view = view;
|
||||
}
|
||||
if (!req_fs) {
|
||||
fs_error();
|
||||
return;
|
||||
}
|
||||
fs_min_size += blob.size;
|
||||
req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
|
||||
fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
|
||||
var save = function() {
|
||||
dir.getFile(name, create_if_not_found, abortable(function(file) {
|
||||
file.createWriter(abortable(function(writer) {
|
||||
writer.onwriteend = function(event) {
|
||||
target_view.location.href = file.toURL();
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
dispatch(filesaver, "writeend", event);
|
||||
revoke(file);
|
||||
};
|
||||
writer.onerror = function() {
|
||||
var error = writer.error;
|
||||
if (error.code !== error.ABORT_ERR) {
|
||||
fs_error();
|
||||
}
|
||||
};
|
||||
"writestart progress write abort".split(" ").forEach(function(event) {
|
||||
writer["on" + event] = filesaver["on" + event];
|
||||
});
|
||||
writer.write(blob);
|
||||
filesaver.abort = function() {
|
||||
writer.abort();
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
};
|
||||
filesaver.readyState = filesaver.WRITING;
|
||||
}), fs_error);
|
||||
}), fs_error);
|
||||
};
|
||||
dir.getFile(name, {create: false}, abortable(function(file) {
|
||||
// delete file if it already exists
|
||||
file.remove();
|
||||
save();
|
||||
}), abortable(function(ex) {
|
||||
if (ex.code === ex.NOT_FOUND_ERR) {
|
||||
save();
|
||||
} else {
|
||||
fs_error();
|
||||
}
|
||||
}));
|
||||
}), fs_error);
|
||||
}), fs_error);
|
||||
}
|
||||
, FS_proto = FileSaver.prototype
|
||||
, saveAs = function(blob, name, no_auto_bom) {
|
||||
return new FileSaver(blob, name, no_auto_bom);
|
||||
}
|
||||
;
|
||||
// IE 10+ (native saveAs)
|
||||
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
|
||||
return function(blob, name, no_auto_bom) {
|
||||
if (!no_auto_bom) {
|
||||
blob = auto_bom(blob);
|
||||
}
|
||||
return navigator.msSaveOrOpenBlob(blob, name || "download");
|
||||
};
|
||||
}
|
||||
|
||||
FS_proto.abort = function() {
|
||||
var filesaver = this;
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
dispatch(filesaver, "abort");
|
||||
};
|
||||
FS_proto.readyState = FS_proto.INIT = 0;
|
||||
FS_proto.WRITING = 1;
|
||||
FS_proto.DONE = 2;
|
||||
|
||||
FS_proto.error =
|
||||
FS_proto.onwritestart =
|
||||
FS_proto.onprogress =
|
||||
FS_proto.onwrite =
|
||||
FS_proto.onabort =
|
||||
FS_proto.onerror =
|
||||
FS_proto.onwriteend =
|
||||
null;
|
||||
|
||||
return saveAs;
|
||||
}(
|
||||
typeof self !== "undefined" && self
|
||||
|| typeof window !== "undefined" && window
|
||||
|| this.content
|
||||
));
|
||||
// `self` is undefined in Firefox for Android content script context
|
||||
// while `this` is nsIContentFrameMessageManager
|
||||
// with an attribute `content` that corresponds to the window
|
||||
|
||||
if (typeof module !== "undefined" && module.exports) {
|
||||
module.exports.saveAs = saveAs;
|
||||
} else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) {
|
||||
define([], function() {
|
||||
return saveAs;
|
||||
});
|
||||
}
|
|
@ -50,7 +50,7 @@ FFZ.prototype.setup_channel = function() {
|
|||
view.ffzInit();
|
||||
};
|
||||
|
||||
|
||||
|
||||
this.log("Hooking the Ember Channel model.");
|
||||
Channel = App.__container__.resolve('model:channel');
|
||||
if ( ! Channel )
|
||||
|
@ -58,7 +58,7 @@ FFZ.prototype.setup_channel = function() {
|
|||
|
||||
Channel.reopen({
|
||||
ffz_host_target: undefined,
|
||||
|
||||
|
||||
setHostMode: function(e) {
|
||||
if ( f.settings.hosted_channels ) {
|
||||
this.set('ffz_host_target', e.target);
|
||||
|
@ -87,13 +87,13 @@ FFZ.prototype.setup_channel = function() {
|
|||
ffzUpdateInfo: function() {
|
||||
if ( this._ffz_update_timer )
|
||||
clearTimeout(this._ffz_update_timer);
|
||||
|
||||
|
||||
if ( ! this.get('content.id') )
|
||||
return;
|
||||
|
||||
|
||||
this._ffz_update_timer = setTimeout(this.ffzCheckUpdate.bind(this), 60000);
|
||||
}.observes("content.id"),
|
||||
|
||||
|
||||
ffzCheckUpdate: function() {
|
||||
var t = this,
|
||||
id = t.get('content.id');
|
||||
|
@ -115,11 +115,11 @@ FFZ.prototype.setup_channel = function() {
|
|||
t.set('game', game);
|
||||
t.set('rollbackData.game', game);
|
||||
}
|
||||
|
||||
|
||||
if ( data.stream.channel ) {
|
||||
if ( data.stream.channel.status )
|
||||
t.set('status', data.stream.channel.status);
|
||||
|
||||
|
||||
if ( data.stream.channel.views )
|
||||
t.set('views', data.stream.channel.views);
|
||||
|
||||
|
@ -176,7 +176,7 @@ FFZ.prototype.setup_channel = function() {
|
|||
|
||||
}.observes("content.hostModeTarget")
|
||||
});
|
||||
|
||||
|
||||
Channel.ffzUpdateInfo();
|
||||
}
|
||||
|
||||
|
@ -265,8 +265,8 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
user = f.get_user(),
|
||||
room = user && f.rooms && f.rooms[user.login] && f.rooms[user.login].room,
|
||||
now_hosting = room && room.ffz_host_target,
|
||||
hosts_left = room && room.ffz_hosts_left,
|
||||
|
||||
hosts_left = room && room.ffz_hosts_left,
|
||||
|
||||
el = this.get('element');
|
||||
|
||||
this.set('ffz_host_updating', false);
|
||||
|
@ -283,13 +283,13 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
btn = document.createElement('span');
|
||||
btn.id = 'ffz-ui-host-button';
|
||||
btn.className = 'button action tooltip';
|
||||
|
||||
|
||||
btn.addEventListener('click', this.ffzClickHost.bind(btn, this, false));
|
||||
|
||||
|
||||
var before;
|
||||
try { before = container.querySelector(':scope > .theatre-button'); }
|
||||
catch(err) { before = undefined; }
|
||||
|
||||
|
||||
if ( before )
|
||||
container.insertBefore(btn, before);
|
||||
else
|
||||
|
@ -307,8 +307,8 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
btn.title += ' You have ' + hosts_left + ' host command' + utils.pluralize(hosts_left) + ' remaining this half hour.';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if ( hosted_id ) {
|
||||
var container = el && el.querySelector('#hostmode .channel-actions'),
|
||||
btn = container && container.querySelector('#ffz-ui-host-button');
|
||||
|
@ -321,9 +321,9 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
btn = document.createElement('span');
|
||||
btn.id = 'ffz-ui-host-button';
|
||||
btn.className = 'button action tooltip';
|
||||
|
||||
|
||||
btn.addEventListener('click', this.ffzClickHost.bind(btn, this, true));
|
||||
|
||||
|
||||
var before;
|
||||
try { before = container.querySelector(':scope > .theatre-button'); }
|
||||
catch(err) { before = undefined; }
|
||||
|
@ -344,9 +344,9 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
if ( typeof hosts_left === "number" )
|
||||
btn.title += ' You have ' + hosts_left + ' host command' + utils.pluralize(hosts_left) + ' remaining this half hour.';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
ffzClickHost: function(controller, is_host) {
|
||||
var target = controller.get(is_host ? 'controller.hostModeTarget.id' : 'controller.id'),
|
||||
user = f.get_user(),
|
||||
|
@ -454,7 +454,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
var container = el && el.querySelector('.stats-and-actions .channel-stats'),
|
||||
stat_el = container && container.querySelector('#ffz-ui-player-stats'),
|
||||
el = stat_el && stat_el.querySelector('span'),
|
||||
|
||||
|
||||
player_cont = f.players && f.players[channel_id],
|
||||
player = player_cont && player_cont.player,
|
||||
stats = player && player.stats;
|
||||
|
@ -468,29 +468,29 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
stat_el = document.createElement('span');
|
||||
stat_el.id = 'ffz-ui-player-stats';
|
||||
stat_el.className = 'ffz stat tooltip';
|
||||
|
||||
|
||||
stat_el.innerHTML = constants.GRAPH + " ";
|
||||
el = document.createElement('span');
|
||||
stat_el.appendChild(el);
|
||||
|
||||
|
||||
var other = container.querySelector('#ffz-uptime-display');
|
||||
if ( other )
|
||||
container.insertBefore(stat_el, other.nextSibling);
|
||||
else
|
||||
container.appendChild(stat_el);
|
||||
}
|
||||
|
||||
|
||||
stat_el.title = 'Stream Latency\nFPS: ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps';
|
||||
el.textContent = stats.hlsLatencyBroadcaster + 's';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if ( hosted_id ) {
|
||||
var container = el && el.querySelector('#hostmode .channel-stats'),
|
||||
stat_el = container && container.querySelector('#ffz-ui-player-stats'),
|
||||
el = stat_el && stat_el.querySelector('span'),
|
||||
|
||||
|
||||
player_cont = f.players && f.players[hosted_id],
|
||||
player = player_cont && player_cont.player,
|
||||
stats = player && player.stats;
|
||||
|
@ -504,22 +504,22 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
stat_el = document.createElement('span');
|
||||
stat_el.id = 'ffz-ui-player-stats';
|
||||
stat_el.className = 'ffz stat tooltip';
|
||||
|
||||
|
||||
stat_el.innerHTML = constants.GRAPH + " ";
|
||||
el = document.createElement('span');
|
||||
stat_el.appendChild(el);
|
||||
|
||||
|
||||
var other = container.querySelector('#ffz-uptime-display');
|
||||
if ( other )
|
||||
container.insertBefore(stat_el, other.nextSibling);
|
||||
else
|
||||
container.appendChild(stat_el);
|
||||
}
|
||||
|
||||
|
||||
stat_el.title = 'Stream Latency\nFPS: ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps';
|
||||
el.textContent = stats.hlsLatencyBroadcaster + 's';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
@ -667,10 +667,10 @@ FFZ.settings_info.hosted_channels = {
|
|||
var cb = document.querySelector('input.ffz-setting-hosted-channels');
|
||||
if ( cb )
|
||||
cb.checked = val;
|
||||
|
||||
|
||||
if ( ! this._cindex )
|
||||
return;
|
||||
|
||||
|
||||
var chan = this._cindex.get('controller.model'),
|
||||
room = chan && this.rooms && this.rooms[chan.get('id')],
|
||||
target = room && room.room && room.room.get('ffz_host_target');
|
||||
|
|
|
@ -12,7 +12,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.",
|
||||
|
||||
|
@ -25,22 +25,22 @@ FFZ.settings_info.minimal_chat = {
|
|||
f._roomv && f._roomv.get('stuckToBottom') && f._roomv._scrollToBottom();
|
||||
},0);
|
||||
}
|
||||
|
||||
|
||||
if ( this._chatv && this._chatv.get('controller.showList') )
|
||||
this._chatv.set('controller.showList', false);
|
||||
|
||||
|
||||
// Remove the style if we have it.
|
||||
if ( ! val && this._chat_style ) {
|
||||
if ( this._inputv ) {
|
||||
if ( this._inputv._ffz_minimal_style )
|
||||
this._inputv._ffz_minimal_style.innerHTML = '';
|
||||
|
||||
|
||||
this._inputv._ffz_last_height = undefined;
|
||||
}
|
||||
|
||||
|
||||
utils.update_css(this._chat_style, "input_height", '');
|
||||
this._roomv && this._roomv.get('stuckToBottom') && this._roomv._scrollToBottom();
|
||||
|
||||
|
||||
} else if ( this._inputv )
|
||||
this._inputv.ffzResizeInput();
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ FFZ.settings_info.remove_deleted = {
|
|||
total = msgs.get('length'),
|
||||
i = total,
|
||||
alternate;
|
||||
|
||||
|
||||
while(i--) {
|
||||
var msg = msgs.get(i);
|
||||
|
||||
|
@ -81,7 +81,7 @@ FFZ.settings_info.remove_deleted = {
|
|||
msgs.removeAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if ( alternate === undefined )
|
||||
alternate = msg.ffz_alternate;
|
||||
else {
|
||||
|
@ -186,11 +186,11 @@ FFZ.prototype.setup_chatview = function() {
|
|||
ffzUpdateChannels: function() {
|
||||
if ( ! f._chatv )
|
||||
return;
|
||||
|
||||
|
||||
f._chatv.ffzRebuildMenu();
|
||||
if ( f.settings.group_tabs )
|
||||
f._chatv.ffzRebuildTabs();
|
||||
|
||||
|
||||
}.observes("currentChannelRoom", "connectedPrivateGroupRooms"),
|
||||
|
||||
removeCurrentChannelRoom: function() {
|
||||
|
@ -204,7 +204,7 @@ FFZ.prototype.setup_chatview = function() {
|
|||
if ( ! f.settings.pinned_rooms || f.settings.pinned_rooms.indexOf(room_id) === -1 ) {
|
||||
if ( room === this.get("currentRoom") )
|
||||
this.blurRoom();
|
||||
|
||||
|
||||
// Don't destroy it if it's the user's room.
|
||||
if ( room && user && user.login === room_id )
|
||||
room.destroy();
|
||||
|
@ -292,7 +292,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});
|
||||
this.$('.chat-messages').find('.html-tooltip').tipsy({live: true, html: true, gravity: jQuery.fn.tipsy.autoNS});
|
||||
|
||||
if ( !f.has_bttv && f.settings.group_tabs )
|
||||
this.ffzEnableTabs();
|
||||
|
@ -330,7 +330,7 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
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');
|
||||
|
@ -361,23 +361,23 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
}),
|
||||
|
||||
// Better Menu
|
||||
|
||||
|
||||
ffzRebuildMenu: function() {
|
||||
return;
|
||||
|
||||
|
||||
var el = this.get('element'),
|
||||
room_list = el && el.querySelector('.chat-rooms .tse-content');
|
||||
|
||||
|
||||
if ( ! room_list )
|
||||
return;
|
||||
|
||||
if ( ! room_list.classList.contains('ffz-room-list') ) {
|
||||
room_list.classList.add('ffz-room-list');
|
||||
|
||||
|
||||
// Find the Pending Invitations
|
||||
var headers = room_list.querySelectorAll('.list-header'),
|
||||
hdr = headers.length ? headers[headers.length-1] : undefined;
|
||||
|
||||
|
||||
if ( hdr ) {
|
||||
hdr.classList.add('ffz');
|
||||
if ( hdr.nextSibling && hdr.nextSibling.classList )
|
||||
|
@ -389,7 +389,7 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
// Channel Table
|
||||
var t = this,
|
||||
chan_table = this._ffz_chan_table || room_list.querySelector('#ffz-channel-table tbody');
|
||||
|
||||
|
||||
if ( ! chan_table ) {
|
||||
var tbl = document.createElement('table');
|
||||
tbl.setAttribute('cellspacing', 0);
|
||||
|
@ -409,13 +409,13 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
row = this.ffzBuildRow(this, room, true);
|
||||
row && chan_table.appendChild(row);
|
||||
}
|
||||
|
||||
|
||||
// Host Target
|
||||
if ( this._ffz_host_room ) {
|
||||
row = this.ffzBuildRow(this, this._ffz_host_room, false, true);
|
||||
row && chan_table.appendChild(row);
|
||||
}
|
||||
|
||||
|
||||
// Pinned Rooms
|
||||
for(var i=0; i < f.settings.pinned_rooms.length; i++) {
|
||||
var room_id = f.settings.pinned_rooms[i];
|
||||
|
@ -424,8 +424,8 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
row && chan_table.appendChild(row);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Group Chat Table
|
||||
var group_table = this._ffz_group_table || room_list.querySelector('#ffz-group-table tbody');
|
||||
if ( ! group_table ) {
|
||||
|
@ -434,49 +434,49 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
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>';
|
||||
|
||||
|
||||
var before = room_list.querySelector('#ffz-channel-table');
|
||||
room_list.insertBefore(tbl, before.nextSibling);
|
||||
|
||||
group_table = this._ffz_group_table = tbl.querySelector('tbody');
|
||||
}
|
||||
|
||||
|
||||
group_table.innerHTML = '';
|
||||
|
||||
|
||||
_.each(this.get('controller.connectedPrivateGroupRooms'), function(room) {
|
||||
var row = t.ffzBuildRow(t, room);
|
||||
row && group_table && group_table.appendChild(row);
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Change Create Tooltip
|
||||
var create_btn = el.querySelector('.button.create');
|
||||
if ( create_btn )
|
||||
create_btn.title = 'Create a Group Room';
|
||||
},
|
||||
|
||||
|
||||
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')),
|
||||
|
||||
|
||||
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);
|
||||
}));
|
||||
|
||||
|
||||
name_el.className = 'ffz-room';
|
||||
name_el.innerHTML = utils.sanitize(name);
|
||||
|
||||
|
||||
if ( current_channel ) {
|
||||
icon.innerHTML = constants.CAMERA;
|
||||
icon.title = name_el.title = "Current Channel";
|
||||
|
@ -486,12 +486,12 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
icon.title = name_el.title = "Hosted Channel";
|
||||
icon.className = name_el.className = 'tooltip';
|
||||
}
|
||||
|
||||
|
||||
toggle_pinned.className = toggle_visible.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'));
|
||||
|
||||
row.className = 'ffz-room-row';
|
||||
|
@ -499,20 +499,20 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
row.classList.toggle('host-channel', host_channel);
|
||||
row.classList.toggle('group-chat', group);
|
||||
row.classList.toggle('active', current);
|
||||
|
||||
|
||||
row.appendChild(icon);
|
||||
row.appendChild(name_el);
|
||||
|
||||
|
||||
if ( ! group ) {
|
||||
row.appendChild(toggle_pinned);
|
||||
btn = toggle_pinned.querySelector('a.switch');
|
||||
btn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation && e.stopPropagation();
|
||||
|
||||
|
||||
var room_id = room.get('id'),
|
||||
is_pinned = f.settings.pinned_rooms.indexOf(room_id) !== -1;
|
||||
|
||||
|
||||
if ( is_pinned )
|
||||
f._leave_room(room_id);
|
||||
else
|
||||
|
@ -527,14 +527,14 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
btn.title = 'Leave Group';
|
||||
|
||||
name_el.appendChild(btn);
|
||||
|
||||
|
||||
btn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation && e.stopPropagation();
|
||||
|
||||
if ( ! confirm('Are you sure you want to leave the group room "' + name + '"?') )
|
||||
return;
|
||||
|
||||
|
||||
room.get('isGroupRoom') && room.del();
|
||||
});
|
||||
}
|
||||
|
@ -544,12 +544,12 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
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 )
|
||||
|
||||
if ( is_visible )
|
||||
visible_rooms.removeObject(room_id);
|
||||
else
|
||||
visible_rooms.push(room_id);
|
||||
|
@ -558,13 +558,13 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
this.classList.toggle('active', !is_visible);
|
||||
view.ffzRebuildTabs();
|
||||
});
|
||||
|
||||
|
||||
row.addEventListener('click', function() {
|
||||
var controller = view.get('controller');
|
||||
controller.focusRoom(room);
|
||||
controller.set('showList', false);
|
||||
});
|
||||
|
||||
|
||||
return row;
|
||||
},
|
||||
|
||||
|
@ -684,7 +684,7 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
|
||||
ffzTabUnread: function(room_id) {
|
||||
// TODO: Update menu.
|
||||
|
||||
|
||||
if ( f.has_bttv || ! f.settings.group_tabs )
|
||||
return;
|
||||
|
||||
|
@ -803,7 +803,7 @@ FFZ.prototype.connect_extra_chat = function() {
|
|||
r = Room && Room.findOne(user.login);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( this.has_bttv )
|
||||
return;
|
||||
|
||||
|
|
|
@ -55,7 +55,8 @@ FFZ.settings_info.replace_bad_emotes = {
|
|||
|
||||
name: "Fix Low Quality Twitch Global Emoticons",
|
||||
help: "Replace emoticons such as DansGame and RedCoat with cleaned up versions that don't have pixels around the edges or white backgrounds for nicer display on dark chat."
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
FFZ.settings_info.parse_emoji = {
|
||||
type: "boolean",
|
||||
|
@ -211,7 +212,6 @@ FFZ.settings_info.clickable_emoticons = {
|
|||
};
|
||||
|
||||
|
||||
|
||||
FFZ.settings_info.link_info = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
|
@ -333,7 +333,7 @@ FFZ.settings_info.high_contrast_chat = {
|
|||
'112': "Background + Bold",
|
||||
'111': 'All'
|
||||
},
|
||||
value: '000',
|
||||
value: '222',
|
||||
|
||||
category: "Chat Appearance",
|
||||
no_bttv: true,
|
||||
|
@ -343,7 +343,7 @@ FFZ.settings_info.high_contrast_chat = {
|
|||
|
||||
process_value: function(val) {
|
||||
if ( val === false )
|
||||
return '000';
|
||||
return '222';
|
||||
else if ( val === true )
|
||||
return '111';
|
||||
return val;
|
||||
|
|
|
@ -50,6 +50,43 @@ try {
|
|||
// Settings
|
||||
// ----------------
|
||||
|
||||
FFZ.basic_settings.enhanced_moderation_cards = {
|
||||
type: "boolean",
|
||||
|
||||
no_bttv: true,
|
||||
|
||||
category: "Chat",
|
||||
name: "Enhanced Moderation Cards",
|
||||
help: "Improve moderation cards with hotkeys, additional buttons, chat history, and other information to make moderating easier.",
|
||||
|
||||
get: function() {
|
||||
return this.settings.mod_card_hotkeys &&
|
||||
this.settings.mod_card_info &&
|
||||
this.settings.mod_card_history;
|
||||
},
|
||||
|
||||
set: function(val) {
|
||||
this.settings.set('mod_card_hotkeys', val);
|
||||
this.settings.set('mod_card_info', val);
|
||||
this.settings.set('mod_card_history', val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
FFZ.basic_settings.chat_hover_pause = {
|
||||
type: "boolean",
|
||||
|
||||
no_bttv: true,
|
||||
|
||||
category: "Chat",
|
||||
name: "Pause Chat Scrolling on Mouse Hover",
|
||||
help: "Automatically prevent the chat from scrolling when moving the mouse over it to prevent moderation mistakes and link misclicks.",
|
||||
|
||||
get: 'chat_hover_pause',
|
||||
set: 'chat_hover_pause'
|
||||
};
|
||||
|
||||
|
||||
FFZ.settings_info.chat_hover_pause = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
|
@ -58,7 +95,7 @@ FFZ.settings_info.chat_hover_pause = {
|
|||
|
||||
category: "Chat Moderation",
|
||||
name: "Pause Chat Scrolling on Mouse Hover",
|
||||
help: "Automatically prevent the chat from scrolling when moving the mouse over it to prevent moderation mistakes and link mis-clicks.",
|
||||
help: "Automatically prevent the chat from scrolling when moving the mouse over it to prevent moderation mistakes and link misclicks.",
|
||||
|
||||
on_update: function(val) {
|
||||
if ( ! this._roomv )
|
||||
|
@ -151,7 +188,7 @@ FFZ.settings_info.mod_card_buttons = {
|
|||
else
|
||||
old_val += ' ' + cmd;
|
||||
}
|
||||
|
||||
|
||||
var new_val = prompt("Moderation Card Additional Buttons\n\nPlease enter a list of additional commands to display buttons for on moderation cards. 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}\"", old_val);
|
||||
|
||||
if ( new_val === null || new_val === undefined )
|
||||
|
@ -159,7 +196,7 @@ FFZ.settings_info.mod_card_buttons = {
|
|||
|
||||
var vals = [];
|
||||
new_val = new_val.trim();
|
||||
|
||||
|
||||
while(new_val) {
|
||||
if ( new_val.charAt(0) === '"' ) {
|
||||
var end = new_val.indexOf('"', 1);
|
||||
|
@ -170,8 +207,8 @@ FFZ.settings_info.mod_card_buttons = {
|
|||
if ( segment )
|
||||
vals.push(segment);
|
||||
|
||||
new_val = new_val.substr(end + 1);
|
||||
|
||||
new_val = new_val.substr(end + 1);
|
||||
|
||||
} else {
|
||||
var ind = new_val.indexOf(' ');
|
||||
if ( ind === -1 ) {
|
||||
|
@ -274,7 +311,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
|
||||
if ( typeof followers === "number" ) {
|
||||
out += '<span class="stat tooltip" title="Followers">' + constants.HEART + ' ' + utils.number_commas(followers || 0) + '</span>';
|
||||
|
||||
|
||||
} else if ( followers === undefined ) {
|
||||
var t = this;
|
||||
this.set('cardInfo.user.ffz_followers', false);
|
||||
|
@ -299,7 +336,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
userName: Ember.computed("cardInfo.user.id", "cardInfo.user.display_name", function() {
|
||||
var user_id = this.get("cardInfo.user.id"),
|
||||
alias = f.aliases[user_id];
|
||||
|
||||
|
||||
return alias || this.get("cardInfo.user.display_name") || user_id.capitalize();
|
||||
}),
|
||||
|
||||
|
@ -313,7 +350,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
var el = this.get('element'),
|
||||
controller = this.get('controller'),
|
||||
line,
|
||||
|
||||
|
||||
user_id = controller.get('cardInfo.user.id'),
|
||||
alias = f.aliases[user_id];
|
||||
|
||||
|
@ -340,7 +377,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
after = el.querySelector('h3.name');
|
||||
if ( after ) {
|
||||
el.classList.add('ffz-has-info');
|
||||
info.className = 'info channel-stats';
|
||||
info.className = 'info channel-stats';
|
||||
after.parentElement.insertBefore(info, after.nextSibling);
|
||||
this.ffzRebuildInfo();
|
||||
}
|
||||
|
@ -350,16 +387,16 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
if ( f.settings.mod_card_buttons && f.settings.mod_card_buttons.length ) {
|
||||
line = document.createElement('div');
|
||||
line.className = 'extra-interface interface clearfix';
|
||||
|
||||
|
||||
var cmds = {},
|
||||
add_btn_click = function(cmd) {
|
||||
var user_id = controller.get('cardInfo.user.id'),
|
||||
cont = App.__container__.lookup('controller:chat'),
|
||||
room = cont && cont.get('currentRoom');
|
||||
|
||||
|
||||
room && room.send(cmd.replace(/{user}/g, user_id));
|
||||
},
|
||||
|
||||
|
||||
add_btn_make = function(cmd) {
|
||||
var btn = document.createElement('button'),
|
||||
segment = cmd.split(' ', 1)[0],
|
||||
|
@ -373,12 +410,12 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
btn.className = 'button';
|
||||
btn.innerHTML = utils.sanitize(title);
|
||||
btn.title = utils.sanitize(cmd.replace(/{user}/g, controller.get('cardInfo.user.id') || '{user}'));
|
||||
|
||||
|
||||
jQuery(btn).tipsy();
|
||||
btn.addEventListener('click', add_btn_click.bind(this, cmd));
|
||||
return btn;
|
||||
};
|
||||
|
||||
|
||||
var cmds = {};
|
||||
for(var i=0; i < f.settings.mod_card_buttons.length; i++)
|
||||
cmds[f.settings.mod_card_buttons[i].split(' ',1)[0]] = (cmds[f.settings.mod_card_buttons[i].split(' ',1)[0]] || 0) + 1;
|
||||
|
@ -392,7 +429,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
|
||||
line.appendChild(add_btn_make(cmd))
|
||||
}
|
||||
|
||||
|
||||
el.appendChild(line);
|
||||
}
|
||||
|
||||
|
@ -431,7 +468,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
// Only do the big stuff if we're mod.
|
||||
if ( controller.get('cardInfo.isModeratorOrHigher') ) {
|
||||
el.classList.add('ffz-is-mod');
|
||||
|
||||
|
||||
// Key Handling
|
||||
if ( f.settings.mod_card_hotkeys ) {
|
||||
el.classList.add('no-mousetrap');
|
||||
|
@ -544,31 +581,31 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
|
||||
msg_btn.title = "Whisper User";
|
||||
jQuery(msg_btn).tipsy();
|
||||
|
||||
|
||||
|
||||
|
||||
var real_msg = document.createElement('button');
|
||||
real_msg.className = 'message-button button glyph-only message tooltip';
|
||||
real_msg.innerHTML = MESSAGE;
|
||||
real_msg.title = "Message User";
|
||||
|
||||
|
||||
real_msg.addEventListener('click', function() {
|
||||
window.open('http://www.twitch.tv/message/compose?to=' + controller.get('cardInfo.user.id'));
|
||||
})
|
||||
|
||||
msg_btn.parentElement.insertBefore(real_msg, msg_btn.nextSibling);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Alias Button
|
||||
var alias_btn = document.createElement('button');
|
||||
alias_btn.className = 'alias button glyph-only tooltip';
|
||||
alias_btn.innerHTML = constants.EDIT;
|
||||
alias_btn.title = "Set Alias";
|
||||
|
||||
|
||||
alias_btn.addEventListener('click', function() {
|
||||
var user = controller.get('cardInfo.user.id'),
|
||||
alias = f.aliases[user];
|
||||
|
||||
|
||||
var new_val = prompt("Alias for User: " + user + "\n\nPlease enter an alias for the user. Leave it blank to remove the alias.", alias);
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
@ -579,20 +616,20 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
|
||||
f.aliases[user] = new_val;
|
||||
f.save_aliases();
|
||||
|
||||
|
||||
// Update UI
|
||||
f._update_alias(user);
|
||||
|
||||
|
||||
Ember.propertyDidChange(controller, 'userName');
|
||||
var name = el.querySelector('h3.name'),
|
||||
link = name && name.querySelector('a');
|
||||
|
||||
|
||||
if ( link )
|
||||
name = link;
|
||||
if ( name )
|
||||
name.classList.toggle('ffz-alias', new_val);
|
||||
name.classList.toggle('ffz-alias', new_val);
|
||||
});
|
||||
|
||||
|
||||
if ( msg_btn )
|
||||
msg_btn.parentElement.insertBefore(alias_btn, msg_btn);
|
||||
else {
|
||||
|
@ -676,22 +713,21 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
|
||||
FFZ.prototype._update_alias = function(user) {
|
||||
var alias = this.aliases && this.aliases[user],
|
||||
display_name = alias,
|
||||
cap_name = FFZ.get_capitalization(user),
|
||||
display_name = alias || cap_name,
|
||||
el = this._roomv && this._roomv.get('element'),
|
||||
lines = el && el.querySelectorAll('.chat-line[data-sender="' + user + '"]');
|
||||
|
||||
|
||||
if ( ! lines )
|
||||
return;
|
||||
|
||||
if ( ! display_name )
|
||||
display_name = FFZ.get_capitalization(user);
|
||||
|
||||
for(var i=0, l = lines.length; i < l; i++) {
|
||||
var line = lines[i],
|
||||
el_from = line.querySelector('.from');
|
||||
|
||||
|
||||
el_from.classList.toggle('ffz-alias', alias);
|
||||
el_from.textContent = display_name;
|
||||
el_from.title = alias ? cap_name : '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,12 +33,12 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
this._dark_style.parentElement.removeChild(this._dark_style);
|
||||
this._dark_style = undefined;
|
||||
}
|
||||
|
||||
|
||||
if ( this._layout_style ) {
|
||||
this._layout_style.parentElement.removeChild(this._layout_style);
|
||||
this._layout_style = undefined;
|
||||
}
|
||||
|
||||
|
||||
if ( this._chat_style ) {
|
||||
utils.update_css(this._chat_style, 'chat_font_size', '');
|
||||
utils.update_css(this._chat_style, 'chat_ts_font_size', '');
|
||||
|
@ -76,6 +76,7 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
if ( this.settings.following_count ) {
|
||||
this._schedule_following_count();
|
||||
this._draw_following_count();
|
||||
this._draw_following_channels();
|
||||
}
|
||||
|
||||
// Remove Sub Count
|
||||
|
@ -186,7 +187,7 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
var original_emoticonize = BetterTTV.chat.templates.emoticonize;
|
||||
BetterTTV.chat.templates.emoticonize = function(message, emotes) {
|
||||
var tokens = original_emoticonize(message, emotes),
|
||||
|
||||
|
||||
room = (received_room || BetterTTV.getChannel()),
|
||||
l_room = room && room.toLowerCase(),
|
||||
l_sender = received_sender && received_sender.toLowerCase(),
|
||||
|
|
|
@ -21,7 +21,7 @@ FFZ.get = function() { return FFZ.instance; }
|
|||
|
||||
// Version
|
||||
var VER = FFZ.version_info = {
|
||||
major: 3, minor: 5, revision: 10,
|
||||
major: 3, minor: 5, revision: 12,
|
||||
toString: function() {
|
||||
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
|
||||
}
|
||||
|
|
750
src/settings.js
750
src/settings.js
|
@ -1,5 +1,6 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
constants = require("./constants");
|
||||
constants = require("./constants"),
|
||||
FileSaver = require("./FileSaver");
|
||||
|
||||
|
||||
make_ls = function(key) {
|
||||
|
@ -11,9 +12,28 @@ var FFZ = window.FrankerFaceZ,
|
|||
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));
|
||||
},
|
||||
|
||||
|
||||
toggle_basic_setting = function(swit, key) {
|
||||
var getter = FFZ.basic_settings[key].get,
|
||||
val = !(typeof getter === 'function' ? getter.bind(this)() : this.settings.get(getter)),
|
||||
|
||||
setter = FFZ.basic_settings[key].set;
|
||||
|
||||
if ( typeof setter === 'function' )
|
||||
setter.bind(this)(val);
|
||||
else
|
||||
this.settings.set(setter, val);
|
||||
|
||||
swit.classList.toggle('active', val);
|
||||
},
|
||||
|
||||
option_basic_setting = function(select, key) {
|
||||
FFZ.basic_settings[key].set.bind(this)(JSON.parse(select.options[select.selectedIndex].value));
|
||||
};
|
||||
|
||||
|
||||
|
@ -21,7 +41,11 @@ var FFZ = window.FrankerFaceZ,
|
|||
// Initializer
|
||||
// --------------------
|
||||
|
||||
FFZ.settings_info = {};
|
||||
FFZ.settings_info = {
|
||||
advanced_settings: { value: false, visible: false }
|
||||
};
|
||||
|
||||
FFZ.basic_settings = {};
|
||||
|
||||
FFZ.prototype.load_settings = function() {
|
||||
this.log("Loading settings.");
|
||||
|
@ -61,200 +85,610 @@ FFZ.prototype.load_settings = function() {
|
|||
}
|
||||
|
||||
|
||||
// --------------------
|
||||
// Backup and Restore
|
||||
// --------------------
|
||||
|
||||
FFZ.prototype.save_settings_file = function() {
|
||||
var data = {
|
||||
version: 1,
|
||||
script_version: FFZ.version_info + '',
|
||||
aliases: this.aliases,
|
||||
settings: {}
|
||||
};
|
||||
|
||||
for(var key in FFZ.settings_info) {
|
||||
if ( ! FFZ.settings_info.hasOwnProperty(key) )
|
||||
continue;
|
||||
|
||||
var info = FFZ.settings_info[key],
|
||||
ls_key = info.storage_key || make_ls(key);
|
||||
|
||||
if ( localStorage.hasOwnProperty(ls_key) )
|
||||
data.settings[key] = this.settings[key];
|
||||
}
|
||||
|
||||
var blob = new Blob([JSON.stringify(data, null, 4)], {type: "application/json;charset=utf-8"});
|
||||
FileSaver.saveAs(blob, "ffz-settings.json");
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.load_settings_file = function(file) {
|
||||
if ( typeof file === "string" )
|
||||
this._load_settings_file(file);
|
||||
else {
|
||||
var reader = new FileReader(),
|
||||
f = this;
|
||||
|
||||
reader.onload = function(e) { f._load_settings_file(e.target.result); }
|
||||
reader.readAsText(file);
|
||||
}
|
||||
}
|
||||
|
||||
FFZ.prototype._load_settings_file = function(data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(err) {
|
||||
this.error("Error Loading Settings: " + err);
|
||||
return alert("There was an error attempting to read the provided settings data.");
|
||||
}
|
||||
|
||||
this.log("Loading Settings Data", data);
|
||||
|
||||
var skipped = [],
|
||||
applied = [];
|
||||
|
||||
if ( data.settings ) {
|
||||
for(var key in data.settings) {
|
||||
if ( ! FFZ.settings_info.hasOwnProperty(key) ) {
|
||||
skipped.push(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
var info = FFZ.settings_info[key],
|
||||
val = data.settings[key];
|
||||
|
||||
if ( info.process_value )
|
||||
val = info.process_value.bind(this)(val);
|
||||
|
||||
if ( val !== this.settings.get(key) )
|
||||
this.settings.set(key, val);
|
||||
|
||||
applied.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Do this in a timeout so that any styles have a moment to update.
|
||||
setTimeout(function(){
|
||||
alert('Successfully loaded ' + applied.length + ' settings and skipped ' + skipped.length + ' settings.');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// --------------------
|
||||
// Menu Page
|
||||
// --------------------
|
||||
|
||||
FFZ.menu_pages.settings = {
|
||||
render: function(view, container) {
|
||||
var settings = {},
|
||||
categories = [],
|
||||
is_android = navigator.userAgent.indexOf('Android') !== -1;
|
||||
// Bottom Bar
|
||||
var menu = document.createElement('ul'),
|
||||
page = document.createElement('div'),
|
||||
|
||||
for(var key in FFZ.settings_info) {
|
||||
if ( ! FFZ.settings_info.hasOwnProperty(key) )
|
||||
tab_basic = document.createElement('li'),
|
||||
link_basic = document.createElement('a'),
|
||||
|
||||
tab_adv = document.createElement('li'),
|
||||
link_adv = document.createElement('a'),
|
||||
|
||||
tab_save = document.createElement('li'),
|
||||
link_save = document.createElement('a'),
|
||||
|
||||
height = parseInt(container.style.maxHeight || '0');
|
||||
|
||||
|
||||
// Height Calculation
|
||||
if ( ! height )
|
||||
height = Math.max(200, view.$().height() - 172);
|
||||
|
||||
if ( height && height !== NaN ) {
|
||||
height -= 37;
|
||||
page.style.maxHeight = height + 'px';
|
||||
}
|
||||
|
||||
// Menu Building
|
||||
page.className = 'ffz-ui-sub-menu-page';
|
||||
menu.className = 'menu sub-menu clearfix';
|
||||
|
||||
tab_basic.className = 'item';
|
||||
tab_basic.id = 'ffz-settings-page-basic';
|
||||
link_basic.innerHTML = 'Basic';
|
||||
tab_basic.appendChild(link_basic);
|
||||
|
||||
tab_adv.className = 'item';
|
||||
tab_adv.id = 'ffz-settings-page-advanced';
|
||||
link_adv.innerHTML = 'Advanced';
|
||||
tab_adv.appendChild(link_adv);
|
||||
|
||||
tab_save.className = 'item';
|
||||
tab_save.id = 'ffz-settings-page-save';
|
||||
link_save.textContent = 'Backup & Restore';
|
||||
tab_save.appendChild(link_save);
|
||||
|
||||
menu.appendChild(tab_basic);
|
||||
menu.appendChild(tab_adv);
|
||||
menu.appendChild(tab_save);
|
||||
|
||||
var cp = FFZ.menu_pages.settings.change_page;
|
||||
|
||||
link_basic.addEventListener('click', cp.bind(this, view, container, menu, page, 'basic'));
|
||||
link_adv.addEventListener('click', cp.bind(this, view, container, menu, page, 'advanced'));
|
||||
link_save.addEventListener('click', cp.bind(this, view, container, menu, page, 'save'));
|
||||
|
||||
if ( this.settings.advanced_settings )
|
||||
link_adv.click();
|
||||
else
|
||||
link_basic.click();
|
||||
|
||||
container.appendChild(page);
|
||||
container.appendChild(menu);
|
||||
},
|
||||
|
||||
change_page: function(view, container, menu, page, key) {
|
||||
page.innerHTML = '';
|
||||
page.setAttribute('data-page', key);
|
||||
|
||||
var els = menu.querySelectorAll('li.active');
|
||||
for(var i=0, l = els.length; i < l; i++)
|
||||
els[i].classList.remove('active');
|
||||
|
||||
var el = menu.querySelector('#ffz-settings-page-' + key);
|
||||
if ( el )
|
||||
el.classList.add('active');
|
||||
|
||||
FFZ.menu_pages.settings['render_' + key].bind(this)(view, page);
|
||||
|
||||
if ( key === 'advanced' )
|
||||
this.settings.set('advanced_settings', true);
|
||||
else if ( key === 'basic' )
|
||||
this.settings.set('advanced_settings', false);
|
||||
},
|
||||
|
||||
render_save: function(view, container) {
|
||||
var backup_head = document.createElement('div'),
|
||||
restore_head = document.createElement('div'),
|
||||
backup_cont = document.createElement('div'),
|
||||
restore_cont = document.createElement('div'),
|
||||
|
||||
backup_para = document.createElement('p'),
|
||||
backup_link = document.createElement('a'),
|
||||
backup_help = document.createElement('span'),
|
||||
|
||||
restore_para = document.createElement('p'),
|
||||
restore_input = document.createElement('input'),
|
||||
restore_link = document.createElement('a'),
|
||||
restore_help = document.createElement('span'),
|
||||
f = this;
|
||||
|
||||
|
||||
backup_cont.className = 'chat-menu-content';
|
||||
backup_head.className = 'heading';
|
||||
backup_head.innerHTML = 'Backup Settings';
|
||||
backup_cont.appendChild(backup_head);
|
||||
|
||||
backup_para.className = 'clearfix option';
|
||||
|
||||
backup_link.href = '#';
|
||||
backup_link.innerHTML = 'Save to File';
|
||||
backup_link.addEventListener('click', this.save_settings_file.bind(this));
|
||||
|
||||
backup_help.className = 'help';
|
||||
backup_help.innerHTML = 'This generates a JSON file containing all of your settings and prompts you to save it.';
|
||||
|
||||
backup_para.appendChild(backup_link);
|
||||
backup_para.appendChild(backup_help);
|
||||
backup_cont.appendChild(backup_para);
|
||||
|
||||
restore_cont.className = 'chat-menu-content';
|
||||
restore_head.className = 'heading';
|
||||
restore_head.innerHTML = 'Restore Settings';
|
||||
restore_cont.appendChild(restore_head);
|
||||
|
||||
restore_para.className = 'clearfix option';
|
||||
|
||||
restore_input.type = 'file';
|
||||
restore_input.addEventListener('change', function() { f.load_settings_file(this.files[0]); })
|
||||
|
||||
restore_link.href = '#';
|
||||
restore_link.innerHTML = 'Restore from File';
|
||||
restore_link.addEventListener('click', function(e) { e.preventDefault(); restore_input.click(); });
|
||||
|
||||
restore_help.className = 'help';
|
||||
restore_help.innerHTML = 'This loads settings from a previously generated JSON file.';
|
||||
|
||||
restore_para.appendChild(restore_link);
|
||||
restore_para.appendChild(restore_help);
|
||||
restore_cont.appendChild(restore_para);
|
||||
|
||||
container.appendChild(backup_cont);
|
||||
container.appendChild(restore_cont);
|
||||
},
|
||||
|
||||
render_basic: function(view, container) {
|
||||
var settings = {},
|
||||
categories = [],
|
||||
is_android = navigator.userAgent.indexOf('Android') !== -1;
|
||||
|
||||
for(var key in FFZ.basic_settings) {
|
||||
if ( ! FFZ.basic_settings.hasOwnProperty(key) )
|
||||
continue;
|
||||
|
||||
var info = FFZ.basic_settings[key],
|
||||
cat = info.category || "Miscellaneous",
|
||||
cs = settings[cat];
|
||||
|
||||
if ( info.visible !== undefined && info.visible !== null ) {
|
||||
var visible = info.visible;
|
||||
if ( typeof info.visible == "function" )
|
||||
visible = info.visible.bind(this)();
|
||||
|
||||
if ( ! visible )
|
||||
continue;
|
||||
|
||||
var info = FFZ.settings_info[key],
|
||||
cat = info.category || "Miscellaneous",
|
||||
cs = settings[cat];
|
||||
|
||||
if ( info.visible !== undefined && info.visible !== null ) {
|
||||
var visible = info.visible;
|
||||
if ( typeof info.visible == "function" )
|
||||
visible = info.visible.bind(this)();
|
||||
|
||||
if ( ! visible )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( is_android && info.no_mobile )
|
||||
continue;
|
||||
|
||||
if ( ! cs ) {
|
||||
categories.push(cat);
|
||||
cs = settings[cat] = [];
|
||||
}
|
||||
|
||||
cs.push([key, info]);
|
||||
}
|
||||
|
||||
categories.sort(function(a,b) {
|
||||
var a = a.toLowerCase(),
|
||||
b = b.toLowerCase();
|
||||
if ( is_android && info.no_mobile )
|
||||
continue;
|
||||
|
||||
if ( a === "debugging" )
|
||||
a = "zzz" + a;
|
||||
if ( ! cs ) {
|
||||
categories.push(cat);
|
||||
cs = settings[cat] = [];
|
||||
}
|
||||
|
||||
if ( b === "debugging" )
|
||||
b = "zzz" + b;
|
||||
cs.push([key, info]);
|
||||
}
|
||||
|
||||
categories.sort(function(a,b) {
|
||||
var a = a.toLowerCase(),
|
||||
b = b.toLowerCase();
|
||||
|
||||
if ( a === "debugging" )
|
||||
a = "zzz" + a;
|
||||
|
||||
if ( b === "debugging" )
|
||||
b = "zzz" + b;
|
||||
|
||||
if ( a < b ) return -1;
|
||||
else if ( a > b ) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
var f = this,
|
||||
current_page = this._ffz_basic_settings_page || categories[0];
|
||||
|
||||
for(var ci=0; ci < categories.length; ci++) {
|
||||
var category = categories[ci],
|
||||
cset = settings[category],
|
||||
|
||||
menu = document.createElement('div'),
|
||||
heading = document.createElement('div');
|
||||
|
||||
heading.className = 'heading';
|
||||
menu.className = 'chat-menu-content'; // collapsable';
|
||||
|
||||
menu.setAttribute('data-category', category);
|
||||
//menu.classList.toggle('collapsed', current_page !== category);
|
||||
|
||||
heading.innerHTML = category;
|
||||
menu.appendChild(heading);
|
||||
|
||||
/*menu.addEventListener('click', function() {
|
||||
if ( ! this.classList.contains('collapsed') )
|
||||
return;
|
||||
|
||||
var t = this,
|
||||
old_selection = container.querySelectorAll('.chat-menu-content:not(.collapsed)');
|
||||
for(var i=0; i < old_selection.length; i++)
|
||||
old_selection[i].classList.add('collapsed');
|
||||
|
||||
f._ffz_basic_settings_page = t.getAttribute('data-category');
|
||||
t.classList.remove('collapsed');
|
||||
setTimeout(function(){t.scrollIntoViewIfNeeded()});
|
||||
});*/
|
||||
|
||||
cset.sort(function(a,b) {
|
||||
var a = a[1],
|
||||
b = b[1],
|
||||
|
||||
at = a.type === "boolean" ? 1 : 2,
|
||||
bt = b.type === "boolean" ? 1 : 2,
|
||||
|
||||
an = a.name.toLowerCase(),
|
||||
bn = b.name.toLowerCase();
|
||||
|
||||
if ( at < bt ) return -1;
|
||||
else if ( at > bt ) return 1;
|
||||
|
||||
else if ( an < bn ) return -1;
|
||||
else if ( an > bn ) return 1;
|
||||
|
||||
if ( a < b ) return -1;
|
||||
else if ( a > b ) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
var f = this,
|
||||
current_page = this._ffz_settings_page || categories[0];
|
||||
for(var i=0; i < cset.length; i++) {
|
||||
var key = cset[i][0],
|
||||
info = cset[i][1],
|
||||
el = document.createElement('p'),
|
||||
val = info.type !== "button" && typeof info.get === 'function' ? info.get.bind(this)() : this.settings.get(info.get);
|
||||
|
||||
for(var ci=0; ci < categories.length; ci++) {
|
||||
var category = categories[ci],
|
||||
cset = settings[category],
|
||||
el.className = 'clearfix';
|
||||
|
||||
menu = document.createElement('div'),
|
||||
heading = document.createElement('div');
|
||||
if ( this.has_bttv && info.no_bttv ) {
|
||||
var label = document.createElement('span'),
|
||||
help = document.createElement('span');
|
||||
label.className = 'switch-label';
|
||||
label.innerHTML = info.name;
|
||||
|
||||
heading.className = 'heading';
|
||||
menu.className = 'chat-menu-content collapsable';
|
||||
|
||||
menu.setAttribute('data-category', category);
|
||||
menu.classList.toggle('collapsed', current_page !== category);
|
||||
help = document.createElement('span');
|
||||
help.className = 'help';
|
||||
help.innerHTML = 'Disabled due to incompatibility with BetterTTV.';
|
||||
|
||||
heading.innerHTML = category;
|
||||
menu.appendChild(heading);
|
||||
el.classList.add('disabled');
|
||||
el.appendChild(label);
|
||||
el.appendChild(help);
|
||||
|
||||
menu.addEventListener('click', function() {
|
||||
if ( ! this.classList.contains('collapsed') )
|
||||
return;
|
||||
} else {
|
||||
if ( info.type == "boolean" ) {
|
||||
var swit = document.createElement('a'),
|
||||
label = document.createElement('span');
|
||||
|
||||
var t = this,
|
||||
old_selection = container.querySelectorAll('.chat-menu-content:not(.collapsed)');
|
||||
for(var i=0; i < old_selection.length; i++)
|
||||
old_selection[i].classList.add('collapsed');
|
||||
|
||||
f._ffz_settings_page = t.getAttribute('data-category');
|
||||
t.classList.remove('collapsed');
|
||||
setTimeout(function(){t.scrollIntoViewIfNeeded()});
|
||||
});
|
||||
swit.className = 'switch';
|
||||
swit.classList.toggle('active', val);
|
||||
swit.innerHTML = "<span></span>";
|
||||
|
||||
cset.sort(function(a,b) {
|
||||
var a = a[1],
|
||||
b = b[1],
|
||||
|
||||
at = a.type === "boolean" ? 1 : 2,
|
||||
bt = b.type === "boolean" ? 1 : 2,
|
||||
|
||||
an = a.name.toLowerCase(),
|
||||
bn = b.name.toLowerCase();
|
||||
|
||||
if ( at < bt ) return -1;
|
||||
else if ( at > bt ) return 1;
|
||||
|
||||
else if ( an < bn ) return -1;
|
||||
else if ( an > bn ) return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
for(var i=0; i < cset.length; i++) {
|
||||
var key = cset[i][0],
|
||||
info = cset[i][1],
|
||||
el = document.createElement('p'),
|
||||
val = this.settings.get(key);
|
||||
|
||||
el.className = 'clearfix';
|
||||
|
||||
if ( this.has_bttv && info.no_bttv ) {
|
||||
var label = document.createElement('span'),
|
||||
help = document.createElement('span');
|
||||
label.className = 'switch-label';
|
||||
label.innerHTML = info.name;
|
||||
|
||||
help = document.createElement('span');
|
||||
help.className = 'help';
|
||||
help.innerHTML = 'Disabled due to incompatibility with BetterTTV.';
|
||||
|
||||
el.classList.add('disabled');
|
||||
el.appendChild(swit);
|
||||
el.appendChild(label);
|
||||
el.appendChild(help);
|
||||
|
||||
swit.addEventListener("click", toggle_basic_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_basic_setting.bind(this, select, key));
|
||||
|
||||
el.appendChild(label);
|
||||
el.appendChild(select);
|
||||
|
||||
} else {
|
||||
if ( info.type == "boolean" ) {
|
||||
var swit = document.createElement('a'),
|
||||
label = document.createElement('span');
|
||||
el.classList.add("option");
|
||||
var link = document.createElement('a');
|
||||
link.innerHTML = info.name;
|
||||
link.href = "#";
|
||||
el.appendChild(link);
|
||||
|
||||
swit.className = 'switch';
|
||||
swit.classList.toggle('active', val);
|
||||
swit.innerHTML = "<span></span>";
|
||||
|
||||
label.className = 'switch-label';
|
||||
label.innerHTML = info.name;
|
||||
|
||||
el.appendChild(swit);
|
||||
el.appendChild(label);
|
||||
|
||||
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');
|
||||
link.innerHTML = info.name;
|
||||
link.href = "#";
|
||||
el.appendChild(link);
|
||||
|
||||
link.addEventListener("click", info.method.bind(this));
|
||||
}
|
||||
|
||||
if ( info.help ) {
|
||||
var help = document.createElement('span');
|
||||
help.className = 'help';
|
||||
help.innerHTML = info.help;
|
||||
el.appendChild(help);
|
||||
}
|
||||
link.addEventListener("click", info.method.bind(this));
|
||||
}
|
||||
|
||||
menu.appendChild(el);
|
||||
if ( info.help ) {
|
||||
var help = document.createElement('span');
|
||||
help.className = 'help';
|
||||
help.innerHTML = info.help;
|
||||
el.appendChild(help);
|
||||
}
|
||||
}
|
||||
|
||||
container.appendChild(menu);
|
||||
menu.appendChild(el);
|
||||
}
|
||||
},
|
||||
|
||||
container.appendChild(menu);
|
||||
}
|
||||
},
|
||||
|
||||
render_advanced: function(view, container) {
|
||||
var settings = {},
|
||||
categories = [],
|
||||
is_android = navigator.userAgent.indexOf('Android') !== -1;
|
||||
|
||||
for(var key in FFZ.settings_info) {
|
||||
if ( ! FFZ.settings_info.hasOwnProperty(key) )
|
||||
continue;
|
||||
|
||||
var info = FFZ.settings_info[key],
|
||||
cat = info.category || "Miscellaneous",
|
||||
cs = settings[cat];
|
||||
|
||||
if ( info.visible !== undefined && info.visible !== null ) {
|
||||
var visible = info.visible;
|
||||
if ( typeof info.visible == "function" )
|
||||
visible = info.visible.bind(this)();
|
||||
|
||||
if ( ! visible )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( is_android && info.no_mobile )
|
||||
continue;
|
||||
|
||||
if ( ! cs ) {
|
||||
categories.push(cat);
|
||||
cs = settings[cat] = [];
|
||||
}
|
||||
|
||||
cs.push([key, info]);
|
||||
}
|
||||
|
||||
categories.sort(function(a,b) {
|
||||
var a = a.toLowerCase(),
|
||||
b = b.toLowerCase();
|
||||
|
||||
if ( a === "debugging" )
|
||||
a = "zzz" + a;
|
||||
|
||||
if ( b === "debugging" )
|
||||
b = "zzz" + b;
|
||||
|
||||
if ( a < b ) return -1;
|
||||
else if ( a > b ) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
var f = this,
|
||||
current_page = this._ffz_settings_page || categories[0];
|
||||
|
||||
for(var ci=0; ci < categories.length; ci++) {
|
||||
var category = categories[ci],
|
||||
cset = settings[category],
|
||||
|
||||
menu = document.createElement('div'),
|
||||
heading = document.createElement('div');
|
||||
|
||||
heading.className = 'heading';
|
||||
menu.className = 'chat-menu-content collapsable';
|
||||
|
||||
menu.setAttribute('data-category', category);
|
||||
menu.classList.toggle('collapsed', current_page !== category);
|
||||
|
||||
heading.innerHTML = category;
|
||||
menu.appendChild(heading);
|
||||
|
||||
menu.addEventListener('click', function() {
|
||||
if ( ! this.classList.contains('collapsed') )
|
||||
return;
|
||||
|
||||
var t = this,
|
||||
old_selection = container.querySelectorAll('.chat-menu-content:not(.collapsed)');
|
||||
for(var i=0; i < old_selection.length; i++)
|
||||
old_selection[i].classList.add('collapsed');
|
||||
|
||||
f._ffz_settings_page = t.getAttribute('data-category');
|
||||
t.classList.remove('collapsed');
|
||||
setTimeout(function(){t.scrollIntoViewIfNeeded()});
|
||||
});
|
||||
|
||||
cset.sort(function(a,b) {
|
||||
var a = a[1],
|
||||
b = b[1],
|
||||
|
||||
at = a.type === "boolean" ? 1 : 2,
|
||||
bt = b.type === "boolean" ? 1 : 2,
|
||||
|
||||
an = a.name.toLowerCase(),
|
||||
bn = b.name.toLowerCase();
|
||||
|
||||
if ( at < bt ) return -1;
|
||||
else if ( at > bt ) return 1;
|
||||
|
||||
else if ( an < bn ) return -1;
|
||||
else if ( an > bn ) return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
for(var i=0; i < cset.length; i++) {
|
||||
var key = cset[i][0],
|
||||
info = cset[i][1],
|
||||
el = document.createElement('p'),
|
||||
val = this.settings.get(key);
|
||||
|
||||
el.className = 'clearfix';
|
||||
|
||||
if ( this.has_bttv && info.no_bttv ) {
|
||||
var label = document.createElement('span'),
|
||||
help = document.createElement('span');
|
||||
label.className = 'switch-label';
|
||||
label.innerHTML = info.name;
|
||||
|
||||
help = document.createElement('span');
|
||||
help.className = 'help';
|
||||
help.innerHTML = 'Disabled due to incompatibility with BetterTTV.';
|
||||
|
||||
el.classList.add('disabled');
|
||||
el.appendChild(label);
|
||||
el.appendChild(help);
|
||||
|
||||
} else {
|
||||
if ( info.type == "boolean" ) {
|
||||
var swit = document.createElement('a'),
|
||||
label = document.createElement('span');
|
||||
|
||||
swit.className = 'switch';
|
||||
swit.classList.toggle('active', val);
|
||||
swit.innerHTML = "<span></span>";
|
||||
|
||||
label.className = 'switch-label';
|
||||
label.innerHTML = info.name;
|
||||
|
||||
el.appendChild(swit);
|
||||
el.appendChild(label);
|
||||
|
||||
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');
|
||||
link.innerHTML = info.name;
|
||||
link.href = "#";
|
||||
el.appendChild(link);
|
||||
|
||||
link.addEventListener("click", info.method.bind(this));
|
||||
}
|
||||
|
||||
if ( info.help ) {
|
||||
var help = document.createElement('span');
|
||||
help.className = 'help';
|
||||
help.innerHTML = info.help;
|
||||
el.appendChild(help);
|
||||
}
|
||||
}
|
||||
|
||||
menu.appendChild(el);
|
||||
}
|
||||
|
||||
container.appendChild(menu);
|
||||
}
|
||||
},
|
||||
|
||||
name: "Settings",
|
||||
icon: constants.GEAR,
|
||||
sort_order: 99999,
|
||||
wide: true
|
||||
wide: true,
|
||||
sub_menu: true
|
||||
};
|
||||
|
||||
|
||||
|
|
103
src/ui/dark.js
103
src/ui/dark.js
|
@ -6,6 +6,107 @@ var FFZ = window.FrankerFaceZ,
|
|||
// Settings
|
||||
// ---------------------
|
||||
|
||||
FFZ.basic_settings.dark_twitch = {
|
||||
type: "boolean",
|
||||
no_bttv: true,
|
||||
|
||||
category: "General",
|
||||
name: "Dark Twitch",
|
||||
help: "Apply a dark background to channels and other related pages for easier viewing.",
|
||||
|
||||
get: function() {
|
||||
return this.settings.dark_twitch;
|
||||
},
|
||||
|
||||
set: function(val) {
|
||||
this.settings.set('dark_twitch', val);
|
||||
this.settings.set('dark_no_blue', val);
|
||||
}
|
||||
};
|
||||
|
||||
FFZ.basic_settings.separated_chat = {
|
||||
type: "boolean",
|
||||
no_bttv: true,
|
||||
|
||||
category: "Chat",
|
||||
name: "Separated Lines",
|
||||
help: "Use alternating rows and thin lines to visually separate chat messages for easier reading.",
|
||||
|
||||
get: function() {
|
||||
return this.settings.chat_rows && this.settings.chat_separators !== '0';
|
||||
},
|
||||
|
||||
set: function(val) {
|
||||
this.settings.set('chat_rows', val);
|
||||
this.settings.set('chat_separators', val ? '2' : '0');
|
||||
}
|
||||
};
|
||||
|
||||
FFZ.basic_settings.minimalistic_chat = {
|
||||
type: "boolean",
|
||||
|
||||
category: "Chat",
|
||||
name: "Minimalistic UI",
|
||||
help: "Hide all of chat except messages and the input box and reduce chat margins.",
|
||||
|
||||
get: function() {
|
||||
return this.settings.minimal_chat && this.settings.chat_padding;
|
||||
},
|
||||
|
||||
set: function(val) {
|
||||
this.settings.set('minimal_chat', val);
|
||||
this.settings.set('chat_padding', val);
|
||||
}
|
||||
};
|
||||
|
||||
FFZ.basic_settings.high_contrast = {
|
||||
type: "boolean",
|
||||
|
||||
category: "Chat",
|
||||
no_bttv: true,
|
||||
|
||||
name: "High Contrast",
|
||||
help: "Display chat using white and black for maximum contrast. This is suitable for capturing and chroma keying chat to display on stream.",
|
||||
|
||||
get: function() {
|
||||
return this.settings.high_contrast_chat !== '222';
|
||||
},
|
||||
|
||||
set: function(val) {
|
||||
this.settings.set('high_contrast_chat', val ? '111': '222');
|
||||
}
|
||||
};
|
||||
|
||||
FFZ.basic_settings.keywords = {
|
||||
type: "button",
|
||||
|
||||
category: "Chat",
|
||||
no_bttv: true,
|
||||
|
||||
name: "Highlight Keywords",
|
||||
help: "Set additional keywords that will be highlighted in chat.",
|
||||
|
||||
method: function() {
|
||||
FFZ.settings_info.keywords.method.bind(this)();
|
||||
}
|
||||
};
|
||||
|
||||
FFZ.basic_settings.banned_words = {
|
||||
type: "button",
|
||||
|
||||
category: "Chat",
|
||||
no_bttv: true,
|
||||
|
||||
name: "Banned Keywords",
|
||||
help: "Set a list of words that will be removed from chat messages, locally.",
|
||||
|
||||
method: function() {
|
||||
FFZ.settings_info.banned_words.method.bind(this)();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
FFZ.settings_info.twitch_chat_dark = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
|
@ -28,7 +129,7 @@ FFZ.settings_info.dark_twitch = {
|
|||
var cb = document.querySelector('input.ffz-setting-dark-twitch');
|
||||
if ( cb )
|
||||
cb.checked = val;
|
||||
|
||||
|
||||
if ( this.has_bttv )
|
||||
return;
|
||||
|
||||
|
|
|
@ -1,19 +1,25 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require('../utils'),
|
||||
constants = require('../constants');
|
||||
constants = require('../constants'),
|
||||
|
||||
FOLLOW_GRAVITY = function(f, el) {
|
||||
return (f.settings.following_count && el.parentElement.getAttribute('data-name') === 'following' ? 'n' : '') + (f.settings.swap_sidebars ? 'e' : 'w');
|
||||
},
|
||||
|
||||
WIDE_TIP = function(f, el) {
|
||||
return ( ! f.settings.following_count || (el.id !== 'header_following' && el.parentElement.getAttribute('data-name') !== 'following') ) ? '' : 'ffz-wide-tip';
|
||||
};
|
||||
|
||||
|
||||
FFZ.settings_info.following_count = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
|
||||
no_bttv: true,
|
||||
no_mobile: true,
|
||||
|
||||
category: "Appearance",
|
||||
name: "Sidebar Following Count",
|
||||
help: "Display the number of live channels you're following on the sidebar.",
|
||||
name: "Sidebar Following Data",
|
||||
help: "Display the number of live channels you're following on the sidebar, and list the channels in a tooltip.",
|
||||
|
||||
on_update: function(val) {
|
||||
this._schedule_following_count();
|
||||
|
@ -21,10 +27,14 @@ FFZ.settings_info.following_count = {
|
|||
var Stream = window.App && App.__container__.resolve('model:stream'),
|
||||
Live = Stream && Stream.find("live");
|
||||
|
||||
if ( Live )
|
||||
this._draw_following_count(Live.get('total') || 0);
|
||||
else
|
||||
if ( Live ) {
|
||||
var total = Live.get('total') || 0;
|
||||
this._draw_following_count(total);
|
||||
this._draw_following_channels(Live.get('content'), total);;
|
||||
} else {
|
||||
this._update_following_count();
|
||||
this._draw_following_channels();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -37,6 +47,9 @@ FFZ.prototype.setup_following_count = function(has_ember) {
|
|||
if ( this.settings.following_count )
|
||||
this._schedule_following_count();
|
||||
|
||||
// Tooltips~!
|
||||
this._install_following_tooltips();
|
||||
|
||||
// If we don't have Ember, no point in trying this stuff.
|
||||
if ( ! has_ember )
|
||||
return this._update_following_count();
|
||||
|
@ -68,7 +81,7 @@ FFZ.prototype.setup_following_count = function(has_ember) {
|
|||
|
||||
|
||||
FFZ.prototype._schedule_following_count = function() {
|
||||
if ( this.has_bttv || ! this.settings.following_count ) {
|
||||
if ( ! this.settings.following_count ) {
|
||||
if ( this._following_count_timer ) {
|
||||
clearTimeout(this._following_count_timer);
|
||||
this._following_count_timer = undefined;
|
||||
|
@ -110,9 +123,21 @@ FFZ.prototype._update_following_count = function() {
|
|||
}
|
||||
|
||||
|
||||
FFZ.prototype._draw_following_channels = function(streams, total) {
|
||||
// First, build the data.
|
||||
var tooltip = 'Following';
|
||||
FFZ.prototype._build_following_tooltip = function(el) {
|
||||
if ( el.id !== 'header_following' && el.parentElement.getAttribute('data-name') !== 'following' )
|
||||
return el.getAttribute('original-title');
|
||||
|
||||
if ( ! this.settings.following_count )
|
||||
return 'Following';
|
||||
|
||||
var tooltip = (this.has_bttv ? '<span class="stat playing">FrankerFaceZ</span>' : '') + 'Following',
|
||||
bb = el.getBoundingClientRect(),
|
||||
height = document.body.clientHeight - (bb.bottom + 54),
|
||||
max_lines = Math.max(Math.floor(height / 36) - 1, 2),
|
||||
|
||||
streams = this._tooltip_streams,
|
||||
total = this._tooltip_total || (streams && streams.length) || 0;
|
||||
|
||||
|
||||
if ( streams && streams.length ) {
|
||||
var c = 0;
|
||||
|
@ -122,61 +147,93 @@ FFZ.prototype._draw_following_channels = function(streams, total) {
|
|||
continue;
|
||||
|
||||
c += 1;
|
||||
if ( c > 5 ) {
|
||||
var ttl = total || streams.length;
|
||||
tooltip += '<hr><span>And ' + utils.number_commas(ttl - 5) + ' more...</span>';
|
||||
if ( c > max_lines ) {
|
||||
tooltip += '<hr><span>And ' + utils.number_commas(total - max_lines) + ' more...</span>';
|
||||
break;
|
||||
}
|
||||
|
||||
tooltip += (i > 0 ? '<br>' : '<hr>') + '<span class="viewers">' + constants.LIVE + ' ' + utils.number_commas(stream.viewers) + '</span><b>' + utils.sanitize(stream.channel.display_name || stream.channel.name) + '</b><br><span class="playing">' + (stream.channel.game ? 'Playing ' + utils.sanitize(stream.channel.game) : 'Not Playing') + '</span>';
|
||||
}
|
||||
}
|
||||
var up_since = this.settings.stream_uptime && stream.created_at && utils.parse_date(stream.created_at),
|
||||
uptime = up_since && Math.floor((Date.now() - up_since.getTime()) / 1000) || 0,
|
||||
minutes = Math.floor(uptime / 60) % 60,
|
||||
hours = Math.floor(uptime / 3600);
|
||||
|
||||
tooltip += (i === 0 ? '<hr>' : '') +
|
||||
(uptime > 0 ? '<span class="stat">' + constants.CLOCK + ' ' + (hours > 0 ? hours + 'h' : '') + minutes + 'm</span>' : '') +
|
||||
'<span class="stat">' + constants.LIVE + ' ' + utils.number_commas(stream.viewers) + '</span>' +
|
||||
'<b>' + utils.sanitize(stream.channel.display_name || stream.channel.name) + '</b><br>' +
|
||||
'<span class="playing">' + (stream.channel.game ? 'Playing ' + utils.sanitize(stream.channel.game) : 'Not Playing') + '</span>';
|
||||
}
|
||||
} else
|
||||
tooltip += "<hr>No one you're following is online.";
|
||||
|
||||
|
||||
// Reposition the tooltip.
|
||||
setTimeout(function() {
|
||||
var tip = document.querySelector('.tipsy'),
|
||||
bb = tip.getBoundingClientRect(),
|
||||
|
||||
left = parseInt(tip.style.left || '0'),
|
||||
right = bb.left + tip.scrollWidth;
|
||||
|
||||
if ( bb.left < 5 )
|
||||
tip.style.left = (left - bb.left) + 5 + 'px';
|
||||
else if ( right > document.body.clientWidth - 5 )
|
||||
tip.style.left = (left - (5 + right - document.body.clientWidth)) + 'px';
|
||||
});
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._install_following_tooltips = function() {
|
||||
var f = this,
|
||||
data = {
|
||||
html: true,
|
||||
className: function() { return WIDE_TIP(f, this); },
|
||||
title: function() { return f._build_following_tooltip(this); }
|
||||
};
|
||||
|
||||
// Small
|
||||
var small_following = jQuery('#small_nav ul.game_filters li[data-name="following"] a');
|
||||
if ( small_following && small_following.length ) {
|
||||
var data = small_following.data('tipsy');
|
||||
if ( data && data.options ) {
|
||||
data.options.gravity = function() { return this.parentElement.getAttribute('data-name') === 'following' ? 'nw': 'w'; };
|
||||
data.options.html = true;
|
||||
data.options.className = 'ffz-wide-tip';
|
||||
var td = small_following.data('tipsy');
|
||||
if ( td && td.options ) {
|
||||
td.options = _.extend(td.options, data);
|
||||
td.options.gravity = function() { return FOLLOW_GRAVITY(f, this); };
|
||||
} else
|
||||
small_following.tipsy({html: true, className: 'ffz-wide-tip', gravity: 'nw'});
|
||||
|
||||
small_following.attr('title', tooltip);
|
||||
small_following.tipsy(_.extend({gravity: function() { return FOLLOW_GRAVITY(f, this); }}, data));
|
||||
}
|
||||
|
||||
|
||||
// Large
|
||||
var large_following = jQuery('#large_nav #nav_personal li[data-name="following"] a');
|
||||
if ( large_following && large_following.length ) {
|
||||
var data = large_following.data('tipsy');
|
||||
if ( data && data.options ) {
|
||||
data.options.html = true;
|
||||
data.options.className = 'ffz-wide-tip';
|
||||
} else
|
||||
large_following.tipsy({html:true, className: 'ffz-wide-tip'});
|
||||
|
||||
large_following.attr('title', tooltip);
|
||||
var td = large_following.data('tipsy');
|
||||
if ( td && td.options )
|
||||
td.options = _.extend(td.options, data);
|
||||
else
|
||||
large_following.tipsy(data);
|
||||
}
|
||||
|
||||
|
||||
// Heading
|
||||
var head_following = jQuery('#header_actions #header_following');
|
||||
if ( head_following && head_following.length ) {
|
||||
var data = head_following.data('tipsy');
|
||||
if ( data && data.options ) {
|
||||
data.options.html = true;
|
||||
data.options.className = 'ffz-wide-tip';
|
||||
} else
|
||||
head_following.tipsy({html: true, className: 'ffz-wide-tip'});
|
||||
|
||||
head_following.attr('title', tooltip);
|
||||
var td = head_following.data('tipsy');
|
||||
if ( td && td.options )
|
||||
td.options = _.extend(td.options, data);
|
||||
else
|
||||
head_following.tipsy(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._draw_following_channels = function(streams, total) {
|
||||
this._tooltip_streams = streams;
|
||||
this._tooltip_total = total;
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._draw_following_count = function(count) {
|
||||
// Small
|
||||
var small_following = document.querySelector('#small_nav ul.game_filters li[data-name="following"] a');
|
||||
|
|
|
@ -308,7 +308,7 @@ FFZ.prototype.build_ui_popup = function(view) {
|
|||
el = document.createElement('li'),
|
||||
link = document.createElement('a');
|
||||
|
||||
el.className = 'item';
|
||||
el.className = 'item' + (page.sub_menu ? ' has-sub-menu' : '');
|
||||
el.id = "ffz-menu-page-" + key;
|
||||
link.title = page.name;
|
||||
link.innerHTML = page.icon;
|
||||
|
|
|
@ -10,6 +10,25 @@ var FFZ = window.FrankerFaceZ,
|
|||
// Initialization
|
||||
// -------------------
|
||||
|
||||
FFZ.basic_settings.replace_twitch_menu = {
|
||||
type: "boolean",
|
||||
|
||||
category: "Chat",
|
||||
|
||||
name: "Unified Emoticons Menu",
|
||||
help: "Completely replace the default Twitch emoticon menu and display global emoticons in the My Emoticons menu.",
|
||||
|
||||
get: function() {
|
||||
return this.settings.replace_twitch_menu && this.settings.global_emotes_in_menu && this.settings.emoji_in_menu;
|
||||
},
|
||||
|
||||
set: function(val) {
|
||||
this.settings.set('replace_twitch_menu', val);
|
||||
this.settings.set('global_emotes_in_menu', val);
|
||||
this.settings.set('emoji_in_menu', val);
|
||||
}
|
||||
};
|
||||
|
||||
FFZ.settings_info.replace_twitch_menu = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
|
|
94
style.css
94
style.css
|
@ -476,6 +476,7 @@ body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + s
|
|||
margin: 11px 13px;
|
||||
}
|
||||
|
||||
.ffz-ui-sub-menu-page,
|
||||
.ffz-ui-menu-page { overflow-y: auto; }
|
||||
|
||||
.ffz-ui-menu-page[data-page="about"],
|
||||
|
@ -540,10 +541,15 @@ body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + s
|
|||
line-height: 25px;
|
||||
}
|
||||
|
||||
.ffz-ui-menu-page input,
|
||||
.ffz-ui-menu-page select {
|
||||
margin: 0 10px 5px;
|
||||
}
|
||||
|
||||
.ffz-ui-menu-page input[type="file"] {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#ffz-chat-menu { pointer-events: none; }
|
||||
|
||||
.ffz-ui-popup ul.menu {
|
||||
|
@ -570,15 +576,42 @@ body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + s
|
|||
background-color: #282828;
|
||||
}
|
||||
|
||||
.ffz-ui-popup ul.sub-menu li.title,
|
||||
.ffz-ui-menu-page .heading .right,
|
||||
.ffz-ui-popup ul.menu li.item {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.ffz-ui-popup ul.sub-menu li.item,
|
||||
.ffz-ui-popup ul.menu li.title {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.ffz-ui-popup ul.sub-menu { background-color: #dfdfdf; }
|
||||
|
||||
.app-main.theatre .ffz-ui-popup ul.sub-menu,
|
||||
.chat-container.dark .ffz-ui-popup ul.sub-menu,
|
||||
.chat-container.force-dark .ffz-ui-popup ul.sub-menu,
|
||||
.ember-chat-container.dark .ffz-ui-popup ul.sub-menu,
|
||||
.ember-chat-container.force-dark .ffz-ui-popup ul.sub-menu,
|
||||
.ffz-ui-popup.dark ul.sub-menu {
|
||||
background-color: #181818;
|
||||
}
|
||||
|
||||
.ffz-ui-popup ul.sub-menu a {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.app-main.theatre .ffz-ui-popup ul.sub-menu a,
|
||||
.chat-container.dark .ffz-ui-popup ul.sub-menu a,
|
||||
.chat-container.force-dark .ffz-ui-popup ul.sub-menu a,
|
||||
.ember-chat-container.dark .ffz-ui-popup ul.sub-menu a,
|
||||
.ember-chat-container.force-dark .ffz-ui-popup ul.sub-menu a,
|
||||
.ffz-ui-popup.dark ul.sub-menu a {
|
||||
color: #d3d3d3 !important;
|
||||
}
|
||||
|
||||
span.ffz-handle {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
@ -668,6 +701,11 @@ span.ffz-handle:after { left: 8px }
|
|||
border-top: 1px solid transparent;
|
||||
}
|
||||
|
||||
.ffz-ui-popup ul.sub-menu a {
|
||||
border-left: none;
|
||||
border-right: 1px solid rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.ffz-ui-popup ul.menu li.active {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
@ -676,6 +714,15 @@ span.ffz-handle:after { left: 8px }
|
|||
border-top-color: #fff;
|
||||
}
|
||||
|
||||
.ffz-ui-popup ul.menu li.active.has-sub-menu {
|
||||
background-color: #dfdfdf;
|
||||
}
|
||||
|
||||
.ffz-ui-popup ul.menu li.active.has-sub-menu a {
|
||||
border-top-color: #dfdfdf;
|
||||
}
|
||||
|
||||
|
||||
.chat-container.dark .chat-interface .ffz-ui-popup ul.menu li.active,
|
||||
.chat-container.force-dark .chat-interface .ffz-ui-popup ul.menu li.active,
|
||||
.ember-chat-container.dark .chat-interface .ffz-ui-popup ul.menu li.active,
|
||||
|
@ -694,6 +741,24 @@ span.ffz-handle:after { left: 8px }
|
|||
border-top-color: rgb(16,16,16);
|
||||
}
|
||||
|
||||
.chat-container.dark .chat-interface .ffz-ui-popup ul.menu li.active.has-sub-menu,
|
||||
.chat-container.force-dark .chat-interface .ffz-ui-popup ul.menu li.active.has-sub-menu,
|
||||
.ember-chat-container.dark .chat-interface .ffz-ui-popup ul.menu li.active.has-sub-menu,
|
||||
.ember-chat-container.force-dark .chat-interface .ffz-ui-popup ul.menu li.active.has-sub-menu,
|
||||
.app-main.theatre .chat-container .chat-interface .ffz-ui-popup ul.menu li.active.has-sub-menu,
|
||||
.ffz-ui-popup.dark ul.menu li.active.has-sub-menu {
|
||||
background-color: #181818;
|
||||
}
|
||||
|
||||
.chat-container.dark .chat-interface .ffz-ui-popup ul.menu li.active.has-sub-menu a,
|
||||
.chat-container.force-dark .chat-interface .ffz-ui-popup ul.menu li.active.has-sub-menu a,
|
||||
.ember-chat-container.dark .chat-interface .ffz-ui-popup ul.menu li.active.has-sub-menu a,
|
||||
.ember-chat-container.force-dark .chat-interface .ffz-ui-popup ul.menu li.active.has-sub-menu a,
|
||||
.app-main.theatre .chat-container .chat-interface .ffz-ui-popup ul.menu li.active.has-sub-menu a,
|
||||
.ffz-ui-popup.dark ul.menu li.active.has-sub-menu a {
|
||||
border-top-color: #181818;
|
||||
}
|
||||
|
||||
.chat-container.dark .chat-interface .ffz-ui-popup a,
|
||||
.chat-container.force-dark .chat-interface .ffz-ui-popup a,
|
||||
.ember-chat-container.dark .chat-interface .ffz-ui-popup a,
|
||||
|
@ -735,6 +800,7 @@ span.ffz-handle:after { left: 8px }
|
|||
.chat-history::-webkit-scrollbar,
|
||||
#ffz-race-popup .table::-webkit-scrollbar,
|
||||
.emoticon-selector-box .all-emotes::-webkit-scrollbar,
|
||||
.ffz-ui-sub-menu-page::-webkit-scrollbar,
|
||||
.ffz-ui-menu-page::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
@ -742,6 +808,7 @@ span.ffz-handle:after { left: 8px }
|
|||
.chat-history::-webkit-scrollbar-thumb,
|
||||
#ffz-race-popup .table::-webkit-scrollbar-thumb,
|
||||
.emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb,
|
||||
.ffz-ui-sub-menu-page::-webkit-scrollbar-thumb,
|
||||
.ffz-ui-menu-page::-webkit-scrollbar-thumb {
|
||||
border-radius: 7px;
|
||||
background: rgba(0,0,0,0.7);
|
||||
|
@ -755,7 +822,10 @@ span.ffz-handle:after { left: 8px }
|
|||
.app-main.theatre .emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb,
|
||||
.ember-chat-container.dark .ffz-ui-menu-page::-webkit-scrollbar-thumb,
|
||||
.chat-container.dark .ffz-ui-menu-page::-webkit-scrollbar-thumb,
|
||||
.app-main.theatre .ffz-ui-menu-page::-webkit-scrollbar-thumb {
|
||||
.app-main.theatre .ffz-ui-menu-page::-webkit-scrollbar-thumb,
|
||||
.ember-chat-container.dark .ffz-ui-sub-menu-page::-webkit-scrollbar-thumb,
|
||||
.chat-container.dark .ffz-ui-sub-menu-page::-webkit-scrollbar-thumb,
|
||||
.app-main.theatre .ffz-ui-sub-menu-page::-webkit-scrollbar-thumb {
|
||||
background: rgba(255,255,255,0.6);
|
||||
box-shadow: 0 0 1px 1px rgba(0,0,0,0.25);
|
||||
}
|
||||
|
@ -1080,21 +1150,35 @@ body:not(.ffz-chat-purge-icon) .ember-chat .mod-icons .purge { display: none; }
|
|||
/* Emoticon Tooltips */
|
||||
|
||||
.ffz-wide-tip .tipsy-inner {
|
||||
min-width: 300px;
|
||||
max-width: 600px;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ffz-wide-tip span.viewers {
|
||||
.ffz-wide-tip span.stat {
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.ffz-wide-tip span.viewers svg {
|
||||
.ffz-wide-tip b { margin-right: 20px; }
|
||||
|
||||
.ffz-wide-tip span.stat svg {
|
||||
float: left;
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
.ffz-wide-tip svg path { fill: #fff; }
|
||||
.ffz-wide-tip span.playing { opacity: 0.7; }
|
||||
.ffz-wide-tip span.playing {
|
||||
opacity: 0.7;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
position: relative;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.tipsy .tipsy-inner {
|
||||
white-space: pre-wrap;
|
||||
|
@ -1102,10 +1186,12 @@ body:not(.ffz-chat-purge-icon) .ember-chat .mod-icons .purge { display: none; }
|
|||
|
||||
/* Menu Page Loader */
|
||||
|
||||
.ffz-ui-sub-menu-page:empty,
|
||||
.ffz-ui-menu-page:empty {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ffz-ui-sub-menu-page:empty::after,
|
||||
.ffz-ui-menu-page:empty::after {
|
||||
content: " ";
|
||||
display: block;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue