mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-07-02 17:18:31 +00:00
584 lines
No EOL
14 KiB
JavaScript
584 lines
No EOL
14 KiB
JavaScript
var FFZ = window.FrankerFaceZ,
|
|
utils = require('../utils'),
|
|
|
|
build_css = function(emote) {
|
|
if ( ! emote.margins && ! emote.css )
|
|
return "";
|
|
|
|
return 'img[src="' + emote.urls[1] + '"]{' + (emote.margins ? 'margin:' + emote.margins + ';' : '') + (emote.css || "") + '}'
|
|
};
|
|
|
|
|
|
// ---------------------
|
|
// Badware Check
|
|
// ---------------------
|
|
|
|
FFZ.prototype.check_badware = function() {
|
|
if ( this.embed_in_dash || ! window.jQuery || ! window.jQuery.noty )
|
|
return;
|
|
|
|
// Check for the stolen version of BTTV4FFZ.
|
|
if ( FFZ.settings_info.bttv_global_emotes && FFZ.settings_info.bttv_global_emotes.category === "BetterTTV" ) {
|
|
var shown = localStorage.ffz_warning_bttv4ffz_clone;
|
|
if ( shown !== "true" ) {
|
|
localStorage.ffz_warning_bttv4ffz_clone = "true";
|
|
this.show_message("You appear to be using an unofficial version of BTTV4FFZ that was copied without the developer's permission. Please use the official version available at <a href=\"https://lordmau5.com/bttv4ffz/\">https://lordmau5.com/bttv4ffz/</a>");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ---------------------
|
|
// API Constructor
|
|
// ---------------------
|
|
|
|
var API = FFZ.API = function(instance, name, icon, version, name_key) {
|
|
this.ffz = instance || FFZ.get();
|
|
|
|
// Check for a known API!
|
|
if ( name ) {
|
|
for(var id in this.ffz._known_apis) {
|
|
if ( this.ffz._known_apis[id] === name ) {
|
|
this.id = id;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ! this.id ) {
|
|
var i = 0;
|
|
while( ! this.id ) {
|
|
if ( ! this.ffz._known_apis.hasOwnProperty(i) ) {
|
|
this.id = i;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if ( name ) {
|
|
this.ffz._known_apis[this.id] = name;
|
|
localStorage.ffz_known_apis = JSON.stringify(this.ffz._known_apis);
|
|
}
|
|
}
|
|
|
|
|
|
this.ffz._apis[this.id] = this;
|
|
|
|
this.emote_sets = {};
|
|
this.global_sets = [];
|
|
this.default_sets = [];
|
|
|
|
this.badges = {};
|
|
|
|
this.users = {};
|
|
this.chat_filters = [];
|
|
this.on_room_callbacks = [];
|
|
|
|
this.name = name || ("Extension#" + this.id);
|
|
this.name_key = name_key || this.name.replace(/[^A-Z0-9_\-]/g, '').toLowerCase();
|
|
|
|
this.icon = icon || null;
|
|
this.version = version || null;
|
|
|
|
this.ffz.log('Registered New Extension #' + this.id + ': ' + this.name);
|
|
};
|
|
|
|
|
|
FFZ.prototype.api = function(name, icon, version) {
|
|
// Load the known APIs list.
|
|
if ( ! this._known_apis ) {
|
|
this._known_apis = {};
|
|
if ( localStorage.hasOwnProperty('ffz_known_apis') )
|
|
try {
|
|
this._known_apis = JSON.parse(localStorage.ffz_known_apis);
|
|
} catch(err) {
|
|
this.log("Error loading Known APIs: " + err);
|
|
}
|
|
}
|
|
|
|
return new API(this, name, icon, version);
|
|
}
|
|
|
|
|
|
API.prototype.log = function(msg, data, to_json, log_json) {
|
|
this.ffz.log('Ext "' + this.name + '": ' + msg, data, to_json, log_json);
|
|
}
|
|
|
|
|
|
// ---------------------
|
|
// Set Loading
|
|
// ---------------------
|
|
|
|
API.prototype._load_set = function(real_id, set_id, data) {
|
|
if ( ! data )
|
|
return null;
|
|
|
|
// Check for an existing set to copy the users.
|
|
var users = [];
|
|
if ( this.emote_sets[real_id] && this.emote_sets[real_id].users )
|
|
users = this.emote_sets[real_id].users;
|
|
|
|
var emote_set = {
|
|
source: this.name,
|
|
source_ext: this.id,
|
|
source_id: set_id,
|
|
users: users,
|
|
count: 0,
|
|
emoticons: {},
|
|
_type: data._type || 0,
|
|
css: data.css || null,
|
|
description: data.description || null,
|
|
icon: data.icon || this.icon || null,
|
|
id: real_id,
|
|
title: data.title || "Global Emoticons",
|
|
};
|
|
|
|
this.emote_sets[real_id] = emote_set;
|
|
|
|
if ( this.ffz.emote_sets )
|
|
this.ffz.emote_sets[real_id] = emote_set;
|
|
|
|
var output_css = "",
|
|
ems = data.emoticons,
|
|
emoticons = emote_set.emoticons;
|
|
|
|
for(var i=0; i < ems.length; i++) {
|
|
var emote = ems[i],
|
|
new_emote = {urls: {}},
|
|
id = emote.id || (this.name + '-' + set_id + '-' + i);
|
|
|
|
if ( ! emote.name )
|
|
continue;
|
|
|
|
new_emote.id = id;
|
|
new_emote.set_id = real_id;
|
|
new_emote.name = emote.name;
|
|
|
|
new_emote.width = emote.width;
|
|
new_emote.height = emote.height;
|
|
|
|
new_emote.hidden = emote.hidden;
|
|
new_emote.owner = emote.owner;
|
|
|
|
new_emote.css = emote.css;
|
|
new_emote.margins = emote.margins;
|
|
|
|
new_emote.srcSet = emote.urls[1] + ' 1x';
|
|
new_emote.urls[1] = emote.urls[1];
|
|
|
|
if ( emote.urls[2] ) {
|
|
new_emote.urls[2] = emote.urls[2];
|
|
new_emote.srcSet += ', ' + emote.urls[2] + ' 2x';
|
|
}
|
|
if ( emote.urls[3] ) {
|
|
new_emote.urls[3] = emote.urls[3];
|
|
new_emote.srcSet += ', ' + emote.urls[3] + ' 3x';
|
|
}
|
|
if ( emote.urls[4] ) {
|
|
new_emote.urls[4] = emote.urls[4];
|
|
new_emote.srcSet += ', ' + emote.urls[4] + ' 4x';
|
|
}
|
|
|
|
if ( emote.regex )
|
|
new_emote.regex = emote.regex;
|
|
else if ( typeof emote.name !== "string" )
|
|
new_emote.regex = emote.name;
|
|
else if ( emote_set.require_spaces || emote.require_spaces )
|
|
new_emote.regex = new RegExp("(^| )(" + utils.escape_regex(emote.name) + ")(?= |$)", "g");
|
|
else
|
|
new_emote.regex = new RegExp("(^|\\W|\\b)(" + utils.escape_regex(emote.name) + ")(?=\\W|$)", "g");
|
|
|
|
new_emote.token = {
|
|
type: "emoticon",
|
|
srcSet: new_emote.srcSet,
|
|
imgSrc: new_emote.urls[1],
|
|
ffzEmote: id,
|
|
ffzEmoteSet: real_id,
|
|
altText: new_emote.hidden ? '???' : new_emote.name
|
|
};
|
|
|
|
output_css += build_css(new_emote);
|
|
emote_set.count++;
|
|
emoticons[id] = new_emote;
|
|
}
|
|
|
|
utils.update_css(this.ffz._emote_style, real_id, output_css + (emote_set.css || ""));
|
|
|
|
if ( this.ffz._cindex )
|
|
this.ffz._cindex.ffzFixTitle();
|
|
|
|
try {
|
|
this.ffz.update_ui_link();
|
|
} catch(err) { }
|
|
|
|
return emote_set;
|
|
}
|
|
|
|
|
|
// -------------------------
|
|
// Loading / Unloading Sets
|
|
// -------------------------
|
|
|
|
API.prototype.load_set = function(id, emote_set) {
|
|
var exact_id = this.id + '-' + id;
|
|
|
|
emote_set.title = emote_set.title || "Global Emoticons";
|
|
emote_set._type = emote_set._type || 0;
|
|
|
|
emote_set = this._load_set(exact_id, id, emote_set);
|
|
this.log("Loaded Emoticon Set #" + id + ": " + emote_set.title + " (" + emote_set.count + " emotes)", emote_set);
|
|
return emote_set;
|
|
}
|
|
|
|
|
|
API.prototype.unload_set = function(id) {
|
|
var exact_id = this.id + '-' + id,
|
|
emote_set = this.emote_sets[exact_id];
|
|
|
|
if ( ! emote_set )
|
|
return;
|
|
|
|
// First, let's unregister it as a global.
|
|
this.unregister_global_set(id);
|
|
|
|
|
|
// Now, remove the set data.
|
|
utils.update_css(this.ffz._emote_style, exact_id, null);
|
|
|
|
this.emote_sets[exact_id] = undefined;
|
|
if ( this.ffz.emote_sets )
|
|
this.ffz.emote_sets[exact_id] = undefined;
|
|
|
|
|
|
// Remove from all its Rooms
|
|
if ( emote_set.users ) {
|
|
for(var i=0; i < emote_set.users.length; i++) {
|
|
var room_id = emote_set.users[i],
|
|
room = this.ffz.rooms && this.ffz.rooms[room_id];
|
|
|
|
if ( ! room )
|
|
continue;
|
|
|
|
var ind = room.ext_sets ? room.ext_sets.indexOf(exact_id) : -1;
|
|
if ( ind !== -1 )
|
|
room.ext_sets.splice(ind,1);
|
|
}
|
|
|
|
emote_set.users = [];
|
|
}
|
|
|
|
|
|
return emote_set;
|
|
}
|
|
|
|
|
|
API.prototype.get_set = function(id) {
|
|
var exact_id = this.id + '-' + id;
|
|
return this.emote_sets[exact_id];
|
|
}
|
|
|
|
|
|
// ---------------------
|
|
// Global Emote Sets
|
|
// ---------------------
|
|
|
|
API.prototype.register_global_set = function(id, emote_set) {
|
|
var exact_id = this.id + '-' + id;
|
|
|
|
if ( emote_set ) {
|
|
// If a set was provided, load it.
|
|
emote_set = this.load_set(id, emote_set);
|
|
} else
|
|
emote_set = this.emote_sets[exact_id];
|
|
|
|
if ( ! emote_set )
|
|
throw new Error("Invalid set ID");
|
|
|
|
|
|
// Make sure the set is still available with FFZ.
|
|
if ( ! this.ffz.emote_sets[exact_id] )
|
|
this.ffz.emote_sets[exact_id] = emote_set;
|
|
|
|
|
|
// It's a valid set if we get here, so make it global.
|
|
if ( this.global_sets.indexOf(exact_id) === -1 )
|
|
this.global_sets.push(exact_id);
|
|
|
|
if ( this.default_sets.indexOf(exact_id) === -1 )
|
|
this.default_sets.push(exact_id);
|
|
|
|
if ( this.ffz.global_sets && this.ffz.global_sets.indexOf(exact_id) === -1 )
|
|
this.ffz.global_sets.push(exact_id);
|
|
|
|
if ( this.ffz.default_sets && this.ffz.default_sets.indexOf(exact_id) === -1 )
|
|
this.ffz.default_sets.push(exact_id);
|
|
|
|
// Update tab completion.
|
|
if ( this.ffz._inputv )
|
|
Ember.propertyDidChange(this.ffz._inputv, 'ffz_emoticons');
|
|
};
|
|
|
|
|
|
API.prototype.unregister_global_set = function(id) {
|
|
var exact_id = this.id + '-' + id,
|
|
emote_set = this.emote_sets[exact_id];
|
|
|
|
if ( ! emote_set )
|
|
return;
|
|
|
|
// Remove the set from global sets.
|
|
var ind = this.global_sets.indexOf(exact_id);
|
|
if ( ind !== -1 )
|
|
this.global_sets.splice(ind,1);
|
|
|
|
ind = this.default_sets.indexOf(exact_id);
|
|
if ( ind !== -1 )
|
|
this.default_sets.splice(ind,1);
|
|
|
|
ind = this.ffz.global_sets ? this.ffz.global_sets.indexOf(exact_id) : -1;
|
|
if ( ind !== -1 )
|
|
this.ffz.global_sets.splice(ind,1);
|
|
|
|
ind = this.ffz.default_sets ? this.ffz.default_sets.indexOf(exact_id) : -1;
|
|
if ( ind !== -1 )
|
|
this.ffz.default_sets.splice(ind,1);
|
|
|
|
// Update tab completion.
|
|
if ( this.ffz._inputv )
|
|
Ember.propertyDidChange(this.ffz._inputv, 'ffz_emoticons');
|
|
};
|
|
|
|
|
|
// -----------------------
|
|
// Per-Channel Emote Sets
|
|
// -----------------------
|
|
|
|
API.prototype.register_room_set = function(room_id, id, emote_set) {
|
|
var exact_id = this.id + '-' + id,
|
|
room = this.ffz.rooms && this.ffz.rooms[room_id];
|
|
|
|
if ( ! room )
|
|
throw new Error("Room not loaded");
|
|
|
|
if ( emote_set ) {
|
|
// If a set was provided, load it.
|
|
emote_set.title = emote_set.title || "Channel: " + (room.display_name || room_id);
|
|
emote_set._type = emote_set._type || 1;
|
|
|
|
emote_set = this.load_set(id, emote_set);
|
|
} else
|
|
emote_set = this.emote_sets[exact_id];
|
|
|
|
if ( ! emote_set )
|
|
throw new Error("Invalid set ID");
|
|
|
|
// Make sure the set is still available with FFZ.
|
|
if ( ! this.ffz.emote_sets[exact_id] )
|
|
this.ffz.emote_sets[exact_id] = emote_set;
|
|
|
|
// Register it on the room.
|
|
room.ext_sets && room.ext_sets.push(exact_id);
|
|
emote_set.users.push(room_id);
|
|
|
|
// Update tab completion.
|
|
if ( this.ffz._inputv )
|
|
Ember.propertyDidChange(this.ffz._inputv, 'ffz_emoticons');
|
|
}
|
|
|
|
|
|
API.prototype.unregister_room_set = function(room_id, id) {
|
|
var exact_id = this.id + '-' + id,
|
|
emote_set = this.emote_sets[exact_id],
|
|
room = this.ffz.rooms && this.ffz.rooms[room_id];
|
|
|
|
if ( ! emote_set || ! room )
|
|
return;
|
|
|
|
var ind = room.ext_sets ? room.ext_sets.indexOf(exact_id) : -1;
|
|
if ( ind !== -1 )
|
|
room.ext_sets.splice(ind,1);
|
|
|
|
ind = emote_set.users.indexOf(room_id);
|
|
if ( ind !== -1 )
|
|
emote_set.users.splice(ind,1);
|
|
|
|
// Update tab completion.
|
|
if ( this.ffz._inputv )
|
|
Ember.propertyDidChange(this.ffz._inputv, 'ffz_emoticons');
|
|
}
|
|
|
|
|
|
// -----------------------
|
|
// Badge APIs
|
|
// -----------------------
|
|
|
|
API.prototype.add_badge = function(badge_id, badge) {
|
|
var exact_id = this.id + '-' + badge_id,
|
|
|
|
real_badge = {
|
|
id: exact_id,
|
|
source_ext: this.id,
|
|
source_id: badge_id,
|
|
alpha_image: badge.alpha_image,
|
|
color: badge.color || "transparent",
|
|
no_invert: badge.no_invert,
|
|
invert_invert: badge.invert_invert,
|
|
css: badge.css,
|
|
image: badge.image,
|
|
name: badge.name,
|
|
title: badge.title,
|
|
slot: badge.slot,
|
|
visible: badge.visible,
|
|
replaces: badge.replaces,
|
|
replaces_type: badge.replaces_type
|
|
};
|
|
|
|
this.ffz.badges[exact_id] = this.badges[badge_id] = real_badge;
|
|
utils.update_css(this.ffz._badge_style, exact_id, utils.badge_css(real_badge));
|
|
}
|
|
|
|
|
|
API.prototype.remove_badge = function(badge_id) {
|
|
var exact_id = this.id + '-' + badge_id;
|
|
this.ffz.badges[exact_id] = this.badges[badge_id] = undefined;
|
|
utils.update_css(this.ffz._badge_style, exact_id);
|
|
}
|
|
|
|
|
|
// -----------------------
|
|
// User Modifications
|
|
// -----------------------
|
|
|
|
API.prototype.user_add_badge = function(username, slot, badge_id) {
|
|
var user = this.users[username] = this.users[username] || {},
|
|
ffz_user = this.ffz.users[username] = this.ffz.users[username] || {},
|
|
|
|
badges = user.badges = user.badges || {},
|
|
ffz_badges = ffz_user.badges = ffz_user.badges || {},
|
|
|
|
exact_id = this.id + '-' + badge_id,
|
|
badge = {id: exact_id};
|
|
|
|
badges[slot] = ffz_badges[slot] = badge;
|
|
}
|
|
|
|
|
|
API.prototype.user_remove_badge = function(username, slot) {
|
|
var user = this.users[username] = this.users[username] || {},
|
|
ffz_user = this.ffz.users[username] = this.ffz.users[username] || {},
|
|
|
|
badges = user.badges = user.badges || {},
|
|
ffz_badges = ffz_user.badges = ffz_user.badges || {};
|
|
|
|
badges[slot] = ffz_badges[slot] = null;
|
|
}
|
|
|
|
|
|
API.prototype.user_add_set = function(username, set_id) {
|
|
var user = this.users[username] = this.users[username] || {},
|
|
ffz_user = this.ffz.users[username] = this.ffz.users[username] || {},
|
|
|
|
emote_sets = user.sets = user.sets || [],
|
|
ffz_sets = ffz_user.sets = ffz_user.sets || [],
|
|
|
|
exact_id = this.id + '-' + set_id;
|
|
|
|
if ( emote_sets.indexOf(exact_id) === -1 )
|
|
emote_sets.push(exact_id);
|
|
|
|
if ( ffz_sets.indexOf(exact_id) === -1 )
|
|
ffz_sets.push(exact_id);
|
|
|
|
// Update tab completion.
|
|
var user = this.ffz.get_user();
|
|
if ( this.ffz._inputv && user && user.login === username )
|
|
Ember.propertyDidChange(this.ffz._inputv, 'ffz_emoticons');
|
|
}
|
|
|
|
|
|
API.prototype.user_remove_set = function(username, set_id) {
|
|
var user = this.users[username],
|
|
ffz_user = this.ffz.users[username],
|
|
|
|
emote_sets = user && user.sets,
|
|
ffz_sets = ffz_user && ffz_user.sets,
|
|
|
|
exact_id = this.id + '-' + set_id;
|
|
|
|
var ind = emote_sets ? emote_sets.indexOf(exact_id) : -1;
|
|
if ( ind !== -1 )
|
|
emote_sets.splice(ind, 1);
|
|
|
|
ind = ffz_sets ? ffz_sets.indexOf(exact_id) : -1;
|
|
if ( ind !== -1 )
|
|
ffz_sets.splice(ind, 1);
|
|
|
|
// Update tab completion.
|
|
var user = this.ffz.get_user();
|
|
if ( this.ffz._inputv && user && user.login === username )
|
|
Ember.propertyDidChange(this.ffz._inputv, 'ffz_emoticons');
|
|
}
|
|
|
|
|
|
// -----------------------
|
|
// Chat Callback
|
|
// -----------------------
|
|
|
|
API.prototype.register_chat_filter = function(filter) {
|
|
this.chat_filters.push(filter);
|
|
this.ffz._chat_filters.push(filter);
|
|
}
|
|
|
|
API.prototype.unregister_chat_filter = function(filter) {
|
|
var ind = this.chat_filters.indexOf(filter);
|
|
if ( ind !== -1 )
|
|
this.chat_filters.splice(ind, 1);
|
|
|
|
ind = this.ffz._chat_filters.indexOf(filter);
|
|
if ( ind !== -1 )
|
|
this.ffz._chat_filters.splice(ind, 1);
|
|
}
|
|
|
|
// -----------------------
|
|
// Channel Callbacks
|
|
// -----------------------
|
|
|
|
API.prototype._room_callbacks = function(room_id, room, specific_func) {
|
|
var callback = this.register_room_set.bind(this, room_id);
|
|
|
|
if ( specific_func ) {
|
|
try {
|
|
specific_func(room_id, callback);
|
|
} catch(err) {
|
|
this.log("Error in On-Room Callback: " + err);
|
|
}
|
|
|
|
} else {
|
|
for(var i=0; i < this.on_room_callbacks.length; i++) {
|
|
var cb = this.on_room_callbacks[i];
|
|
try {
|
|
cb(room_id, callback);
|
|
} catch(err) {
|
|
this.log("Error in On-Room Callback: " + err);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
API.prototype.register_on_room_callback = function(callback, dont_iterate) {
|
|
this.on_room_callbacks.push(callback);
|
|
|
|
// Call this for all current rooms.
|
|
if ( ! dont_iterate && this.ffz.rooms ) {
|
|
for(var room_id in this.ffz.rooms)
|
|
this._room_callbacks(room_id, this.ffz.rooms[room_id], callback);
|
|
}
|
|
}
|
|
|
|
|
|
API.prototype.unregister_on_room_callback = function(callback) {
|
|
var ind = this.on_room_callbacks.indexOf(callback);
|
|
if ( ind !== -1 )
|
|
this.on_room_callbacks.splice(ind, 1);
|
|
} |