diff --git a/background.js b/background.js
new file mode 100644
index 0000000..8a22f49
--- /dev/null
+++ b/background.js
@@ -0,0 +1,612 @@
+chrome.alarms.create("refresh", {
+ "delayInMinutes": 0.1,
+ "periodInMinutes": 0.1
+});
+chrome.alarms.onAlarm.addListener(alarm => {
+ refreshToken(true);
+})
+
+chrome.runtime.onInstalled.addListener(() => {
+ onStart();
+});
+
+chrome.runtime.onStartup.addListener(() => {
+ onStart();
+});
+
+function onStart() {
+ initValues();
+
+ chrome.storage.local.set({"lastFollowedChannelsRefresh": 1});
+ chrome.storage.local.set({"lastTTVTokenRefresh": 1});
+ chrome.storage.local.set({"lastStreamsRefresh": 0});
+ chrome.storage.local.set({"callUserInfos": true});
+ chrome.storage.local.set({"totalRefreshCount": 0}, () => {
+ chrome.storage.sync.set({"alreadyNotifiedStreams": []}, () => {
+ chrome.storage.sync.set({"alreadyNotifiedCategoryChanges": []}, () => {
+ forceRefresh();
+ });
+ });
+ });
+}
+
+function initValues() {
+ chrome.storage.sync.get("viewercount-order", value => {
+ if (value["viewercount-order"] == null)
+ chrome.storage.sync.set({"viewercount-order": "descendant"});
+ });
+ chrome.storage.sync.get("streams-layout", value => {
+ if (value["streams-layout"] == null)
+ chrome.storage.sync.set({"streams-layout": "regular"});
+ });
+ chrome.storage.sync.get("notified-streams", value => {
+ if (value["notified-streams"] == null)
+ chrome.storage.sync.set({"notified-streams": []});
+ });
+ chrome.storage.sync.get("notify-all-streams", value => {
+ if (value["notify-all-streams"] == null)
+ chrome.storage.sync.set({"notify-all-streams": true});
+ });
+ chrome.storage.sync.get("enabled-notifications", value => {
+ if (value["enabled-notifications"] == null)
+ chrome.storage.sync.set({"enabled-notifications": []});
+ });
+}
+
+function refreshToken(refreshAll) {
+ /**
+ * We validate the ttv token every 1h
+ * This also allows to get some values from Twitch
+ */
+ chrome.storage.sync.get("ttvToken", ttvToken_result => {
+ var ttvToken = ttvToken_result.ttvToken;
+ if (ttvToken != null && ttvToken != "failed" && ttvToken != "none") {
+ chrome.storage.local.get("lastTTVTokenRefresh", lastTTVTokenRefresh_result => {
+ if (lastTTVTokenRefresh_result.lastTTVTokenRefresh < (Date.now() - 3600000)) {
+ validateTTVToken(ttvToken, data => {
+ if (data == null) {
+ hardDisconnect();
+ } else {
+ chrome.storage.local.remove("disconnected");
+ let ttvUser = {
+ "client_id": data.client_id,
+ "expires_in": data.expires_in,
+ "login": data.login,
+ "user_id": data.user_id
+ };
+ chrome.storage.local.set({"ttvUser": ttvUser});
+ if (refreshAll)
+ refresh(ttvToken, ttvUser);
+ }
+ });
+ } else if (refreshAll) {
+ chrome.storage.local.get("ttvUser", ttvUser_result => {
+ refresh(ttvToken, ttvUser_result.ttvUser);
+ });
+ }
+ });
+ }
+ });
+}
+
+
+function getAllTwitchCategories(ttvToken, ttvUserId, clientId, callback, page) {
+ fetch("https://api.twitch.tv/helix/games/top?first=100" + (page != null ? "&after=" + page : ""), {
+ method: "GET",
+ headers: {
+ "Authorization": "Bearer " + ttvToken,
+ "Client-Id": clientId
+ }
+ }).then(response => {
+ response.json().then(data => {
+ if (data.pagination != null && Object.keys(data.pagination).length !== 0) {
+ let totalData = [];
+ Array.prototype.push.apply(totalData, data.data);
+ getAllTwitchCategories(ttvToken, ttvUserId, clientId, data => {
+ Array.prototype.push.apply(totalData, data);
+ callback(totalData);
+ }, data.pagination.cursor);
+ } else {
+ callback(data.data);
+ }
+ })
+ }, reason => {
+ // TODO: handle this case
+ });
+}
+
+function refresh(ttvToken, ttvUser) {
+ chrome.storage.local.get("callUserInfos", callUserInfos_result => {
+ if (callUserInfos_result.callUserInfos) {
+ chrome.storage.local.set({"callUserInfos": false});
+ getUserInfos(ttvToken, ttvUser.client_id, data => {
+ chrome.storage.local.set({"ttvUser_data": data.data[0]});
+ });
+ } else {
+ chrome.storage.local.set({"callUserInfos": true});
+ }
+ });
+
+ getAllTwitchCategories(
+ ttvToken, ttvUser.user_id, ttvUser.client_id, data => {
+ var twitchCategoriesMap = data.map(elm => elm.name);
+ chrome.storage.local.set({"allTwitchCategories": (twitchCategoriesMap)}, () => {});
+ },
+ null);
+
+ getLiveFollowedStreams(ttvToken, ttvUser.user_id, ttvUser.client_id, data => {
+ chrome.storage.local.set({"lastStreamsRefresh": Date.now()});
+ chrome.storage.local.set({"ttvStreams_data": data.data});
+
+ var allStreams = [];
+ var allCategoryChanges = [];
+ data.data.forEach(el => {
+ allStreams.push(el["user_login"]);
+ allCategoryChanges.push({
+ game_name: el["game_name"],
+ user_login: el["user_login"],
+ });
+ })
+
+
+ chrome.storage.sync.get("notified-streams", notified_streams_result => {
+ var notified_streams = notified_streams_result["notified-streams"];
+ chrome.storage.sync.get("notify-all-streams", notify_result => {
+ var notify_streams = notify_result["notify-all-streams"];
+ chrome.storage.local.get("totalRefreshCount", (totalRefreshCount_result) => {
+ if (totalRefreshCount_result.totalRefreshCount != 0) {
+ chrome.storage.sync.get("alreadyNotifiedStreams", (alreadyNotifiedStreams_result) => {
+ let alreadyNotifiedStreams = alreadyNotifiedStreams_result.alreadyNotifiedStreams;
+
+ //alreadyNotifiedStreams.pop();
+
+ let notifiedStreams = [];
+ let newStreams = allStreams;
+ let stoppedStreams = [];
+ if (alreadyNotifiedStreams != null) {
+ newStreams = allStreams.filter(x => !alreadyNotifiedStreams.includes(x))
+ stoppedStreams = alreadyNotifiedStreams.filter(x => !allStreams.includes(x))
+ notifiedStreams = allStreams.filter(x => !stoppedStreams.includes(x))
+ }
+
+ // If "enable all notifications" checkbox isnt checked, we filter out streams
+ if (!notify_streams) {
+ newStreams = newStreams.filter(x => notified_streams.includes(x))
+ }
+
+ if (newStreams.length > 0) {
+ newNotification(newStreams);
+ }
+ chrome.storage.sync.set({"alreadyNotifiedStreams": notifiedStreams});
+ });
+
+ chrome.storage.sync.get("alreadyNotifiedCategoryChanges", (alreadyNotifiedCategoryChanges_result) => {
+ let alreadyNotifiedCategoryChanges = alreadyNotifiedCategoryChanges_result.alreadyNotifiedCategoryChanges;
+
+ //alreadyNotifiedCategoryChanges.pop();
+
+
+ let notifiedCategoryChanges = [];
+ let newCategoryChanges = [];
+ let stoppedCategoryChanges = [];
+ if (alreadyNotifiedCategoryChanges != null) {
+
+ for (const element of allCategoryChanges) {
+ var current_user_login = (element.user_login);
+ var current_game_name = (element.game_name);
+
+ if (!(alreadyNotifiedCategoryChanges.filter(e => e.user_login === current_user_login).length > 0)) {
+ //streamer is not in alreadyNotifiedCategoryChanges
+ //means a new streamer went online
+
+ newCategoryChanges.push({
+ game_name: current_game_name,
+ user_login: current_user_login,
+ user_just_went_online: true,
+ });
+ } else {
+ //streamer is in alreadyNotifiedCategoryChanges (already notified)
+ //means check if game changed
+ for (const el of alreadyNotifiedCategoryChanges) {
+ if (el.user_login === current_user_login) {
+ if (el.game_name !== current_game_name) {
+ newCategoryChanges.push({
+ game_name: current_game_name,
+ old_game_name: el.game_name,
+ user_login: current_user_login,
+ });
+ }
+ }
+ }
+ }
+ }
+
+
+
+ for (const ell of alreadyNotifiedCategoryChanges) {
+ if (!(allCategoryChanges.filter(e => e.user_login === ell.user_login).length > 0)) {
+ //one stream went offline
+ stoppedCategoryChanges.push({
+ game_name: ell.game_name,
+ user_login: ell.user_login,
+ });
+ }
+ }
+
+
+
+ for (const elll of allCategoryChanges) {
+ if (!(stoppedCategoryChanges.filter(e => e.user_login === elll.user_login).length > 0)) {
+ notifiedCategoryChanges.push({
+ game_name: elll.game_name,
+ user_login: elll.user_login,
+ });
+ }
+ }
+
+ }
+
+ if (newCategoryChanges.length > 0) {
+ newCategoryNotification(newCategoryChanges);
+ }
+ chrome.storage.sync.set({"alreadyNotifiedCategoryChanges": notifiedCategoryChanges});
+ });
+ } else {
+ chrome.storage.sync.set({"alreadyNotifiedStreams": allStreams});
+ chrome.storage.sync.set({"alreadyNotifiedCategoryChanges": allCategoryChanges});
+ }
+ chrome.storage.local.set({"totalRefreshCount": totalRefreshCount_result.totalRefreshCount + 1});
+ });
+ });
+ });
+ });
+}
+
+
+
+async function currentTabHandlesCategoryChangeNotification(new_category_changes) {
+ /**
+ * (c) David Konrad 2018-
+ * MIT License
+ *
+ * Javascript function to convert plain text to unicode variants
+ *
+ * Loosely based on the nodejs monotext CLI utility https://github.com/cpsdqs/monotext
+ * (c) cpsdqs 2016
+ *
+ * For more inspiration see http://unicode.org/charts/
+ *
+ */
+
+ /*
+ * supported unicode variants
+ *
+ * m: monospace
+ * b: bold
+ * i: italic
+ * c: script (Mathematical Alphanumeric Symbols)
+ * g: gothic / fraktur
+ * d: double-struck
+ * s: sans-serif
+ * o: circled text
+ * p: parenthesized latin letters
+ * q: squared text
+ * w: fullwidth
+ */
+
+ function toUnicodeVariant(str, variant, flags) {
+ str = str.toUpperCase();
+
+ const offsets = {
+ m: [0x1d670, 0x1d7f6],
+ b: [0x1d400, 0x1d7ce],
+ i: [0x1d434, 0x00030],
+ bi: [0x1d468, 0x00030],
+ c: [0x0001d49c, 0x00030],
+ bc: [0x1d4d0, 0x00030],
+ g: [0x1d504, 0x00030],
+ d: [0x1d538, 0x1d7d8],
+ bg: [0x1d56c, 0x00030],
+ s: [0x1d5a0, 0x1d7e2],
+ bs: [0x1d5d4, 0x1d7ec],
+ is: [0x1d608, 0x00030],
+ bis: [0x1d63c, 0x00030],
+ o: [0x24B6, 0x2460],
+ on: [0x0001f150, 0x2460],
+ p: [0x249c, 0x2474],
+ q: [0x1f130, 0x00030],
+ qn: [0x0001F170, 0x00030],
+ w: [0xff21, 0xff10],
+ u: [0x2090, 0xff10]
+ }
+
+ const variantOffsets = {
+ 'monospace': 'm',
+ 'bold' : 'b',
+ 'italic' : 'i',
+ 'bold italic' : 'bi',
+ 'script': 'c',
+ 'bold script': 'bc',
+ 'gothic': 'g',
+ 'gothic bold': 'bg',
+ 'doublestruck': 'd',
+ 'sans': 's',
+ 'bold sans' : 'bs',
+ 'italic sans': 'is',
+ 'bold italic sans': 'bis',
+ 'parenthesis': 'p',
+ 'circled': 'o',
+ 'circled negative': 'on',
+ 'squared': 'q',
+ 'squared negative': 'qn',
+ 'fullwidth': 'w'
+ }
+
+ // special characters (absolute values)
+ const special = {
+ m: {
+ ' ': 0x2000,
+ '-': 0x2013
+ },
+ i: {
+ 'h': 0x210e
+ },
+ g: {
+ 'C': 0x212d,
+ 'H': 0x210c,
+ 'I': 0x2111,
+ 'R': 0x211c,
+ 'Z': 0x2128
+ },
+ d: {
+ 'C': 0x2102,
+ 'H': 0x210D,
+ 'N': 0x2115,
+ 'P': 0x2119,
+ 'Q': 0x211A,
+ 'R': 0x211D,
+ 'Z': 0x2124
+ },
+ o: {
+ '0': 0x24EA,
+ '1': 0x2460,
+ '2': 0x2461,
+ '3': 0x2462,
+ '4': 0x2463,
+ '5': 0x2464,
+ '6': 0x2465,
+ '7': 0x2466,
+ '8': 0x2467,
+ '9': 0x2468,
+ },
+ on: {},
+ p: {},
+ q: {},
+ qn: {},
+ w: {}
+ }
+ //support for parenthesized latin letters small cases
+ //support for full width latin letters small cases
+ //support for circled negative letters small cases
+ //support for squared letters small cases
+ //support for squared letters negative small cases
+ ;['p', 'w', 'on', 'q', 'qn'].forEach(t => {
+ for (var i = 97; i <= 122; i++) {
+ special[t][String.fromCharCode(i)] = offsets[t][0] + (i-97)
+ }
+ })
+
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
+ const numbers = '0123456789'
+
+ const getType = function(variant) {
+ if (variantOffsets[variant]) return variantOffsets[variant]
+ if (offsets[variant]) return variant
+ return 'm' //monospace as default
+ }
+ const getFlag = function(flag, flags) {
+ if (!flags) return false
+ return flag.split('|').some(f => flags.split(',').indexOf(f) > -1)
+ }
+
+ const type = getType(variant)
+ const underline = getFlag('underline|u', flags)
+ const strike = getFlag('strike|s', flags)
+ let result = ''
+
+ for (let c of str) {
+ let index
+ if (special[type] && special[type][c]) c = String.fromCodePoint(special[type][c])
+ if (type && (index = chars.indexOf(c)) > -1) {
+ result += String.fromCodePoint(index + offsets[type][0])
+ } else if (type && (index = numbers.indexOf(c)) > -1) {
+ result += String.fromCodePoint(index + offsets[type][1])
+ } else {
+ result += c
+ }
+ if (underline) result += '\u0332' // add combining underline
+ if (strike) result += '\u0336' // add combining strike
+ }
+ return result
+ }
+
+
+ if (typeof module === 'object' && module && typeof module.exports === 'object') {
+ module.exports = toUnicodeVariant
+ }
+
+
+
+
+ var msg = "";
+
+ chrome.storage.sync.get("enabled-notifications", enabled_notifications_result => {
+ var enabled_notifications = enabled_notifications_result["enabled-notifications"];
+
+ if (enabled_notifications) {
+ for (const enabled_notification of enabled_notifications) {
+
+ for (const new_category_change of new_category_changes) {
+ if (new_category_change.user_just_went_online) {
+ if (enabled_notification.login === "ANY STREAMER" || enabled_notification.login === new_category_change.user_login) {
+
+ if (enabled_notification.fromcat === "OFFLINE") {
+
+ if (enabled_notification.tocat === "ANY CATEGORY" || enabled_notification.tocat === new_category_change.game_name) {
+ //send alert
+ msg = msg + "[" + toUnicodeVariant(new_category_change.user_login, 'bs') + "] just went online and has changed the category to ["+ toUnicodeVariant(new_category_change.game_name, 'bs') + "]. ";
+ }
+ }
+ }
+ } else {
+
+ if (enabled_notification.login === "ANY STREAMER" || enabled_notification.login === new_category_change.user_login) {
+
+ if (enabled_notification.fromcat === "ANY CATEGORY" || enabled_notification.fromcat === new_category_change.old_game_name) {
+
+ if (enabled_notification.tocat === "ANY CATEGORY" || enabled_notification.tocat === new_category_change.game_name) {
+ //send alert
+ msg = msg + "[" + toUnicodeVariant(new_category_change.user_login, 'bs') + "] has changed the category from [" + toUnicodeVariant(new_category_change.old_game_name, 'bs') + "] to [" + toUnicodeVariant(new_category_change.game_name, 'bs') + "]. ";
+ }
+
+ }
+
+ }
+ }
+ }
+
+
+ }
+ }
+
+
+ if (msg !== "") {
+ alert(msg + "\n\nɴᴏᴛɪꜰɪᴇᴅ ʙʏ ᴛᴡɪᴛᴄʜ ᴄᴀᴛᴇɢᴏʀʏ ᴄʜᴀɴɢᴇ ɴᴏᴛɪꜰɪᴇʀ");
+ }
+ });
+}
+
+async function currentTabHandlesNotification(streamers) {
+ var msg = "Message: ";
+
+ for (const streamer of streamers) {
+ msg = msg + streamer + " went online. ";
+ }
+}
+
+
+function newCategoryNotification(new_category_changes) {
+
+ chrome.tabs.query({active: true, currentWindow: true}).then(([tab]) => {
+
+ chrome.scripting.executeScript(
+ {
+ target: {tabId: tab.id},
+ //files: ['test.js'],
+ function: currentTabHandlesCategoryChangeNotification,
+ args: [new_category_changes],
+ });
+
+
+
+ }).catch(err => setTimeout(function(){ newCategoryNotification(new_category_changes); }, 5000))
+}
+
+function newNotification(streamers) {
+
+ chrome.tabs.query({active: true, currentWindow: true}).then(([tab]) => {
+ chrome.scripting.executeScript(
+ {
+ target: {tabId: tab.id},
+ //files: ['test.js'],
+ function: currentTabHandlesNotification,
+ args: [streamers],
+ })
+ }).catch(err => setTimeout(function(){ newNotification(streamers); }, 3000))
+}
+
+function forceRefresh() {
+ chrome.storage.local.set({"lastTTVTokenRefresh": 0}, () => {
+ chrome.storage.local.set({"callUserInfos": true}, () => {
+ refreshToken(true);
+ });
+ });
+}
+
+function disconnect() {
+ chrome.storage.sync.remove("ttvToken");
+ chrome.storage.local.remove("ttvStreams_data");
+ chrome.storage.local.remove("ttvUser_data");
+ chrome.storage.local.remove("ttvUser");
+}
+
+chrome.runtime.onMessageExternal.addListener(
+ function(request, sender, sendResponse) {
+ if (sender.origin === "https://strikr.alwaysdata.net") {
+ if (request.requestType == "setTtvToken") {
+ if (request.ttvToken != "none") {
+ chrome.storage.sync.set({"ttvToken": request.ttvToken}, () => {
+ forceRefresh();
+ });
+ sendResponse({status: "success"});
+ } else {
+ chrome.storage.sync.set({"ttvToken": "failed"});
+ sendResponse({status: "token_none"});
+ }
+ } else {
+ console.log("Unknown message type");
+ sendResponse({status: "unknown_msg"});
+ }
+ }
+ }
+);
+
+function validateTTVToken(ttvToken, callback) {
+ fetch("https://id.twitch.tv/oauth2/validate", {
+ method: "GET",
+ headers: {
+ "Authorization": "Bearer " + ttvToken
+ }
+ }).then(response => {
+ chrome.storage.local.set({"lastTTVTokenRefresh": Date.now()});
+ response.json().then(data => {
+ callback(data);
+ })
+ }, reason => {
+ callback(null);
+ });
+}
+
+function getLiveFollowedStreams(ttvToken, ttvUserId, clientId, callback) {
+ fetch("https://api.twitch.tv/helix/streams/followed?user_id=" + ttvUserId, {
+ method: "GET",
+ headers: {
+ "Authorization": "Bearer " + ttvToken,
+ "Client-Id": clientId
+ }
+ }).then(response => {
+ response.json().then(data => {
+ callback(data);
+ })
+ }, reason => {
+ // TODO: handle this case
+ console.log(reason);
+ });
+}
+
+function getUserInfos(ttvToken, clientId, callback) {
+ fetch("https://api.twitch.tv/helix/users", {
+ method: "GET",
+ headers: {
+ "Authorization": "Bearer " + ttvToken,
+ "Client-Id": clientId
+ }
+ }).then(response => {
+ response.json().then(data => {
+ callback(data);
+ })
+ }, reason => {
+ // TODO: handle this case
+ console.log(reason);
+ });
+}
diff --git a/images/icon.png b/images/icon.png
new file mode 100644
index 0000000..3df58da
Binary files /dev/null and b/images/icon.png differ
diff --git a/images/loading.svg b/images/loading.svg
new file mode 100644
index 0000000..a6d4c8a
--- /dev/null
+++ b/images/loading.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..4c3f778
--- /dev/null
+++ b/index.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+ Please wait a few seconds...
+
+
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..aa9cba8
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,26 @@
+{
+"update_url": "https://clients2.google.com/service/update2/crx",
+
+ "name": "Twitch Category Change Notifier",
+ "description": "Know when your favorite streamers are changing categories",
+ "version": "1.0.1",
+ "manifest_version": 3,
+ "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1uE11ize3U8cy0zr3m1R8HxA1cDMAMGadNOP2v/HRVa7n3Td1Zm8Aj6wlYodv6emLzXRGF/WhAaKQatX97Hkm8Q2HQZ+CVoyCw7X5oD3VIZo3m+K2mmB9ViPdkQXWzITxZcGuA9KdxamragF9QG62UXEPAeEuajQQvfYlK4jdjNmXhT8lXa2aAJ3/G9x2x8GcRfzwi64K9/r0gV4GEyq0E91QQou4O8mw3eqTNbZ8DjhyhHJJAiEmghlz73st2UUCyCYDZT26J2KoKFa3oVXgkGpLoC/Z0C/i76lLhP6RjKB3WniWCS0HNmn4yPNkgTg+Evv2ogC/NzqEJRz/sMYEQIDAQAB",
+ "background": {
+ "service_worker": "background.js"
+ },"host_permissions": [""],
+ "permissions": ["storage", "alarms", "scripting", "activeTab", "background"],
+ "action": {
+ "default_popup": "popup.html"
+ },
+ "icons": {
+ "256": "images/icon.png"
+ },
+ "externally_connectable": {
+ "matches": ["https://strikr.alwaysdata.net/*"]
+ },
+ "web_accessible_resources": [{
+ "resources": ["test.js"],
+ "matches": [""]
+ }]
+}
\ No newline at end of file
diff --git a/popup.html b/popup.html
new file mode 100644
index 0000000..748b0f5
--- /dev/null
+++ b/popup.html
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
TTV Category Change Notifier
+
+
+
+
Click here to Logout.Twitch User
+
+
+
+
+
Don't like the current game a streamer is playing?
Waiting for a streamer to finally start playing something?
Missed another watch party because it was unannounced?
+
Login with your Twitch account and create alerts for your individual use case.
+
+
+
I am very grateful for PayPal donations, even smol ones. -ttv/Strikr
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
I am very grateful for PayPal donations, even smol ones. -ttv/Strikr
+
+
+
+
+
+
+
+
Currently active Alerts:
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/scripts/j.js b/scripts/j.js
new file mode 100644
index 0000000..200b54e
--- /dev/null
+++ b/scripts/j.js
@@ -0,0 +1,2 @@
+/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */
+!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 {
+ disconnect();
+ });
+ };
+
+ chrome.storage.sync.get("notify-all-streams", notify_result => {
+ this.document.getElementById("notify-all-streams-checkbox").checked = notify_result["notify-all-streams"];
+ });
+ this.document.getElementById("notify-all-streams-checkbox").onchange = function(me) {
+ chrome.storage.sync.set({"notify-all-streams": me.target.checked});
+ };
+
+ this.document.getElementById("searchbox").addEventListener("input", val => {
+ chrome.storage.local.get("currentTab", currentTab_result => {
+ switch(currentTab_result.currentTab) {
+ case "followed-streams-button":
+ chrome.storage.local.get("ttvStreams_data", ttvStreams_data_result => {
+ let ttvStreams_data = ttvStreams_data_result.ttvStreams_data;
+
+ let newStreamData = ttvStreams_data.filter(element => {
+ return element.user_name.toLowerCase().includes(this.document.getElementById("searchbox").value.toLowerCase());
+ })
+
+ populateStreams(newStreamData);
+ });
+ break;
+ case "streams-notifications-button":
+ chrome.storage.local.get("followedChannels", followedChannels_result => {
+ let followedChannels = followedChannels_result.followedChannels;
+
+ let newNotifsData = followedChannels.filter(element => {
+ return element.name.toLowerCase().includes(this.document.getElementById("searchbox").value.toLowerCase());
+ })
+
+ addNotifChannels(newNotifsData);
+ });
+ break;
+ }
+ });
+ });
+
+ populatePage();
+});
+
+function disconnect() {
+ chrome.storage.sync.remove("ttvToken");
+ chrome.storage.local.remove("ttvStreams_data", () => {
+ chrome.storage.local.remove("ttvUser_data", () => {
+ populatePage();
+ });
+ });
+ chrome.storage.local.remove("ttvUser");
+}
+
+function populatePage() {
+ isDisconnected(result => {
+ if (!result) {
+ this.document.getElementById("login-twitch").style["display"] = "none";
+ this.document.getElementById("user-infos-top").style["display"] = "inline-block";
+ this.document.getElementById("disconnected-tip").style["display"] = "none";
+ this.document.getElementById("disconnected-stream-tip").style["display"] = "none";
+ this.document.getElementById("searchbox").style["display"] = "inline-block";
+ this.document.getElementById("informations").style["display"] = "block";
+ this.document.getElementById("bottom-bar").style["display"] = "block";
+
+
+ chrome.storage.local.get("ttvUser_data", ttvUser_data_result => {
+ let ttvUser_data = ttvUser_data_result.ttvUser_data;
+
+ // Populating username + user picture
+ this.document.getElementById("username-top").innerText = ttvUser_data.display_name;
+ this.document.getElementById("user-pic-top").src = ttvUser_data.profile_image_url;
+ });
+
+ chrome.storage.local.get("ttvStreams_data", ttvStreams_data_result => {
+ let ttvStreams_data = ttvStreams_data_result.ttvStreams_data;
+
+ });
+ } else {
+ this.document.getElementById("login-twitch").style["display"] = "inline-block";
+ this.document.getElementById("user-infos-top").style["display"] = "none";
+ this.document.getElementById("disconnected-tip").style["display"] = "inline-block";
+ this.document.getElementById("disconnected-stream-tip").style["display"] = "block";
+ this.document.getElementById("searchbox").style["display"] = "none";
+ this.document.getElementById("informations").style["display"] = "none";
+ this.document.getElementById("bottom-bar").style["display"] = "none";
+ }
+ })
+
+ setTimeout(populatePage, 10000);
+}
+
+function populateStreams(stream_data) {
+ this.document.getElementById("streams").innerHTML = "";
+ stream_data.forEach(element => {
+ this.document.getElementById("streams").innerHTML += newDiv(element);
+ });
+}
+
+function isDisconnected(callback) {
+ chrome.storage.local.get("disconnected", disconnected_result => {
+ if (disconnected_result.disconnected != null) {
+ callback(true);
+ return;
+ }
+ chrome.storage.sync.get("ttvToken", ttvToken_result => {
+ if (ttvToken_result.ttvToken == null || ttvToken_result.ttvToken == "failed" || ttvToken_result.ttvToken == "none") {
+ callback(true);
+ return;
+ }
+ callback(false);
+ });
+ });
+}
+
+function newDiv(stream_info){
+ var newStream =
+ '' +
+ '
' +
+ '
' + sanitizeString(stream_info.user_name) + '
' +
+ '
' +
+ '
' + sanitizeString(stream_info.title) + '
' +
+ '
' + sanitizeString(stream_info.game_name) + '
' +
+ '
' + new Intl.NumberFormat('fr-FR').format(stream_info.viewer_count) + '
' +
+ '
' +
+ '
' +
+ '
';
+
+ return newStream;
+}
+
+function sanitizeString(str){
+ str = str.replace(/[^a-z0-9áéíóúñü \.,_-]/gim,"");
+ return str.trim();
+}
+
+function getStreamUptime(startTime) {
+ let start = new Date(startTime);
+ let now = new Date();
+ let hours = (((now - start) / 1000) / 60) / 60;
+ let minutes = (((now - start) / 1000) / 60) % 60;
+
+ return parseInt(hours) + ":" + parseInt(minutes).toString().padStart(2, '0');
+}
+
+function getFollowedChannels(ttvToken, ttvUserId, clientId, callback, page) {
+ fetch("https://api.twitch.tv/helix/users/follows?first=100&from_id=" + ttvUserId + (page != null ? "&after=" + page : ""), {
+ method: "GET",
+ headers: {
+ "Authorization": "Bearer " + ttvToken,
+ "Client-Id": clientId
+ }
+ }).then(response => {
+ response.json().then(data => {
+ if (data.pagination != null && Object.keys(data.pagination).length !== 0 && data.total > 100) {
+ let totalData = [];
+ Array.prototype.push.apply(totalData, data.data);
+ getFollowedChannels(ttvToken, ttvUserId, clientId, data => {
+ Array.prototype.push.apply(totalData, data);
+ callback(totalData);
+ }, data.pagination.cursor);
+ } else {
+ callback(data.data);
+ }
+ })
+ }, reason => {
+ // TODO: handle this case
+ console.log(reason);
+ });
+}
+
+function getAllTwitchCategories(ttvToken, ttvUserId, clientId, callback, page) {
+ fetch("https://api.twitch.tv/helix/games/top?first=100" + (page != null ? "&after=" + page : ""), {
+ method: "GET",
+ headers: {
+ "Authorization": "Bearer " + ttvToken,
+ "Client-Id": clientId
+ }
+ }).then(response => {
+ response.json().then(data => {
+ if (data.pagination != null && Object.keys(data.pagination).length !== 0) {
+ let totalData = [];
+ Array.prototype.push.apply(totalData, data.data);
+ getAllTwitchCategories(ttvToken, ttvUserId, clientId, data => {
+ Array.prototype.push.apply(totalData, data);
+ callback(totalData);
+ }, data.pagination.cursor);
+ } else {
+ callback(data.data);
+ }
+ })
+ }, reason => {
+ // TODO: handle this case
+ console.log(reason);
+ });
+}
+
+/**
+ *
+ * @param {*} data Data to normalize
+ * @returns Returns alphabetically sorted normalized data array
+ */
+function normalizeFollowedChannels(data) {
+ var newData = [];
+
+ data.forEach(element => {
+ newData.push({
+ "id": element.to_id,
+ "name": element.to_name,
+ "login": element.to_login,
+ "date": element.followed_at
+ });
+ });
+
+ newData.sort(function(a, b) {
+ return a.name.localeCompare(b.name);
+ });
+
+ return newData;
+}
\ No newline at end of file
diff --git a/scripts/tab_notifications.js b/scripts/tab_notifications.js
new file mode 100644
index 0000000..9e82b75
--- /dev/null
+++ b/scripts/tab_notifications.js
@@ -0,0 +1,317 @@
+function openNotificationsTab() {
+ populateNotifications();
+}
+
+function populateNotifications() {
+ isDisconnected(result => {
+ if (!result) {
+ chrome.storage.local.get("lastFollowedChannelsRefresh", lastFollowedChannelsRefresh_result => {
+ if (lastFollowedChannelsRefresh_result.lastFollowedChannelsRefresh < (Date.now() - 1800000)) {
+ chrome.storage.sync.get("ttvToken", ttvToken_result => {
+ chrome.storage.local.get("ttvUser", ttvUser_result => {
+ getFollowedChannels(
+ ttvToken_result.ttvToken,
+ ttvUser_result.ttvUser.user_id,
+ ttvUser_result.ttvUser.client_id,
+ data => {
+ chrome.storage.local.set({"lastFollowedChannelsRefresh": Date.now()});
+ chrome.storage.local.set({"followedChannels": normalizeFollowedChannels(data)}, () => {
+ chrome.storage.local.get("followedChannels", followedChannels_result => {
+ chrome.storage.local.get("allTwitchCategories", allTwitchCategories_result => {
+ if (!allTwitchCategories_result.allTwitchCategorie || allTwitchCategories_result.allTwitchCategories.length < 50) {
+ getAllTwitchCategories(
+ ttvToken_result.ttvToken, ttvUser_result.ttvUser.user_id,
+ ttvUser_result.ttvUser.client_id, data => {
+ var twitchCategoriesMap = data.map(elm => elm.name);
+ chrome.storage.local.set({"allTwitchCategories": (twitchCategoriesMap)}, () => {
+ chrome.storage.local.get("allTwitchCategories", allTwitchCategories_result => {
+ addNotifChannels(followedChannels_result.followedChannels, allTwitchCategories_result.allTwitchCategories);
+
+ });
+ });
+ },
+ null);
+ } else {
+ addNotifChannels(followedChannels_result.followedChannels, allTwitchCategories_result.allTwitchCategories);
+ }
+ });
+ });
+ });
+ },
+ null);
+ });
+ });
+ } else {
+ chrome.storage.local.get("followedChannels", followedChannels_result => {
+ chrome.storage.local.get("allTwitchCategories", allTwitchCategories_result => {
+ addNotifChannels(followedChannels_result.followedChannels, allTwitchCategories_result.allTwitchCategories);
+
+ });
+ });
+ }
+ });
+ } else {
+
+ }
+ })
+}
+
+function updateAllAlerts() {
+ $(".loading").show();
+
+ this.document.getElementsByClassName("all-alerts")[0].innerHTML = "";
+
+ chrome.storage.sync.get("enabled-notifications", notifieds_result => {
+ var enabled_notifications = notifieds_result["enabled-notifications"];
+ if (enabled_notifications == null || enabled_notifications === false)
+ enabled_notifications = [];
+
+
+ var q_enabled_notifications = enabled_notifications.reduce((unique, o) => {
+ if(!unique.some(obj => obj.type === o.type && obj.login === o.login && obj.fromcat === o.fromcat && obj.tocat === o.tocat)) {
+ unique.push(o);
+ }
+ return unique;
+ },[]);
+
+
+ var html = "";
+ q_enabled_notifications.forEach(function (element, i) {
+ var text = "I receive an " + (element.type).toString() + " when " + (element.login).toString() + " changes category from " + (element.fromcat).toString() + " to " + (element.tocat).toString()+"";
+ var single_alert = ""+text+"
"
+ html = html + single_alert;
+ });
+
+ this.document.getElementsByClassName("all-alerts")[0].innerHTML = html;
+
+ $(".remove-alert").click(function(){
+ var type = $(this).parents(".single-alert").children('.type')[0].textContent;
+ var login = $(this).parents(".single-alert").children('.login')[0].textContent;
+ var fromcat = $(this).parents(".single-alert").children('.fromcat')[0].textContent;
+ var tocat = $(this).parents(".single-alert").children('.tocat')[0].textContent;
+
+ removeFromEnabledNotifications(type,login,fromcat,tocat);
+ })
+
+ $(".loading").hide();
+ });
+}
+
+function addNotifChannels(followedChannels, allTwitchCategories) {
+ chrome.storage.sync.get("notified-streams", notifieds_result => {
+
+ updateAllAlerts();
+
+ this.document.getElementById("alertcreator").innerHTML = "";
+ this.document.getElementById("alertcreator").innerHTML = newStreamElement(notifieds_result["notified-streams"], followedChannels, allTwitchCategories);
+
+ tail.select("select", {
+ search: true,
+ descriptions: true,
+ hideSelected: true,
+ hideDisabled: true,
+ multiple: false,
+ });
+
+
+ document.querySelectorAll('.notification').forEach(function(el) {
+ $(".add-new-alert").click(function(){
+ var selects = el.getElementsByTagName('select');
+
+ var select_type;
+ var select_login;
+ var select_fromcat;
+ var select_tocat;
+
+
+ for (var i = 0; i < selects.length; i++) {
+ var select = selects.item(i);
+
+ if (i === 0) {
+ select_type = $(select).val();
+ }
+ if (i === 1) {
+
+ select_login = $(select).val();
+ }
+ if (i === 2) {
+ select_fromcat = $(select).val();
+ }
+ if (i === 3) {
+ select_tocat = $(select).val();
+ }
+ }
+
+
+ addToEnabledNotifications(select_type,select_login,select_fromcat,select_tocat);
+ });
+ });
+ })
+}
+
+function addToEnabledNotifications(type,login,fromcat,tocat) {
+
+
+ chrome.storage.sync.get("enabled-notifications", notifieds_result => {
+ var enabled_notifications = notifieds_result["enabled-notifications"];
+ if (enabled_notifications == null || enabled_notifications === false )
+ enabled_notifications = [];
+
+ enabled_notifications.push({
+ type: type,
+ login: login,
+ fromcat: fromcat,
+ tocat: tocat,
+ });
+
+
+ var q_enabled_notifications = enabled_notifications.reduce((unique, o) => {
+ if(!unique.some(obj => obj.type === o.type && obj.login === o.login && obj.fromcat === o.fromcat && obj.tocat === o.tocat)) {
+ unique.push(o);
+ }
+ return unique;
+ },[]);
+
+
+ chrome.storage.sync.set({"enabled-notifications": q_enabled_notifications});
+
+ chrome.storage.sync.get("enabled-notifications", notifieds_result => {
+ var enabled_notifications = notifieds_result["enabled-notifications"];
+
+ updateAllAlerts();
+ alert("Done!");
+ });
+ });
+}
+
+function removeFromEnabledNotifications(type,login,fromcat,tocat) {
+
+ chrome.storage.sync.get("enabled-notifications", notifieds_result => {
+ var enabled_notifications = notifieds_result["enabled-notifications"];
+ if (enabled_notifications == null || enabled_notifications === false)
+ enabled_notifications = [];
+
+
+ var q_enabled_notifications = enabled_notifications.reduce((unique, o) => {
+ if(!unique.some(obj => obj.type === o.type && obj.login === o.login && obj.fromcat === o.fromcat && obj.tocat === o.tocat)) {
+ unique.push(o);
+ }
+ return unique;
+ },[]);
+
+
+
+ var index = -1;
+ q_enabled_notifications.forEach(function (element, i) {
+ console.log(element);
+ console.log(type+login+fromcat+tocat);
+
+ if (element.type === type && element.login === login && element.fromcat === fromcat && element.tocat === tocat) {
+ index = i;
+ }
+ });
+ if (index !== -1) {
+ var removed = q_enabled_notifications.splice(index, 1);
+
+ chrome.storage.sync.set({"enabled-notifications": q_enabled_notifications});
+
+ chrome.storage.sync.get("enabled-notifications", notifieds_result => {
+ var enabled_notifications = notifieds_result["enabled-notifications"];
+ updateAllAlerts();
+ alert("Done!");
+ });
+ }
+ });
+}
+
+
+
+
+function addToNotified(login) {
+ chrome.storage.sync.get("notified-streams", notifieds_result => {
+ var notifieds = notifieds_result["notified-streams"];
+ if (notifieds == null)
+ notifieds = [];
+ notifieds.push(login);
+ chrome.storage.sync.set({"notified-streams": notifieds});
+ });
+}
+
+function removeFromNotified(login) {
+ chrome.storage.sync.get("notified-streams", notifieds_result => {
+ var notifieds = notifieds_result["notified-streams"];
+ if (notifieds == null)
+ notifieds = [];
+ if (notifieds.includes(login)) {
+ notifieds.splice(notifieds.indexOf(login), 1);
+ chrome.storage.sync.set({"notified-streams": notifieds});
+ }
+ });
+}
+
+function newStreamElement(notified_streams, followedChannels, allTwitchCategories){
+
+ var dropdowntwitchCategories = document.createElement("select");
+ let any = document.createElement("option");
+ any.text = "ANY CATEGORY";
+ any.value = "ANY CATEGORY";
+ dropdowntwitchCategories.add(any);
+ let off = document.createElement("option");
+ off.text = "OFFLINE";
+ off.value = "OFFLINE";
+ dropdowntwitchCategories.add(off);
+ allTwitchCategories.forEach(element => {
+ let optEle = document.createElement("option");
+ optEle.text = element;
+ optEle.value = element;
+ dropdowntwitchCategories.add(optEle);
+ });
+
+
+
+ var dropdownfollowedChannels = document.createElement("select");
+ let ansy = document.createElement("option");
+ ansy.text = "ANY STREAMER";
+ ansy.value = "ANY STREAMER";
+ dropdownfollowedChannels.add(ansy);
+ followedChannels.forEach(element => {
+ let optEle = document.createElement("option");
+ optEle.text = element.login;
+ optEle.value = element.login;
+ dropdownfollowedChannels.add(optEle);
+ });
+
+
+ var dropdownNotificationTypes = document.createElement("select");
+ var notificationTypes = [{label: "ALERT", value: "alert"},];
+ notificationTypes.forEach(element => {
+ let optEle = document.createElement("option");
+ optEle.text = element.label;
+ optEle.value = element.value;
+ dropdownNotificationTypes.add(optEle);
+ });
+
+
+ var newStream =
+ '' +
+ '
' +
+ '
I receive an '+dropdownNotificationTypes.outerHTML+' when '+dropdownfollowedChannels.outerHTML+' changes category from '+dropdowntwitchCategories.outerHTML+' to '+dropdowntwitchCategories.outerHTML+'' + '
' +
+ '
' +
+ '
' +
+ '
'
+ return newStream;
+}
+
+function getFollowedTime(time) {
+ let date = new Date(time);
+ return date.toLocaleString("en-US", {month: "long", day: "numeric", year: "numeric"}) + " · " + date.toLocaleTimeString("en-US", {hour12: false}).substring(0, 5);
+}
+
+function hasNotificationsEnabled(notified_streams, login) {
+ if (notified_streams == null) {
+ return false;
+ }
+ return notified_streams.includes(login);
+}
\ No newline at end of file
diff --git a/scripts/tabs.js b/scripts/tabs.js
new file mode 100644
index 0000000..1a6220e
--- /dev/null
+++ b/scripts/tabs.js
@@ -0,0 +1,39 @@
+window.addEventListener("load", function() {
+ Array.from(document.getElementById("bottom-bar").getElementsByClassName("tab-button")).forEach(element => {
+ element.onclick = function(click) {
+ switchTab(element.id);
+ };
+ });
+
+
+ switchTab("streams-notifications-button");
+});
+
+function switchTab(tabName) {
+ removeAllSelected();
+ hideAllTabs();
+
+
+ switch(tabName) {
+ case "streams-notifications-button":
+ chrome.storage.local.set({"currentTab": tabName});
+ this.document.getElementById("streams-notifications-button").classList.add("selected");
+ this.document.getElementById("notifications-page").style["display"] = "block";
+ openNotificationsTab();
+ break;
+ }
+}
+
+function removeAllSelected() {
+ var selected = Array.from(document.getElementById("bottom-bar").getElementsByClassName("selected"));
+ selected.forEach(element => {
+ element.classList.remove("selected");
+ });
+}
+
+function hideAllTabs() {
+ var selected = Array.from(document.getElementsByClassName("tab"));
+ selected.forEach(element => {
+ element.style["display"] = "none";
+ });
+}
\ No newline at end of file
diff --git a/scripts/tail.js b/scripts/tail.js
new file mode 100644
index 0000000..3da4eae
--- /dev/null
+++ b/scripts/tail.js
@@ -0,0 +1,13 @@
+/*
+ | tail.select - The vanilla solution to make your HTML select fields AWESOME!
+ | @file ./js/tail.select.js
+ | @author wolffe
+ | @author SamBrishes
+ | @version 0.5.22
+ |
+ | @website https://github.com/wolffe/tail.select.js
+ | @license X11 / MIT License
+ | @copyright Copyright © 2020 - 2021 wolffe
+ | @copyright Copyright © 2014 - 2019 SamBrishes, pytesNET
+ */
+!function(root,factory){"function"==typeof define&&define.amd?define((function(){return factory(root)})):"object"==typeof module&&module.exports?module.exports=factory(root):(void 0===root.tail&&(root.tail={}),root.tail.select=factory(root),"undefined"!=typeof jQuery&&(jQuery.fn.tailselect=function(o){var r=[],i;return this.each((function(){!1!==(i=tail.select(this,o))&&r.push(i)})),1===r.length?r[0]:0!==r.length&&r}),"undefined"!=typeof MooTools&&Element.implement({tailselect:function(o){return new tail.select(this,o)}}))}(window,(function(root){"use strict";var w=root,d=root.document;function cHAS(el,name){return!!(el&&"classList"in el)&&el.classList.contains(name)}function cADD(el,name){return el&&"classList"in el?el.classList.add(name):void 0}function cREM(el,name){return el&&"classList"in el?el.classList.remove(name):void 0}function trigger(el,event,opt){var ev;if(CustomEvent&&CustomEvent.name)var ev=new CustomEvent(event,opt);else(ev=d.createEvent("CustomEvent")).initCustomEvent(event,!!opt.bubbles,!!opt.cancelable,opt.detail);return el.dispatchEvent(ev)}function clone(obj,rep){if("function"==typeof Object.assign)return Object.assign({},obj,rep||{});var clone=Object.constructor();for(var key in obj)clone[key]=key in rep?rep[key]:obj[key];return clone}function create(tag,classes){var r=d.createElement(tag);return r.className=classes&&classes.join?classes.join(" "):classes||"",r}var select=function(el,config){if((el="string"==typeof el?d.querySelectorAll(el):el)instanceof NodeList||el instanceof HTMLCollection||el instanceof Array){for(var _r=[],l=el.length,i=0;i=0?config.multiLimit:1/0,this.e=el,this.id=++select.count,this.con=clone(select.defaults,config),this.events={},select.inst["tail-"+this.id]=this,this.init().bind()},options;return select.count=0,select.inst={},select.defaults={animate:!0,classNames:null,csvOutput:!1,csvSeparator:",",descriptions:!1,deselect:!1,disabled:!1,height:350,hideDisabled:!1,hideSelected:!1,items:{},locale:"en",linguisticRules:{"е":"ё",a:"ä",o:"ö",u:"ü",ss:"ß"},multiple:!1,multiLimit:1/0,multiPinSelected:!1,multiContainer:!1,multiShowCount:!0,multiShowLimit:!1,multiSelectAll:!1,multiSelectGroup:!0,openAbove:null,placeholder:null,search:!1,searchConfig:["text","value"],searchFocus:!0,searchMarked:!0,searchMinLength:1,searchDisabled:!0,sortItems:!1,sortGroups:!1,sourceBind:!1,sourceHide:!0,startOpen:!1,stayOpen:!1,width:null,cbComplete:void 0,cbEmpty:void 0,cbLoopItem:void 0,cbLoopGroup:void 0},select.strings={en:{all:"All",none:"None",empty:"No Options available",emptySearch:"No Options found",limit:"You can't select more Options",placeholder:"Select an Option...",placeholderMulti:"Select up to :limit Options...",search:"Type in to search...",disabled:"This Field is disabled"},modify:function(locale,id,string){if(!(locale in this))return!1;if(id instanceof Object)for(var key in id)this.modify(locale,key,id[key]);else this[locale][id]="string"==typeof string?string:this[locale][id];return!0},register:function(locale,object){return"string"==typeof locale&&object instanceof Object&&(this[locale]=object,!0)}},select.prototype={_e:function(string,replace,def){if(!(string in this.__))return def||string;var string;if("function"==typeof(string=this.__[string])&&(string=string.call(this,replace)),"object"==typeof replace)for(var key in replace)string=string.replace(key,replace[key]);return string},init:function(){var self=this,classes=["tail-select"],con=this.con,regexp=/^[0-9.]+(?:cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|\%)$/i,c=!0===con.classNames?this.e.className:con.classNames;classes.push(c&&c.push?c.join(" "):c&&c.split?c:"no-classes"),con.hideSelected&&classes.push("hide-selected"),con.hideDisabled&&classes.push("hide-disabled"),0==con.multiLimit&&classes.push("disabled"),con.multiple&&classes.push("multiple"),con.deselect&&classes.push("deselect"),con.disabled&&classes.push("disabled"),this.__=clone(select.strings.en,select.strings[con.locale]||{}),this._init=!0,this._query=!1,this.select=create("DIV",classes),this.label=create("DIV","select-label"),this.dropdown=create("DIV","select-dropdown"),this.search=create("DIV","dropdown-search"),this.csvInput=create("INPUT","select-search"),null!==this.e.getAttribute("tabindex")?this.select.setAttribute("tabindex",this.e.getAttribute("tabindex")):this.select.setAttribute("tabindex",0),con.width&®exp.test(con.width)?this.select.style.width=con.width:con.width&&!isNaN(parseFloat(con.width,10))&&(this.select.style.width=con.width+"px"),this.label.addEventListener("click",(function(event){self.toggle.call(self,self.con.animate)})),this.select.appendChild(this.label),isNaN(parseInt(con.height,10))||(this.dropdown.style.maxHeight=parseInt(con.height,10)+"px"),con.search&&(this.search.innerHTML='',this.search.children[0].placeholder=this._e("search"),this.search.children[0].addEventListener("input",(function(event){self.query.call(self,this.value.length>con.searchMinLength?this.value:void 0)})),this.dropdown.appendChild(this.search)),this.select.appendChild(this.dropdown),this.csvInput.type="hidden",con.csvOutput&&(this.csvInput.name=this.e.name,this.e.removeAttribute("name"),this.select.appendChild(this.csvInput)),con.multiple&&con.multiContainer&&(d.querySelector(con.multiContainer)?(this.container=d.querySelector(con.multiContainer),this.container.className+=" tail-select-container"):!0===con.multiContainer&&(this.container=this.label,this.container.className+=" tail-select-container")),this.options=new options(this.e,this);for(var l=this.e.options.length,i=0;i0&&"UL"==temp.tagName&&temp.children[40==key?0:temp.children.length-1])&&(!cHAS(opt,"dropdown-option")||cHAS(opt,"disabled")););if(opt||40!=key?opt||38!=key||(opt=(tmp=self.dropdown.querySelectorAll(".dropdown-option:not(.disabled)"))[tmp.length-1]):opt=self.dropdown.querySelector(".dropdown-option:not(.disabled)"),opt&&(inner=self.dropdown.querySelector(".dropdown-inner"))){var pos=function(el){for(var _r={top:el.offsetTop,height:el.offsetHeight};(el=el.parentElement)!=inner;)_r.top+=el.offsetTop;return _r}(opt);cADD(opt,"hover"),inner.scrollTop=Math.max(0,pos.top-2*pos.height)}return!0})),d.addEventListener("click",(function(ev){if(!cHAS(self.select,"active")||cHAS(self.select,"idle"))return!1;if(!0===self.con.stayOpen)return!1;for(var targets=[self.e,self.select,self.container],l=targets.length,i=0;i=0)||(u.splice(u.indexOf(item),1),!1)}));self.options.walk.call(self.options,"unselect",u),self.options.walk.call(self.options,"select",s)}})),!0)},callback:function(item,state,_force){var rkey,rgrp,rsel="[data-key='"+item.key.replace(/('|\\)/g,"\\$1")+"'][data-group='"+item.group.replace(/('|\\)/g,"\\$1")+"']";if("rebuild"==state)return this.query();var element=this.dropdown.querySelector(rsel);return element&&["select","disable"].indexOf(state)>=0?cADD(element,"select"==state?"selected":"disabled"):element&&["unselect","enable"].indexOf(state)>=0&&cREM(element,"unselect"==state?"selected":"disabled"),this.update(item),!0===_force||this.trigger("change",item,state)},trigger:function(event){if(this._init)return!1;var obj={bubbles:!1,cancelable:!0,detail:{args:arguments,self:this}};"change"==event&&arguments[2]&&arguments[2].indexOf("select")>=0&&(trigger(this.e,"input",obj),trigger(this.e,"change",obj)),trigger(this.select,"tail::"+event,obj);var args=[],pass;return Array.prototype.map.call(arguments,(function(item,i){i>0&&args.push(item)})),(this.events[event]||[]).forEach((function(item){(pass=[].concat(args)).push(item.args||null),(item.cb||function(){}).apply(obj.detail.self,pass)})),!0},calc:function(){var clone=this.dropdown.cloneNode(!0),height=this.con.height,search=0,inner=this.dropdown.querySelector(".dropdown-inner");(clone=this.dropdown.cloneNode(!0)).style.cssText="height:auto;min-height:auto;max-height:none;opacity:0;display:block;visibility:hidden;",clone.style.maxHeight=this.con.height+"px",clone.className+=" cloned",this.dropdown.parentElement.appendChild(clone),height=height>clone.clientHeight?clone.clientHeight:height,this.con.search&&(search=clone.querySelector(".dropdown-search").clientHeight),this.dropdown.parentElement.removeChild(clone);var pos=this.select.getBoundingClientRect(),bottom=w.innerHeight-(pos.top+pos.height),view=height+search>bottom&&pos.top>bottom;return!0===this.con.openAbove||!1!==this.con.openAbove&&view?(view=!0,height=Math.min(height,pos.top-10),cADD(this.select,"open-top")):(view=!1,height=Math.min(height,bottom-10),cREM(this.select,"open-top")),inner&&(this.dropdown.style.maxHeight=height+"px",inner.style.maxHeight=height-search+"px"),this},query:function(search,conf){var item,tp,ul,li,a1,a2,self=this,con=this.con,g="getAttribute",root=create("DIV","dropdown-inner"),func=search?"finder":"walker",args=search?[search,conf]:[con.sortItems,con.sortGroups];for(this._query="string"==typeof search&&search;item=this.options[func].apply(this.options,args);){if(!ul||ul&&ul[g]("data-group")!==item.group){if(!((tp=(con.cbLoopGroup||this.cbGroup).call(this,item.group,search,root))instanceof Element))break;(ul=tp).setAttribute("data-group",item.group),root.appendChild(ul)}if(null!==(li=(con.cbLoopItem||this.cbItem).call(this,item,ul,search,root))){if(!1===li)break;li.setAttribute("data-key",item.key),li.setAttribute("data-group",item.group),li.addEventListener("click",(function(event){if(!this.hasAttribute("data-key"))return!1;var key=this[g]("data-key"),group=this[g]("data-group")||"#";self.options.toggle.call(self.options,key,group)&&(!1!==self.con.stayOpen||self.con.multiple||self.close.call(self,self.con.animate))})),ul.appendChild(li)}}var count=root.querySelectorAll("*[data-key]").length;0==count&&(this.con.cbEmpty||function(element){var li=create("SPAN","dropdown-empty");li.innerText=this._e("empty"),element.appendChild(li)}).call(this,root,search),count>0&&con.multiple&&con.multiLimit==1/0&&con.multiSelectAll&&(a1=create("BUTTON","tail-all"),a2=create("BUTTON","tail-none"),a1.innerText=this._e("all"),a1.addEventListener("click",(function(event){event.preventDefault();var options=self.dropdown.querySelectorAll(".dropdown-inner .dropdown-option");self.options.walk.call(self.options,"select",options)})),a2.innerText=this._e("none"),a2.addEventListener("click",(function(event){event.preventDefault();var options=self.dropdown.querySelectorAll(".dropdown-inner .dropdown-option");self.options.walk.call(self.options,"unselect",options)})),(li=create("SPAN","dropdown-action")).appendChild(a1),li.appendChild(a2),root.insertBefore(li,root.children[0]));var data=this.dropdown.querySelector(".dropdown-inner");return this.dropdown[(data?"replace":"append")+"Child"](root,data),cHAS(this.select,"active")&&this.calc(),this.updateCSV().updateLabel()},cbGroup:function(group,search){var ul=create("UL","dropdown-optgroup"),self=this,a1,a2;return"#"==group?ul:(ul.innerHTML=''+group+"",this.con.multiple&&this.con.multiLimit==1/0&&this.con.multiSelectAll&&(a1=create("BUTTON","tail-none"),a2=create("BUTTON","tail-all"),a1.innerText=this._e("none"),a1.addEventListener("click",(function(event){event.preventDefault();var grp=this.parentElement.parentElement.getAttribute("data-group");self.options.all.call(self.options,"unselect",grp)})),a2.innerText=this._e("all"),a2.addEventListener("click",(function(event){event.preventDefault();var grp=this.parentElement.parentElement.getAttribute("data-group");self.options.all.call(self.options,"select",grp)})),ul.children[0].appendChild(a1),ul.children[0].appendChild(a2)),ul)},cbItem:function(item,optgroup,search){var li=create("LI","dropdown-option"+(item.selected?" selected":"")+(item.disabled?" disabled":""));return li.title=item.option.title,search&&search.length>0&&this.con.searchMarked?(search=this.options.applyLinguisticRules(search),li.innerHTML=item.value.replace(new RegExp("("+search+")","i"),"$1")):li.innerText=item.value,this.con.descriptions&&item.description&&(li.innerHTML+=''+item.description+""),li},update:function(item){return this.updateLabel().updateContainer(item).updatePin(item).updateCSV(item)},updateLabel:function(label){if(this.container==this.label&&this.options.selected.length>0)return this.label.querySelector(".label-inner")&&this.label.removeChild(this.label.querySelector(".label-inner")),this.label.querySelector(".label-count")&&this.label.removeChild(this.label.querySelector(".label-count")),this;var c=this.con,len=this.options.selected.length,limit,selected_class;("string"!=typeof label&&(label=c.disabled?"disabled":0==this.dropdown.querySelectorAll("*[data-key]").length?"empty"+(cHAS(this.select,"in-search")?"Search":""):c.multiLimit<=len?"limit":!c.multiple&&this.options.selected.length>0?this.options.selected[0].innerText:"string"==typeof c.placeholder?c.placeholder:"placeholder"+(c.multiple&&c.multiLimit<1/0?"Multi":"")),label=''+(label=this._e(label,{":limit":c.multiLimit},label))+"",limit=c.multiShowLimit&&c.multiLimit<1/0,c.multiple&&c.multiShowCount)&&(label=(label=':c'+label).replace(":c",len+(limit?" / "+c.multiLimit:"")));return this.label.innerHTML=label,this},updateContainer:function(item){if(!this.container||!this.con.multiContainer)return this;var s="[data-group='"+item.group+"'][data-key='"+item.key+"']";if(this.container.querySelector(s))return item.selected||this.container.removeChild(this.container.querySelector(s)),this;if(item.selected){var self=this,hndl=create("DIV","select-handle");hndl.innerText=item.value,hndl.setAttribute("data-key",item.key),hndl.setAttribute("data-group",item.group),hndl.addEventListener("click",(function(event){event.preventDefault(),event.stopPropagation();var key=this.getAttribute("data-key"),grp=this.getAttribute("data-group");self.options.unselect.call(self.options,key,grp)})),this.container.appendChild(hndl)}return this},updatePin:function(item){var inner=this.dropdown.querySelector(".dropdown-inner ul"),option="li[data-key='"+item.key+"'][data-group='"+item.group+"']";if(!this.con.multiPinSelected||!inner||!1!==this._query)return this;if(option=this.dropdown.querySelector(option),item.selected)inner.insertBefore(option,inner.children[0]);else{for(var grp=this.dropdown.querySelector("ul[data-group='"+item.group+"']"),prev=this.options[item.index-1],found=!1;prev&&prev.group==item.group&&!(found=grp.querySelector("li[data-key='"+prev.key+"']"));)prev=this.options[prev.index-1];found&&found.nextElementSibling?grp.insertBefore(option,found.nextElementSibling):grp.appendChild(option)}return this},updateCSV:function(item){if(!this.csvInput||!this.con.csvOutput)return this;for(var selected=[],l=this.options.selected.length,i=0;i=m)return final.call(self);e.height=(h+50>m?m:h+50)+"px",setTimeout(animate,20)}()):(e.cssText="height:"+e.maxHeight+";display:block;overflow:hidden;",final.call(this)),this},close:function(animate){if(!cHAS(this.select,"active")||cHAS(this.select,"idle"))return!1;var final=function(){cREM(this.select,"active"),cREM(this.select,"idle"),this.dropdown.removeAttribute("style"),this.dropdown.querySelector(".dropdown-inner").removeAttribute("style"),this.trigger.call(this,"close")},self=this,e=this.dropdown;return!1!==animate?(cADD(this.select,"idle"),this.dropdown.style.overflow="hidden",function animate(){if(parseInt(e.offsetHeight,10)-50<=0)return final.call(self);e.style.height=parseInt(e.offsetHeight,10)-50+"px",setTimeout(animate,20)}()):final.call(this),this},toggle:function(animate){return cHAS(this.select,"active")?this.close(animate):cHAS(this.select,"idle")?this:this.open(animate)},remove:function(){if(this.e.removeAttribute("data-tail-select"),this.e.hasAttribute("data-select-hidden")&&("0"==this.e.getAttribute("data-select-hidden")&&this.e.style.removeProperty("display"),this.e.removeAttribute("data-select-hidden")),Array.prototype.map.call(this.e.querySelectorAll("[data-select-option='add']"),(function(item){item.parentElement.removeChild(item)})),Array.prototype.map.call(this.e.querySelectorAll("[data-select-optgroup='add']"),(function(item){item.parentElement.removeChild(item)})),this.e.name=this.csvInput.hasAttribute("name")?this.csvInput.name:this.e.name,this.select.parentElement&&this.select.parentElement.removeChild(this.select),this.container)for(var handles=this.container.querySelectorAll(".select-handle"),l=handles.length,i=0;i=con.multiLimit&&(opt.selected=!1),!opt.selected||!con.deselect||opt.hasAttribute("selected")&&0!=con.multiLimit||(opt.selected=!1,opt.parentElement.selectedIndex=-1),opt.hasAttribute("data-description")){var span=create("SPAN");span.innerHTML=opt.getAttribute("data-description"),opt.setAttribute("data-description",span.innerHTML)}return this.items[grp][id]={key:key,value:opt.text,description:opt.getAttribute("data-description")||null,group:grp,option:opt,optgroup:"#"!=grp?this.groups[grp]:void 0,selected:opt.selected,disabled:opt.disabled,hidden:opt.hidden||!1},this.length++,opt.selected&&this.select(this.items[grp][id]),opt.disabled&&this.disable(this.items[grp][id]),!rebuild||this.self.callback(this.items[grp][key],"rebuild")},add:function(key,value,group,selected,disabled,description,rebuild){if(key instanceof Object){for(var k in key)this.add(key[k].key||k,key[k].value,key[k].group,key[k].selected,key[k].disabled,key[k].description,!1);return this.self.query()}if(this.get(key,group))return!1;if("#"!==(group="string"==typeof group?group:"#")&&!(group in this.groups)){var optgroup=create("OPTGROUP");optgroup.label=group,optgroup.setAttribute("data-select-optgroup","add"),this.element.appendChild(optgroup),this.items[group]={},this.groups[group]=optgroup}this.self.con.multiple&&this.selected.length>=this.self.con.multiLimit&&(selected=!1),disabled=!!disabled;var option=d.createElement("OPTION");return option.value=key,option.selected=selected,option.disabled=disabled,option.innerText=value,option.setAttribute("data-select-option","add"),description&&description.length>0&&option.setAttribute("data-description",description),("#"==group?this.element:this.groups[group]).appendChild(option),this.set(option,rebuild)},move:function(item,group,new_group,rebuild){if(!(item=this.get(item,group)))return!1;if("#"!==new_group&&!(new_group in this.groups)){var optgroup=create("OPTGROUP");optgroup.label=new_group,this.element.appendChild(optgroup),this.items[new_group]={},this.groups[new_group]=optgroup,this.groups[new_group].appendChild(item.option)}return delete this.items[item.group][item.key],item.group=new_group,item.optgroup=this.groups[new_group]||void 0,this.items[new_group][item.key]=item,!rebuild||this.self.query()},remove:function(item,group,rebuild){if(!(item=this.get(item,group)))return!1;item.selected&&this.unselect(item),item.disabled&&this.enable(item),item.option.parentElement.removeChild(item.option);var id=/^[0-9]+$/.test(item.key)?"_"+item.key:item.key;return delete this.items[item.group][id],this.length--,0===Object.keys(this.items[item.group]).length&&(delete this.items[item.group],delete this.groups[item.group]),!rebuild||this.self.query()},is:function(state,key,group){var state=this._r(state),item=this.get(key,group);return!item||["select","unselect","disable","enable"].indexOf(state)<0?null:"disable"==state||"enable"==state?"disable"==state?item.disabled:!item.disabled:("select"==state||"unselect"==state)&&("select"==state?item.selected:!item.selected)},handle:function(state,key,group,_force){var item=this.get(key,group),state=this._r(state);if(!item||["select","unselect","disable","enable"].indexOf(state)<0)return null;if("disable"==state||"enable"==state)return item.option in this.disabled||"disable"!=state?item.option in this.disabled&&"enable"==state&&this.disabled.splice(this.disabled.indexOf(item.option),1):this.disabled.push(item.option),item.disabled="disable"==state,item.option.disabled="disable"==state,this.self.callback.call(this.self,item,state);var dis=cHAS(this.self.select,"disabled")||item.disabled||item.option.disabled,lmt=this.self.con.multiple&&this.self.con.multiLimit<=this.selected.length,sgl=!this.self.con.multiple&&this.selected.indexOf(item.option)>0,del=0==this.self.con.multiLimit&&1==this.self.con.deselect,uns=!this.self.con.multiple&&!this.self.con.deselect&&!0!==_force;if("select"==state){if(dis||lmt||del||sgl)return!1;if(!this.self.con.multiple)for(var i in this.selected)this.unselect(this.selected[i],void 0,!0);this.selected.indexOf(item.option)<0&&this.selected.push(item.option)}else if("unselect"==state){if(dis||uns)return!1;this.selected.splice(this.selected.indexOf(item.option),1)}return item.selected="select"==state,item.option.selected="select"==state,item.option[(state.length>6?"remove":"set")+"Attribute"]("selected","selected"),this.self.callback.call(this.self,item,state,_force)},enable:function(key,group){return this.handle("enable",key,group,!1)},disable:function(key,group){return this.handle("disable",key,group,!1)},select:function(key,group){return this.handle("select",key,group,!1)},unselect:function(key,group,_force){return this.handle("unselect",key,group,_force)},toggle:function(item,group){return!!(item=this.get(item,group))&&this.handle(item.selected?"unselect":"select",item,group,!1)},invert:function(state){if(state=this._r(state),["enable","disable"].indexOf(state)>=0)var invert=this.disabled,action="enable"==state?"disable":"enable";else if(["select","unselect"].indexOf(state)>=0)var invert=this.selected,action="select"==state?"unselect":"select";var convert=Array.prototype.filter.call(this,(function(element){return!(element in invert)})),self=this;return[].concat(invert).forEach((function(item){self.handle.call(self,action,item)})),[].concat(convert).forEach((function(item){self.handle.call(self,state,item)})),!0},all:function(state,group){var self=this,list=this;return group in this.items?list=Object.keys(this.items[group]):["unselect","enable"].indexOf(state)>=0&&(list=[].concat("unselect"==state?this.selected:this.disabled)),Array.prototype.forEach.call(list,(function(item){self.handle.call(self,state,item,group,!1)})),!0},walk:function(state,items,args){if(items instanceof Array||items.length)for(var l=items.length,i=0;i0){do{var temp=this.items[this._inGroup][this._inItems.shift()]}while(!0===temp.hidden);return temp}if(this._inGroups.length>0){for(;this._inGroups.length>0;){var group=this._inGroups.shift();if(!(group in this.items))return!1;var keys=Object.keys(this.items[group]);if(keys.length>0)break}return"ASC"==orderi?keys.sort():"DESC"==orderi?keys.sort().reverse():"function"==typeof orderi&&(keys=orderi.call(this,keys)),this._inItems=keys,this._inGroup=group,this.walker(null,null)}return delete this._inLoop,delete this._inItems,delete this._inGroup,delete this._inGroups,!1}var groups=Object.keys(this.groups)||[];return"ASC"==orderg?groups.sort():"DESC"==orderg?groups.sort().reverse():"function"==typeof orderg&&(groups=orderg.call(this,groups)),groups.unshift("#"),this._inLoop=!0,this._inItems=[],this._inGroups=groups,this.walker(orderi,null)}},select}));
\ No newline at end of file
diff --git a/style/bottombar.css b/style/bottombar.css
new file mode 100644
index 0000000..9c79a10
--- /dev/null
+++ b/style/bottombar.css
@@ -0,0 +1 @@
+#bottom-bar{display:none}
diff --git a/style/checkbox.css b/style/checkbox.css
new file mode 100644
index 0000000..9dcc4ee
--- /dev/null
+++ b/style/checkbox.css
@@ -0,0 +1 @@
+.switch{position:relative;display:inline-block;width:30px;height:17px}.slider,.slider:before{position:absolute;transition:.4s}.switch input{opacity:0;width:0;height:0}.slider{cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ccc;border-radius:12px}.slider:before{content:"";height:13px;width:13px;left:2px;bottom:2px;background-color:#fff;border-radius:50%}input:checked+.slider{background-color:#62c462}input:focus+.slider{box-shadow:0 0 1px #62c462}input:checked+.slider:before{transform:translateX(13px)}
diff --git a/style/mainframe.css b/style/mainframe.css
new file mode 100644
index 0000000..2ba5c8f
--- /dev/null
+++ b/style/mainframe.css
@@ -0,0 +1 @@
+#top-bar input,body{background-color:var(--main-light-color)}#login-twitch,#top-bar input{font-family:Roboto,Tahoma,Geneva,Verdana,sans-serif;margin-top:5px;height:25px;display:inline-block}#disconnected-tip,#login-twitch,#top-bar input,.icon{display:inline-block}.stream .right-part .stream-title,.stream .streamer-name{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}#streams,#streams-page,#top-bar,#top-bar input,#user-pic-top,.stream .right-part,.stream .streamer-name{box-sizing:border-box}#top-bar input,.single-alert button{margin-left:5px}*{scrollbar-width:7px;scrollbar-color:var(--scrollbar-thumb-color) var(--main-light-color)}::-webkit-scrollbar{width:7px}::-webkit-scrollbar-track{background:var(--main-light-color)}::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb-color);border-radius:10px;border:0 #fff}body{--main-dark-color:#000000;--main-light-color:#000000;--main-border-color:#00000000;--scrollbar-thumb-color:#585858;--stream-element-text:#dbdbdb;--stream-tags-background:#363636;--stream-tags-border:#ffffff00;--color-white:#ffffff;width:360px;height:570px;margin:0;padding:0;font-family:monospace;font-size:82%}.loading{transform:scale(.7)}.icon{background-image:url("chrome-extension://__MSG_@@extension_id__/images/icon.png");background-size:auto 100%;background-repeat:no-repeat}#top-bar{height:35px;width:100%;position:fixed;background-color:var(--main-dark-color);border-bottom:1px solid var(--main-border-color)}#notifications,.single-alert{background-color:#2e3e3d;color:#fff}#top-bar input{border-radius:4px;border:1px solid var(--main-border-color);position:absolute;width:190px;padding-left:5px;caret-color:var(--color-white);color:var(--color-white);font-weight:700}#top-bar input:focus{outline:0}#login-twitch{position:absolute;right:7px}#login-twitch button{height:100%;background:linear-gradient(0deg,#6832b9 9%,#9147ff 100%);border:none;border-radius:4px;color:var(--color-white);font-weight:700;padding-left:9px;padding-right:9px;cursor:pointer;font-family:monospace}#login-twitch button:active{background:linear-gradient(0deg,#6832b9 50%,#9147ff 100%)}#disconnected-tip{color:var(--color-white);margin-top:8px;margin-left:7px;font-size:10px;line-height:2}#disconnected-stream-tip{position:absolute;top:100px;width:100%;text-align:center;color:#878787;font-size:15px}#username-top,.stream .streamer-name{font-size:14px;color:var(--color-white)}#user-infos-top{position:absolute;top:0;right:0;cursor:pointer;height:35px;padding-left:9px;transition:.2s}#user-infos-top:hover{background-color:#363636;transition:.2s}#username-top{position:relative;margin-top:9px;margin-right:6px;font-weight:700;float:right}#user-pic-top{position:relative;width:27px;height:27px;margin-top:4px;margin-right:5px;border-radius:50%;float:right}#streams-page{position:relative;width:100%;height:500px;top:35px;overflow-y:scroll}#streams{width:100%;padding-left:5px;padding-right:5px;padding-top:10px}#streams .stream{position:relative;height:65px;overflow:hidden;background:linear-gradient(270deg,var(--main-light-color) 50%,var(--main-dark-color) 100%);box-shadow:0 0 5px 1px #00000000;margin-bottom:10px;transition:.5s;cursor:pointer}#streams .stream:hover{background:linear-gradient(270deg,var(--main-light-color) 10%,var(--main-dark-color) 100%);transition:.5s}#informations{text-align:center;color:#6a6a6a;font-size:12px;font-weight:700;font-style:italic;padding:0 5px 10px}.stream .stream-pic{width:115px;height:100%}.stream .streamer-name{position:absolute;bottom:0;background-color:rgb(0 0 0 / 80%);padding:2px 7px 3px 3px;max-width:115px}.stream .right-part{position:absolute;width:225px;height:100%;top:0;right:0;padding:2px 2px 2px 3px}.stream .right-part .stream-title{color:var(--stream-element-text);font-weight:700;font-size:13px}.stream .right-part .stream-game{color:var(--stream-element-text);font-size:13px;font-style:italic}.stream .right-part .viewer-count{position:absolute;color:var(--stream-element-text);padding:2px 8px 2px 5px;background-color:var(--stream-tags-background);border:1px solid var(--stream-tags-border);border-radius:15px;right:3px;bottom:3px;font-size:14px;font-weight:700;box-sizing:border-box;transform:scale(.85)}.viewer-count .live-circle{background-color:#eb0400;width:15px;height:15px;border-radius:50%;margin-right:5px;margin-top:1px;float:left}.stream .right-part .stream-tags{position:absolute;bottom:0}.stream .right-part .stream-language,.stream .right-part .stream-mature,.stream .right-part .stream-time{color:var(--stream-element-text);background-color:var(--stream-tags-background);right:3px;bottom:4px;left:1px;font-size:12px;font-weight:700;position:relative;display:inline-block;box-sizing:border-box}.stream .right-part .stream-language,.stream .right-part .stream-time{padding:1px 7px 1px 6px;border:1px solid var(--stream-tags-border);border-radius:15px}.stream .right-part .stream-mature{padding:1px 5px;border:1px solid var(--stream-tags-border);border-radius:15px}#notifications-page{position:relative;width:100%;height:465px;top:35px;box-sizing:border-box;overflow-y:scroll;padding-left:5px}#notifications{width:100%;box-sizing:border-box;text-align:center;padding:5px;margin-top:10px}#notifications .notification{text-align:left}.notification .streamer-name{display:inline-block;font-size:16px;text-align:left;background-color:var(--main-dark-color);padding:3px 9px 4px 5px;box-sizing:border-box;max-width:170px;max-height:25px;text-overflow:ellipsis;overflow:hidden;color:var(--color-white)}.notification .followed-date{padding:1px 7px;font-size:11px}#notifications-page #notify-all-streams{padding:5px 7px;margin:5px 5px 7px;background-color:#2e3e3d;box-shadow:0 0 3px 0 var(--main-dark-color)}#notifications-page #notify-all-streams span{color:var(--color-white);font-size:15px}#notifications-page #notify-all-streams label{float:right}.all-alerts{flex-direction:column;display:flex;gap:10px;margin-top:10px}.single-alert{padding:10px}.single-alert span{font-weight:900;text-transform:uppercase;font-size:130%}.add-alert{margin-top:5px;width:100%;margin-bottom:5px}.separator{display:flex;align-items:center;text-align:center;color:#fff}.separator::after,.separator::before{content:'';flex:1;border-bottom:1px solid #fff}.separator:not(:empty)::before{margin-right:.25em}.separator:not(:empty)::after{margin-left:.25em}#disconnected-stream-tip[style*="display: block;"]+.tab,#searchbox{display:none!important}.enable-notifications.switch{width:60%;margin-bottom:12px;display:block;margin-top:10px}.add-new-alert{width:100%}
diff --git a/style/tail.css b/style/tail.css
new file mode 100644
index 0000000..b6f9940
--- /dev/null
+++ b/style/tail.css
@@ -0,0 +1,450 @@
+.tail-select,
+.tail-select *,
+.tail-select *:before,
+.tail-select *:after {
+ box-sizing: border-box;
+}
+.tail-select{
+ width: 250px;
+ margin: 1px;
+ padding: 0;
+ display: inline-block;
+ position: relative;
+ font-size: 14px;
+ line-height: 22px;
+ font-family: inherit;
+}
+.tail-select mark{
+ color: white;
+ background-color: #DC4650;
+}
+.tail-select button{
+ outline: none;
+}
+.tail-select button.tail-all, .tail-select button.tail-none{
+ height: auto;
+ margin: 0 2px;
+ padding: 2px 6px;
+ display: inline-block;
+ font-size: 10px;
+ line-height: 14px;
+ text-shadow: none;
+ letter-spacing: 0;
+ text-transform: none;
+ vertical-align: top;
+ border-width: 1px;
+ border-style: solid;
+ border-color: transparent;
+ border-radius: 3px;
+ box-shadow: none;
+ transition: color 142ms linear, border 142ms linear, background 142ms linear;
+}
+.tail-select button.tail-all{
+ color: rgba(255, 255, 255, 0.35);
+ border-color: rgba(255, 255, 255, 0.35);
+ background-color: transparent;
+}
+.tail-select button.tail-all:hover{
+ color: #62C462;
+ border-color: #62C462;
+ background-color: transparent;
+}
+.tail-select button.tail-none{
+ color: rgba(255, 255, 255, 0.35);
+ border-color: rgba(255, 255, 255, 0.35);
+ background-color: transparent;
+}
+.tail-select button.tail-none:hover{
+ color: #EE5F5B;
+ border-color: #EE5F5B;
+ background-color: transparent;
+}
+.tail-select.disabled button.tail-all{
+ color: rgba(255, 255, 255, 0.1);
+ border-color: rgba(255, 255, 255, 0.1);
+ background-color: transparent;
+}
+.tail-select.disabled button.tail-none{
+ color: rgba(255, 255, 255, 0.1);
+ border-color: rgba(255, 255, 255, 0.1);
+ background-color: transparent;
+}
+.tail-select input[type="text"]{
+ color: rgba(255, 255, 255, 0.85);
+ width: 100%;
+ height: auto;
+ margin: 0;
+ padding: 10px 15px;
+ display: inline-block;
+ outline: 0;
+ font-size: 12px;
+ line-height: 20px;
+ vertical-align: middle;
+ background-color: transparent;
+ border-width: 0;
+ border-style: solid;
+ border-color: transparent;
+ border-radius: 0;
+ box-shadow: none;
+}
+.tail-select input[type="text"]:hover{
+ color: rgba(255, 255, 255, 0.85);
+ border-color: transparent;
+ background-color: transparent;
+}
+.tail-select input[type="text"]:focus{
+ color: #3C82E6;
+ border-color: transparent;
+ background-color: transparent;
+}
+.tail-select.disabled input[type="text"]{
+ color: rgba(255, 255, 255, 0.6);
+ border-color: transparent;
+ background-color: transparent;
+}
+.tail-select-container{
+ margin: 0;
+ padding: 3px;
+ text-align: left;
+ border-radius: 3px;
+}
+.tail-select-container .select-handle{
+ width: auto;
+ color: rgba(255, 255, 255, 0.85);
+ cursor: pointer;
+ margin: 1px;
+ padding: 0.2em 0.6em 0.3em;
+ display: inline-block;
+ position: relative;
+ font-size: 11.844px;
+ text-align: left;
+ font-weight: bold;
+ line-height: 16px;
+ text-shadow: none;
+ vertical-align: top;
+ background-color: #000000;
+ border-width: 0;
+ border-style: solid;
+ border-color: transparent;
+ border-radius: 3px;
+ transition: background 142ms linear;
+}
+.tail-select-container .select-handle:hover{
+ color: rgba(255, 255, 255, 0.85);
+ background-color: #DC4650;
+}
+.tail-select-container.select-label .select-handle{
+ margin: 5px 3px;
+}
+/* @end GENERAL */
+
+/* @start LABEL */
+.tail-select .select-label{
+ cursor: pointer;
+ color: rgba(255, 255, 255, 0.7);
+ width: 100%;
+ margin: 0;
+ padding: 5px 30px 5px 10px;
+ display: block;
+ z-index: 27;
+ position: relative;
+ text-align: left;
+ background-color: #202428;
+ border-width: 0px;
+ border-style: solid;
+ border-color: #101418;
+ border-radius: 3px;
+ box-shadow: 0 0 0 1px #000408, inset 0 0 1px 1px #404448;
+ transition: background 142ms linear, box-shadow 142ms linear;
+}
+.tail-select .select-label:after{
+ top: 15px;
+ right: 12px;
+ width: 0;
+ height: 0;
+ margin: 0;
+ padding: 0;
+ z-index: 25;
+ content: "";
+ opacity: 0.5;
+ display: inline-block;
+ position: absolute;
+ border-top: 5px dashed;
+ border-top: 5px solid\9;
+ border-right: 5px solid transparent;
+ border-left: 5px solid transparent;
+ text-shadow: none;
+ transition: opacity 142ms linear, transform 142ms linear;
+}
+.tail-select .select-label .label-count, .tail-select .select-label .label-inner{
+ width: auto;
+ margin: 0;
+ text-align: left;
+ vertical-align: top;
+}
+.tail-select .select-label .label-count{
+ float: left;
+ color: rgba(255, 255, 255, 0.7);
+ margin: 0 5px 0 0;
+ padding: 0 7px 0 0;
+ display: inline-block;
+ font-size: 11.844px;
+ font-weight: bold;
+ text-shadow: none;
+ white-space: nowrap;
+ vertical-align: top;
+ border-width: 0px 1px 0 0;
+ border-style: solid;
+ border-color: #101418;
+ border-radius: 0px;
+}
+.tail-select .select-label .label-inner{
+ display: block;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+.tail-select:hover .select-label, .tail-select.idle .select-label,
+.tail-select.active .select-label{
+ z-index: 25;
+ box-shadow: 0 0 0 1px #000408, inset 0 0 1px 1px #3C82E6;
+}
+.tail-select:hover .select-label:after, .tail-select.idle .select-label:after,
+.tail-select.active .select-label:after{
+ opacity: 0.85;
+}
+.tail-select:hover .select-label .label-count, .tail-select.idle .select-label .label-count,
+.tail-select.active .select-label .label-count, .tail-select:hover .select-label .label-inner,
+.tail-select.idle .select-label .label-inner, .tail-select.active .select-label .label-inner{
+ opacity: 1;
+}
+.tail-select.active .select-label{
+ z-index: 27;
+}
+.tail-select.active .select-label:after{
+ opacity: 0.85;
+ transform: rotate(180deg);
+}
+.tail-select.disabled .select-label{
+ cursor: not-allowed;
+ box-shadow: 0 0 0 1px #000408, inset 0 0 1px 1px #404448;
+}
+/* @end LABEL */
+
+/* @start DROPDOWN */
+.tail-select .select-dropdown{
+ top: 100%;
+ left: 0;
+ color: rgba(255, 255, 255, 0.85);
+ width: 100%;
+ min-height: 35px;
+ margin: -1px 0 0 0;
+ padding: 0;
+ z-index: 30;
+ display: none;
+ overflow: hidden;
+ position: absolute;
+ background-color: #202428;
+ border-width: 0px;
+ border-style: solid;
+ border-color: #101418;
+ border-radius: 0 0 3px 3px;
+ box-shadow: 0 0 0 1px #000408, inset 0 0 1px 1px #404448;
+}
+.tail-select .select-dropdown .dropdown-search{
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ display: block;
+ position: relative;
+ border-width: 0 0 1px 0;
+ border-style: solid;
+ border-color: #101418;
+ box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
+}
+.tail-select .select-dropdown .dropdown-inner{
+ width: 100%;
+ margin: 0;
+ padding: 1px 0;
+ display: block;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+.tail-select .select-dropdown .dropdown-empty{
+ margin: 0;
+ padding: 16px 0;
+ display: block;
+ font-size: 12px;
+ text-align: center;
+ line-height: 18px;
+}
+.tail-select .select-dropdown .dropdown-action{
+ top: 3px;
+ right: 15px;
+ width: auto;
+ margin: 0;
+ padding: 7px 0;
+ z-index: 35;
+ display: inline-block;
+ position: absolute;
+ text-align: center;
+}
+.tail-select .select-dropdown ul, .tail-select .select-dropdown ul li{
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ display: block;
+ position: relative;
+ list-style: none;
+ font-size: 14px;
+ line-height: 20px;
+ vertical-align: top;
+}
+.tail-select .select-dropdown ul li{
+ color: rgba(255, 255, 255, 0.85);
+ padding: 5px 10px 5px 35px;
+ font-size: 12px;
+ text-align: left;
+ line-height: 18px;
+ font-weight: normal;
+}
+.tail-select .select-dropdown ul li:first-of-type{
+ margin-top: 7px;
+}
+.tail-select .select-dropdown ul li:last-of-type{
+ margin-bottom: 7px;
+}
+.tail-select .select-dropdown ul li.optgroup-title{
+ color: rgba(255, 255, 255, 0.55);
+ cursor: default;
+ margin: 9px 0 0 0;
+ padding-left: 10px;
+ font-size: 14px;
+ text-shadow: none;
+}
+.tail-select .select-dropdown ul li.optgroup-title button{
+ float: right;
+ margin-top: -2px;
+ opacity: 0;
+}
+.tail-select .select-dropdown ul:hover li button{
+ opacity: 1;
+}
+.tail-select .select-dropdown ul li.dropdown-option{
+ cursor: pointer;
+ color: rgba(255, 255, 255, 0.85);
+}
+.tail-select .select-dropdown ul li.dropdown-option:before{
+ top: 0;
+ left: 0;
+ width: 30px;
+ height: 30px;
+ margin: 0;
+ padding: 0;
+ z-index: 21;
+ display: inline-block;
+ content: "";
+ opacity: 0;
+ position: absolute;
+ vertical-align: top;
+ background-repeat: no-repeat;
+ background-position: center center;
+ transition: opacity 50ms linear;
+}
+.tail-select .select-dropdown ul li.dropdown-option .option-description{
+ color: rgba(255, 255, 255, 0.7);
+ width: auto;
+ margin: 0;
+ padding: 0;
+ display: block;
+ font-size: 10px;
+ text-align: left;
+ line-height: 14px;
+ vertical-align: top;
+}
+.tail-select .select-dropdown ul li.dropdown-option:hover,
+.tail-select .select-dropdown ul li.dropdown-option.hover{
+ color: rgba(255, 255, 255, 0.85);
+}
+.tail-select .select-dropdown ul li.dropdown-option:hover .option-description,
+.tail-select .select-dropdown ul li.dropdown-option.hover .option-description{
+ color: rgba(255, 255, 255, 0.85);
+}
+.tail-select.open-top .select-dropdown{
+ top: auto;
+ bottom: 100%;
+ margin: 0 0 -1px 0;
+ border-radius: 3px 3px 0 0;
+}
+.tail-select.hide-selected .select-dropdown ul li.selected,
+.tail-select.hide-disabled .select-dropdown ul li.disabled{
+ display: none;
+}
+/* State & Icons :: Single */
+.tail-select .select-dropdown ul li.dropdown-option:before{
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2218%22%20height%3D%2218%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22rgba%28255,%20255,%20255,%200.85%29%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M8%203v3a2%202%200%200%201-2%202H3m18%200h-3a2%202%200%200%201-2-2V3m0%2018v-3a2%202%200%200%201%202-2h3M3%2016h3a2%202%200%200%201%202%202v3%22%3E%3C/path%3E%3C/svg%3E");
+}
+.tail-select .select-dropdown ul li.dropdown-option:hover:before,
+.tail-select .select-dropdown ul li.dropdown-option.hover:before{
+ opacity: 0.5;
+}
+.tail-select .select-dropdown ul li.dropdown-option.selected{
+ color: #3C82E6;
+ background-color: #202428;
+}
+.tail-select .select-dropdown ul li.dropdown-option.selected:before{
+ opacity: 0.85;
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2218%22%20height%3D%2218%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22rgba%28255,%20255,%20255,%200.85%29%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpolyline%20points%3D%2220%206%209%2017%204%2012%22%3E%3C/polyline%3E%3C/svg%3E");
+}
+.tail-select .select-dropdown ul li.dropdown-option.selected .option-description{
+ color: #3C82E6;
+}
+.tail-select.deselect .select-dropdown ul li.dropdown-option.selected:hover:before,
+.tail-select.deselect .select-dropdown ul li.dropdown-option.selected.hover:before{
+ opacity: 0.85;
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2218%22%20height%3D%2218%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22rgba%28255,%20255,%20255,%200.85%29%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cline%20x1%3D%2218%22%20y1%3D%226%22%20x2%3D%226%22%20y2%3D%2218%22%3E%3C/line%3E%3Cline%20x1%3D%226%22%20y1%3D%226%22%20x2%3D%2218%22%20y2%3D%2218%22%3E%3C/line%3E%3C/svg%3E");
+}
+.tail-select.deselect .select-dropdown ul li.dropdown-option.selected:hover .option-description,
+.tail-select.deselect .select-dropdown ul li.dropdown-option.selected.hover .option-description{
+ color: #3C82E6;
+}
+.tail-select .select-dropdown ul li.dropdown-option{
+ transition: all 0.3s ease-in;
+}
+.tail-select .select-dropdown ul li.dropdown-option:hover,
+.tail-select .select-dropdown ul li.dropdown-option.hover{
+ transition: all 0.4s ease;
+ background-color: #171d24;
+}
+.tail-select.disabled .select-dropdown ul li.dropdown-option,
+.tail-select .select-dropdown ul li.dropdown-option.disabled{
+ cursor: not-allowed;
+ color: rgba(255, 255, 255, 0.25);
+ text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.1), 0px -1px 0px rgba(179, 179, 179, 0.1);
+ background-color: #2a2a2a;
+}
+.tail-select.disabled .select-dropdown ul li.dropdown-option:before,
+.tail-select .select-dropdown ul li.dropdown-option.disabled:before{
+ opacity: 0.85;
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2218%22%20height%3D%2218%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22rgba%28255,%20255,%20255,%200.85%29%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M8%203H5a2%202%200%200%200-2%202v3m18%200V5a2%202%200%200%200-2-2h-3m0%2018h3a2%202%200%200%200%202-2v-3M3%2016v3a2%202%200%200%200%202%202h3%22%3E%3C/path%3E%3C/svg%3E");
+}
+.tail-select.disabled .select-dropdown ul li.dropdown-option .option-description,
+.tail-select .select-dropdown ul li.dropdown-option.disabled .option-description{
+ color: rgba(255, 255, 255, 0.25);
+}
+/* State & Icons :: Multiple */
+.tail-select.multiple .select-dropdown ul li.dropdown-option:before{
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2218%22%20height%3D%2218%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22rgba%28255,%20255,%20255,%200.85%29%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%3E%3C/circle%3E%3Cline%20x1%3D%2212%22%20y1%3D%228%22%20x2%3D%2212%22%20y2%3D%2216%22%3E%3C/line%3E%3Cline%20x1%3D%228%22%20y1%3D%2212%22%20x2%3D%2216%22%20y2%3D%2212%22%3E%3C/line%3E%3C/svg%3E");
+}
+.tail-select.multiple .select-dropdown ul li.dropdown-option.selected:before{
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2218%22%20height%3D%2218%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22rgba%28255,%20255,%20255,%200.85%29%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M22%2011.08V12a10%2010%200%201%201-5.93-9.14%22%3E%3C/path%3E%3Cpolyline%20points%3D%2222%204%2012%2014.01%209%2011.01%22%3E%3C/polyline%3E%3C/svg%3E");
+}
+.tail-select.multiple .select-dropdown ul li.dropdown-option.selected:hover:before,
+.tail-select.multiple .select-dropdown ul li.dropdown-option.selected.hover:before{
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2218%22%20height%3D%2218%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22rgba%28255,%20255,%20255,%200.85%29%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%3E%3C/circle%3E%3Cline%20x1%3D%228%22%20y1%3D%2212%22%20x2%3D%2216%22%20y2%3D%2212%22%3E%3C/line%3E%3C/svg%3E");
+}
+.tail-select.multiple.disabled .select-dropdown ul li.dropdown-option:before,
+.tail-select.multiple .select-dropdown ul li.dropdown-option.disabled:before{
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2218%22%20height%3D%2218%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22rgba%28255,%20255,%20255,%200.85%29%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%3E%3C/circle%3E%3C/svg%3E");
+}
+/* @end DROPDOWN */
\ No newline at end of file