1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-07-02 17:18:31 +00:00
FrankerFaceZ/src/ext/api.js

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);
}