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;
|
||||
});
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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 )
|
||||
|
@ -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 : '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 || "");
|
||||
}
|
||||
|
|
440
src/settings.js
440
src/settings.js
|
@ -1,5 +1,6 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
constants = require("./constants");
|
||||
constants = require("./constants"),
|
||||
FileSaver = require("./FileSaver");
|
||||
|
||||
|
||||
make_ls = function(key) {
|
||||
|
@ -14,6 +15,25 @@ var FFZ = window.FrankerFaceZ,
|
|||
|
||||
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,12 +85,421 @@ 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) {
|
||||
// Bottom Bar
|
||||
var menu = document.createElement('ul'),
|
||||
page = document.createElement('div'),
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_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;
|
||||
|
||||
return 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);
|
||||
|
||||
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_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 {
|
||||
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);
|
||||
}
|
||||
},
|
||||
|
||||
render_advanced: function(view, container) {
|
||||
var settings = {},
|
||||
categories = [],
|
||||
is_android = navigator.userAgent.indexOf('Android') !== -1;
|
||||
|
@ -254,7 +687,8 @@ FFZ.menu_pages.settings = {
|
|||
name: "Settings",
|
||||
icon: constants.GEAR,
|
||||
sort_order: 99999,
|
||||
wide: true
|
||||
wide: true,
|
||||
sub_menu: true
|
||||
};
|
||||
|
||||
|
||||
|
|
101
src/ui/dark.js
101
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,
|
||||
|
|
|
@ -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