diff --git a/Chrome Extension/script.js b/Chrome Extension/script.js index 87dba66b..36ead7b2 100644 --- a/Chrome Extension/script.js +++ b/Chrome Extension/script.js @@ -18,7 +18,7 @@ function ffz_init() // we include the script, otherwise someone could break their // experience and not be able to recover. var xhr = new XMLHttpRequest(); - xhr.open("GET", "http://localhost:8000/dev_server", true); + xhr.open("GET", "//localhost:8000/dev_server", true); xhr.onload = function(e) { var resp = JSON.parse(xhr.responseText); console.log("FFZ: Development Server is present. Version " + resp.version + " running from: " + resp.path); diff --git a/script.js b/script.js index 713570f2..60713251 100644 --- a/script.js +++ b/script.js @@ -147,12 +147,12 @@ FFZ.prototype.render_badge = function(view) { // -------------------- FFZ.prototype._legacy_add_donors = function(tries) { - this.badges[1] = {id: 1, title: "FFZ Donor", color: "#755000", image: "http://cdn.frankerfacez.com/channel/global/donoricon.png"}; + this.badges[1] = {id: 1, title: "FFZ Donor", color: "#755000", image: "//cdn.frankerfacez.com/channel/global/donoricon.png"}; utils.update_css(this._badge_style, 1, badge_css(this.badges[1])); // Developer Badges // TODO: Upload the badge to the proper CDN. - this.badges[0] = {id: 0, title: "FFZ Developer", color: "#FAAF19", image: "http://sir.stendec.me/devicon.png"}; + this.badges[0] = {id: 0, title: "FFZ Developer", color: "#FAAF19", image: "//cdn.frankerfacez.com/channel/global/devicon.png"}; utils.update_css(this._badge_style, 0, badge_css(this.badges[0])); this.users.sirstendec = {badges: {0: {id:0}}}; @@ -190,141 +190,12 @@ FFZ.prototype._legacy_parse_donors = function(data) { this.log("Added donor badge to " + utils.number_commas(count) + " users."); } -},{"./constants":4,"./utils":22}],2:[function(require,module,exports){ -var FFZ = window.FrankerFaceZ, - SENDER_REGEX = /(\sdata-sender="[^"]*"(?=>))/; - - -// -------------------- -// Initialization -// -------------------- - -FFZ.prototype.find_bttv = function(increment, delay) { - this.has_bttv = false; - if ( window.BTTVLOADED ) - return this.setup_bttv(); - - if ( delay >= 60000 ) - this.log("BetterTTV was not detected after 60 seconds."); - else - setTimeout(this.find_bttv.bind(this, increment, (delay||0) + increment), - increment); -} - - -FFZ.prototype.setup_bttv = function() { - this.log("BetterTTV was detected. Hooking."); - this.has_bttv = true; - - this.track('setCustomVariable', '3', 'BetterTTV', BetterTTV.info.versionString()); - - // Send Message Behavior - var original_send = BetterTTV.chat.helpers.sendMessage, f = this; - BetterTTV.chat.helpers.sendMessage = function(message) { - var cmd = message.split(' ', 1)[0].toLowerCase(); - - if ( cmd === "/ffz" ) - f.run_command(message.substr(5), BetterTTV.chat.store.currentRoom); - else - return original_send(message); - } - - - // Ugly Hack for Current Room - var original_handler = BetterTTV.chat.handlers.privmsg, - received_room; - BetterTTV.chat.handlers.privmsg = function(room, data) { - received_room = room; - var output = original_handler(room, data); - received_room = null; - return output; - } - - - // Message Display Behavior - var original_privmsg = BetterTTV.chat.templates.privmsg; - BetterTTV.chat.templates.privmsg = function(highlight, action, server, isMod, data) { - // Handle badges. - f.bttv_badges(data); - - var output = original_privmsg(highlight, action, server, isMod, data); - return output.replace(SENDER_REGEX, '$1 data-room="' + received_room + '"'); - } - - - // Ugly Hack for Current Sender - var original_template = BetterTTV.chat.templates.message, - received_sender; - BetterTTV.chat.templates.message = function(sender, message, emotes, colored) { - received_sender = sender; - var output = original_template(sender, message, emotes, colored); - received_sender = null; - return output; - } - - - // Emoticonize - var original_emoticonize = BetterTTV.chat.templates.emoticonize; - BetterTTV.chat.templates.emoticonize = function(message, emotes) { - var tokens = original_emoticonize(message, emotes), - sets = f.getEmotes(received_sender, received_room), - emotes = []; - - // Build a list of emotes that match. - _.each(sets, function(set_id) { - var set = f.emote_sets[set_id]; - if ( ! set ) - return; - - _.each(set.emotes, function(emote) { - _.any(tokens, function(token) { - return _.isString(token) && token.match(emote.regex); - }) && emotes.push(emote); - }); - }); - - // Don't bother proceeding if we have no emotes. - if ( ! emotes.length ) - return tokens; - - // Why is emote parsing so bad? ;_; - _.each(emotes, function(emote) { - var eo = ['' + emote.name + ''], - old_tokens = tokens; - - tokens = []; - - if ( ! old_tokens || ! old_tokens.length ) - return tokens; - - for(var i=0; i < old_tokens.length; i++) { - var token = old_tokens[i]; - if ( typeof token != "string" ) { - tokens.push(token); - continue; - } - - var tbits = token.split(emote.regex); - tbits.forEach(function(val, ind) { - if ( val && val.length ) - tokens.push(val); - - if ( ind !== tbits.length - 1 ) - tokens.push(eo); - }); - } - }); - - return tokens; - } - - this.update_ui_link(); -} -},{}],3:[function(require,module,exports){ +},{"./constants":3,"./utils":23}],2:[function(require,module,exports){ var FFZ = window.FrankerFaceZ; + // ----------------- -// Mass Unmod +// Mass Moderation // ----------------- FFZ.chat_commands.massunmod = function(room, args) { @@ -380,7 +251,7 @@ FFZ.chat_commands.massmod = function(room, args) { } FFZ.chat_commands.massmod.help = "Usage: /ffz massmod \nBroadcaster only. Mod all the users in the provided list."; -},{}],4:[function(require,module,exports){ +},{}],3:[function(require,module,exports){ var SVGPATH = '', DEBUG = localStorage.ffzDebugMode == "true" && document.body.classList.contains('ffz-dev'); @@ -392,7 +263,7 @@ module.exports = { ZREKNARF: '' + SVGPATH + '', CHAT_BUTTON: '' + SVGPATH + '' } -},{}],5:[function(require,module,exports){ +},{}],4:[function(require,module,exports){ var FFZ = window.FrankerFaceZ; @@ -416,7 +287,7 @@ FFZ.chat_commands.developer_mode = function(room, args) { FFZ.chat_commands.developer_mode.help = "Usage: /ffz developer_mode \nEnable or disable Developer Mode. When Developer Mode is enabled, the script will be reloaded from //localhost:8000/script.js instead of from the CDN."; -},{}],6:[function(require,module,exports){ +},{}],5:[function(require,module,exports){ var FFZ = window.FrankerFaceZ; @@ -472,7 +343,7 @@ FFZ.prototype._modify_cview = function(view) { }) }); } -},{}],7:[function(require,module,exports){ +},{}],6:[function(require,module,exports){ var FFZ = window.FrankerFaceZ; @@ -516,6 +387,11 @@ FFZ.prototype.setup_line = function() { } }); + + // Store the capitalization of our own name. + var user = this.get_user(); + if ( user && user.name ) + FFZ.capitalization[user.login] = [user.name, Date.now()]; } @@ -528,6 +404,9 @@ FFZ._cap_fetching = 0; FFZ.get_capitalization = function(name, callback) { name = name.toLowerCase(); + if ( name == "jtv" || name == "twitchnotify" ) + return name; + var old_data = FFZ.capitalization[name]; if ( old_data ) { if ( Date.now() - old_data[1] < 3600000 ) @@ -632,7 +511,7 @@ FFZ.prototype._emoticonize = function(controller, tokens) { return tokens; } -},{}],8:[function(require,module,exports){ +},{}],7:[function(require,module,exports){ var FFZ = window.FrankerFaceZ, CSS = /\.([\w\-_]+)\s*?\{content:\s*?"([^"]+)";\s*?background-image:\s*?url\("([^"]+)"\);\s*?height:\s*?(\d+)px;\s*?width:\s*?(\d+)px;\s*?margin:([^;}]+);?([^}]*)\}/mg, MOD_CSS = /[^\n}]*\.badges\s+\.moderator\s*{\s*background-image:\s*url\(\s*['"]([^'"]+)['"][^}]+(?:}|$)/, @@ -914,7 +793,7 @@ FFZ.prototype._legacy_load_room_css = function(room_id, callback, data) { output.css = data || null; return this._load_room_json(room_id, callback, output); } -},{"../constants":4,"../utils":22}],9:[function(require,module,exports){ +},{"../constants":3,"../utils":23}],8:[function(require,module,exports){ var FFZ = window.FrankerFaceZ; @@ -932,7 +811,7 @@ FFZ.prototype.setup_router = function() { }.on('didTransition') }); } -},{}],10:[function(require,module,exports){ +},{}],9:[function(require,module,exports){ var FFZ = window.FrankerFaceZ; @@ -1025,9 +904,10 @@ FFZ.prototype._modify_viewers = function(controller) { }.property("content.chatters") }); } -},{}],11:[function(require,module,exports){ +},{}],10:[function(require,module,exports){ var FFZ = window.FrankerFaceZ, CSS = /\.([\w\-_]+)\s*?\{content:\s*?"([^"]+)";\s*?background-image:\s*?url\("([^"]+)"\);\s*?height:\s*?(\d+)px;\s*?width:\s*?(\d+)px;\s*?margin:([^;}]+);?([^}]*)\}/mg, + MOD_CSS = /[^\n}]*\.badges\s+\.moderator\s*{\s*background-image:\s*url\(\s*['"]([^'"]+)['"][^}]+(?:}|$)/, constants = require('./constants'), utils = require('./utils'), @@ -1201,7 +1081,7 @@ FFZ.prototype._legacy_load_set = function(set_id, callback, tries) { FFZ.prototype._legacy_load_css = function(set_id, callback, data) { var emotes = {}, output = {id: set_id, emotes: emotes, extra_css: null}, f = this; - data.replace(CSS, function(match, klass, name, path, height, width, margins, extra) { + data = data.replace(CSS, function(match, klass, name, path, height, width, margins, extra) { height = parseInt(height); width = parseInt(width); margins = check_margins(margins, height); var hidden = path.substr(path.lastIndexOf("/") + 1, 1) === ".", @@ -1210,11 +1090,224 @@ FFZ.prototype._legacy_load_css = function(set_id, callback, data) { emotes[id] = emote; return ""; - }); + }).trim(); + + if ( data ) + data.replace(MOD_CSS, function(match, url) { + if ( output.icon || url.substr(-11) !== 'modicon.png' ) + return; + + output.icon = url; + }); this._load_set_json(set_id, callback, output); } -},{"./constants":4,"./utils":22}],12:[function(require,module,exports){ +},{"./constants":3,"./utils":23}],11:[function(require,module,exports){ +var FFZ = window.FrankerFaceZ, + SENDER_REGEX = /(\sdata-sender="[^"]*"(?=>))/; + + +// -------------------- +// Initialization +// -------------------- + +FFZ.prototype.find_bttv = function(increment, delay) { + this.has_bttv = false; + if ( window.BTTVLOADED ) + return this.setup_bttv(delay||0); + + if ( delay >= 60000 ) + this.log("BetterTTV was not detected after 60 seconds."); + else + setTimeout(this.find_bttv.bind(this, increment, (delay||0) + increment), + increment); +} + + +FFZ.prototype.setup_bttv = function(delay) { + this.log("BetterTTV was detected after " + delay + "ms. Hooking."); + this.has_bttv = true; + + this.track('setCustomVariable', '3', 'BetterTTV', BetterTTV.info.versionString()); + + // Send Message Behavior + var original_send = BetterTTV.chat.helpers.sendMessage, f = this; + BetterTTV.chat.helpers.sendMessage = function(message) { + var cmd = message.split(' ', 1)[0].toLowerCase(); + + if ( cmd === "/ffz" ) + f.run_command(message.substr(5), BetterTTV.chat.store.currentRoom); + else + return original_send(message); + } + + + // Ugly Hack for Current Room + var original_handler = BetterTTV.chat.handlers.privmsg, + received_room; + BetterTTV.chat.handlers.privmsg = function(room, data) { + received_room = room; + var output = original_handler(room, data); + received_room = null; + return output; + } + + + // Message Display Behavior + var original_privmsg = BetterTTV.chat.templates.privmsg; + BetterTTV.chat.templates.privmsg = function(highlight, action, server, isMod, data) { + // Handle badges. + f.bttv_badges(data); + + var output = original_privmsg(highlight, action, server, isMod, data); + return output.replace(SENDER_REGEX, '$1 data-room="' + received_room + '"'); + } + + + // Ugly Hack for Current Sender + var original_template = BetterTTV.chat.templates.message, + received_sender; + BetterTTV.chat.templates.message = function(sender, message, emotes, colored) { + received_sender = sender; + var output = original_template(sender, message, emotes, colored); + received_sender = null; + return output; + } + + + // Emoticonize + var original_emoticonize = BetterTTV.chat.templates.emoticonize; + BetterTTV.chat.templates.emoticonize = function(message, emotes) { + var tokens = original_emoticonize(message, emotes), + sets = f.getEmotes(received_sender, received_room), + emotes = []; + + // Build a list of emotes that match. + _.each(sets, function(set_id) { + var set = f.emote_sets[set_id]; + if ( ! set ) + return; + + _.each(set.emotes, function(emote) { + _.any(tokens, function(token) { + return _.isString(token) && token.match(emote.regex); + }) && emotes.push(emote); + }); + }); + + // Don't bother proceeding if we have no emotes. + if ( ! emotes.length ) + return tokens; + + // Why is emote parsing so bad? ;_; + _.each(emotes, function(emote) { + var eo = ['' + emote.name + ''], + old_tokens = tokens; + + tokens = []; + + if ( ! old_tokens || ! old_tokens.length ) + return tokens; + + for(var i=0; i < old_tokens.length; i++) { + var token = old_tokens[i]; + if ( typeof token != "string" ) { + tokens.push(token); + continue; + } + + var tbits = token.split(emote.regex); + tbits.forEach(function(val, ind) { + if ( val && val.length ) + tokens.push(val); + + if ( ind !== tbits.length - 1 ) + tokens.push(eo); + }); + } + }); + + return tokens; + } + + this.update_ui_link(); +} +},{}],12:[function(require,module,exports){ +var FFZ = window.FrankerFaceZ; + + +// -------------------- +// Initialization +// -------------------- + +FFZ.prototype.find_emote_menu = function(increment, delay) { + this.has_emote_menu = false; + if ( window.emoteMenu && emoteMenu.registerEmoteGetter ) + return this.setup_emote_menu(delay||0); + + if ( delay >= 60000 ) + this.log("Emote Menu for Twitch was not detected after 60 seconds."); + else + setTimeout(this.find_emote_menu.bind(this, increment, (delay||0) + increment), + increment); +} + + +FFZ.prototype.setup_emote_menu = function(delay) { + this.log("Emote Menu for Twitch was detected after " + delay + "ms. Registering emote enumerator."); + emoteMenu.registerEmoteGetter("FrankerFaceZ", this._emote_menu_enumerator.bind(this)); +} + + +// -------------------- +// Emote Enumerator +// -------------------- + +FFZ.prototype._emote_menu_enumerator = function() { + var twitch_user = this.get_user(), + user_id = twitch_user ? twitch_user.login : null, + controller = App.__container__.lookup('controller:chat'), + room_id = controller ? controller.get('currentRoom.id') : null, + sets = this.getEmotes(user_id, room_id), + emotes = []; + + for(var x = 0; x < sets.length; x++) { + var set = this.emote_sets[sets[x]]; + if ( ! set || ! set.emotes ) + continue; + + for(var emote_id in set.emotes) { + if ( ! set.emotes.hasOwnProperty(emote_id) ) + continue; + + var emote = set.emotes[emote_id]; + if ( emote.hidden ) + continue; + + // TODO: Stop having to calculate this here. + var title = set.title, badge = set.icon || null; + if ( ! title ) { + if ( set.id == "global" ) + title = "FrankerFaceZ Global Emotes"; + + else if ( set.id == "globalevent" ) + title = "FrankerFaceZ Event Emotes"; + + else if ( this.feature_friday && set.id == this.feature_friday.set ) + title = "FrankerFaceZ Feature Friday: " + this.feature_friday.channel; + + else + title = "FrankerFaceZ Set: " + FFZ.get_capitalization(set.id); + } + + emotes.push({text: emote.name, url: emote.url, + hidden: false, channel: title, badge: badge}); + } + } + + return emotes; +} +},{}],13:[function(require,module,exports){ // Modify Array and others. require('./shims'); @@ -1287,12 +1380,14 @@ require('./ember/room'); require('./ember/line'); require('./ember/chatview'); require('./ember/viewers'); +//require('./ember/teams'); require('./tracking'); require('./debug'); -require('./betterttv'); +require('./ext/betterttv'); +require('./ext/emote_menu'); require('./featurefriday'); @@ -1330,11 +1425,11 @@ FFZ.prototype.initialize = function(increment, delay) { return; } - this.setup(delay); + this.setup_ember(delay); } -FFZ.prototype.setup = function(delay) { +FFZ.prototype.setup_ember = function(delay) { var start = (window.performance && performance.now) ? performance.now() : Date.now(); this.log("Found Twitch application after " + (delay||0) + " ms in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info); @@ -1346,47 +1441,35 @@ FFZ.prototype.setup = function(delay) { localStorage.removeItem(key); } - // Store the capitalization of our own name. - var user = this.get_user(); - if ( user && user.name ) - FFZ.capitalization[user.login] = [user.name, Date.now()]; - - // Initialize all the modules. - try { - this.ws_create(); - this.setup_emoticons(); - this.setup_badges(); + this.ws_create(); + this.setup_emoticons(); + this.setup_badges(); - this.setup_piwik(); + this.setup_piwik(); - this.setup_router(); - this.setup_room(); - this.setup_line(); - this.setup_chatview(); - this.setup_viewers(); + this.setup_router(); + this.setup_room(); + this.setup_line(); + this.setup_chatview(); + this.setup_viewers(); - this.setup_css(); - this.setup_menu(); + //this.setup_teams(); - this.find_bttv(10); + this.setup_css(); + this.setup_menu(); - this.check_ff(); + this.find_bttv(10); + this.find_emote_menu(10); - } catch(err) { - this.log("An error occurred while starting FrankerFaceZ: " + err); - return; - } - - if ( window.console && console.time ) - console.timeEnd("FrankerFaceZ Initialization"); + this.check_ff(); var end = (window.performance && performance.now) ? performance.now() : Date.now(), duration = end - start; this.log("Initialization complete in " + duration + "ms"); } -},{"./badges":1,"./betterttv":2,"./commands":3,"./debug":5,"./ember/chatview":6,"./ember/line":7,"./ember/room":8,"./ember/router":9,"./ember/viewers":10,"./emoticons":11,"./featurefriday":13,"./shims":14,"./socket":15,"./tracking":16,"./ui/menu":17,"./ui/menu_button":18,"./ui/notifications":19,"./ui/styles":20,"./ui/viewer_count":21}],13:[function(require,module,exports){ +},{"./badges":1,"./commands":2,"./debug":4,"./ember/chatview":5,"./ember/line":6,"./ember/room":7,"./ember/router":8,"./ember/viewers":9,"./emoticons":10,"./ext/betterttv":11,"./ext/emote_menu":12,"./featurefriday":14,"./shims":15,"./socket":16,"./tracking":17,"./ui/menu":18,"./ui/menu_button":19,"./ui/notifications":20,"./ui/styles":21,"./ui/viewer_count":22}],14:[function(require,module,exports){ var FFZ = window.FrankerFaceZ, constants = require('./constants'); @@ -1533,7 +1616,7 @@ FFZ.prototype._update_ff_name = function(name) { if ( this.feature_friday ) this.feature_friday.display_name = name; } -},{"./constants":4}],14:[function(require,module,exports){ +},{"./constants":3}],15:[function(require,module,exports){ Array.prototype.equals = function (array) { // if the other array is a falsy value, return if (!array) @@ -1559,7 +1642,7 @@ Array.prototype.equals = function (array) { } -},{}],15:[function(require,module,exports){ +},{}],16:[function(require,module,exports){ var FFZ = window.FrankerFaceZ; FFZ.prototype._ws_open = false; @@ -1577,6 +1660,7 @@ FFZ.prototype.ws_create = function() { this._ws_last_req = 0; this._ws_callbacks = {}; + this._ws_pending = this._ws_pending || []; var ws = this._ws_sock = new WebSocket("ws://ffz.stendec.me/"); @@ -1592,6 +1676,15 @@ FFZ.prototype.ws_create = function() { // Send the current rooms. for(var room_id in f.rooms) f.ws_send("sub", room_id); + + // Send any pending commands. + var pending = f._ws_pending; + f._ws_pending = []; + + for(var i=0; i < pending.length; i++) { + var d = pending[i]; + f.ws_send(d[0], d[1], d[2]); + } } ws.onclose = function(e) { @@ -1641,8 +1734,15 @@ FFZ.prototype.ws_create = function() { } -FFZ.prototype.ws_send = function(func, data, callback) { - if ( ! this._ws_open ) return false; +FFZ.prototype.ws_send = function(func, data, callback, can_wait) { + if ( ! this._ws_open ) { + if ( can_wait ) { + var pending = this._ws_pending = this._ws_pending || []; + pending.push([func, data, callback]); + return true; + } else + return false; + } var request = ++this._ws_last_req; data = data !== undefined ? " " + JSON.stringify(data) : ""; @@ -1653,7 +1753,7 @@ FFZ.prototype.ws_send = function(func, data, callback) { this._ws_sock.send(request + " " + func + data); return request; } -},{}],16:[function(require,module,exports){ +},{}],17:[function(require,module,exports){ var FFZ = window.FrankerFaceZ, constants = require('./constants'), PIWIK = ("https:" == document.location.protocol ? 'https:' : 'http:') + '//sir.stendec.me/ffz_piwik/'; @@ -1785,7 +1885,7 @@ FFZ.prototype.track_page = function() { this.track("trackPageView", document.title); } } -},{"./constants":4}],17:[function(require,module,exports){ +},{"./constants":3}],18:[function(require,module,exports){ var FFZ = window.FrankerFaceZ; @@ -1929,7 +2029,7 @@ FFZ.prototype._add_emote = function(view, emote) { room.set('messageToSend', current_text + (emote.name || emote)); } -},{}],18:[function(require,module,exports){ +},{}],19:[function(require,module,exports){ var FFZ = window.FrankerFaceZ, constants = require('../constants'); @@ -1952,7 +2052,8 @@ FFZ.prototype.build_ui_link = function(view) { FFZ.prototype.update_ui_link = function(link) { var controller = App.__container__.lookup('controller:chat'); link = link || document.querySelector('a.ffz-ui-toggle'); - if ( !link || !controller ) return this.log("No button."); + if ( !link || !controller ) + return; var room_id = controller.get('currentRoom.id'), room = this.rooms[room_id], @@ -1979,7 +2080,7 @@ FFZ.prototype.update_ui_link = function(link) { link.classList.toggle('dark', dark); link.classList.toggle('blue', blue); } -},{"../constants":4}],19:[function(require,module,exports){ +},{"../constants":3}],20:[function(require,module,exports){ var FFZ = window.FrankerFaceZ; FFZ.prototype.show_notification = function(message) { @@ -1995,7 +2096,7 @@ FFZ.prototype.show_notification = function(message) { FFZ.ws_commands.message = function(message) { this.show_notification(message); } -},{}],20:[function(require,module,exports){ +},{}],21:[function(require,module,exports){ var FFZ = window.FrankerFaceZ, constants = require('../constants'); @@ -2020,7 +2121,7 @@ FFZ.prototype.setup_css = function() { } }; } -},{"../constants":4}],21:[function(require,module,exports){ +},{"../constants":3}],22:[function(require,module,exports){ var FFZ = window.FrankerFaceZ, constants = require('../constants'), utils = require('../utils'); @@ -2057,7 +2158,7 @@ FFZ.ws_commands.viewers = function(data) { jQuery(view_count).tipsy(); } } -},{"../constants":4,"../utils":22}],22:[function(require,module,exports){ +},{"../constants":3,"../utils":23}],23:[function(require,module,exports){ var FFZ = window.FrankerFaceZ, constants = require('./constants'); @@ -2088,4 +2189,4 @@ module.exports = { return parts.join("."); } } -},{"./constants":4}]},{},[12]);window.ffz = new FrankerFaceZ()}(window)); \ No newline at end of file +},{"./constants":3}]},{},[13]);window.ffz = new FrankerFaceZ()}(window)); \ No newline at end of file diff --git a/script.min.js b/script.min.js index 33d138f7..4ec32ff2 100644 --- a/script.min.js +++ b/script.min.js @@ -1,2 +1,2 @@ -!function(e){!function t(e,o,s){function n(r,i){if(!o[r]){if(!e[r]){var l="function"==typeof require&&require;if(!i&&l)return l(r,!0);if(a)return a(r,!0);throw new Error("Cannot find module '"+r+"'")}var c=o[r]={exports:{}};e[r][0].call(c.exports,function(t){var o=e[r][1][t];return n(o?o:t)},c,c.exports,t,e,o,s)}return o[r].exports}for(var a="function"==typeof require&&require,r=0;re?this._legacy_add_donors(e):void 0):void 0})},o.prototype._legacy_parse_donors=function(e){var t=0;if(null!=e)for(var o=e.trim().split(/\W+/),s=0;s))/;t.prototype.find_bttv=function(t,o){return this.has_bttv=!1,e.BTTVLOADED?this.setup_bttv():void(o>=6e4?this.log("BetterTTV was not detected after 60 seconds."):setTimeout(this.find_bttv.bind(this,t,(o||0)+t),t))},t.prototype.setup_bttv=function(){this.log("BetterTTV was detected. Hooking."),this.has_bttv=!0,this.track("setCustomVariable","3","BetterTTV",BetterTTV.info.versionString());var e=BetterTTV.chat.helpers.sendMessage,t=this;BetterTTV.chat.helpers.sendMessage=function(o){var s=o.split(" ",1)[0].toLowerCase();return"/ffz"!==s?e(o):void t.run_command(o.substr(5),BetterTTV.chat.store.currentRoom)};var s,n=BetterTTV.chat.handlers.privmsg;BetterTTV.chat.handlers.privmsg=function(e,t){s=e;var o=n(e,t);return s=null,o};var a=BetterTTV.chat.templates.privmsg;BetterTTV.chat.templates.privmsg=function(e,n,r,i,l){t.bttv_badges(l);var c=a(e,n,r,i,l);return c.replace(o,'$1 data-room="'+s+'"')};var r,i=BetterTTV.chat.templates.message;BetterTTV.chat.templates.message=function(e,t,o,s){r=e;var n=i(e,t,o,s);return r=null,n};var l=BetterTTV.chat.templates.emoticonize;BetterTTV.chat.templates.emoticonize=function(e,o){var n=l(e,o),a=t.getEmotes(r,s),o=[];return _.each(a,function(e){var s=t.emote_sets[e];s&&_.each(s.emotes,function(e){_.any(n,function(t){return _.isString(t)&&t.match(e.regex)})&&o.push(e)})}),o.length?(_.each(o,function(e){var t=[''+e.name+''],o=n;if(n=[],!o||!o.length)return n;for(var s=0;s50)return"Each user you unmod counts as a single message. To avoid being globally banned, please limit yourself to 50 at a time and wait between uses.";for(var s=t.length;t.length;){var n=t.shift();e.room.tmiRoom.sendMessage("/unmod "+n)}return"Sent unmod command for "+s+" users."},t.chat_commands.massunmod.help="Usage: /ffz massunmod \nBroadcaster only. Unmod all the users in the provided list.",t.chat_commands.massmod=function(e,t){if(t=t.join(" ").trim(),!t.length)return"You must provide a list of users to mod.";t=t.split(/\W*,\W*/);var o=this.get_user();if(!o||!o.login==e.id)return"You must be the broadcaster to use massmod.";if(t.length>50)return"Each user you mod counts as a single message. To avoid being globally banned, please limit yourself to 50 at a time and wait between uses.";for(var s=t.length;t.length;){var n=t.shift();e.room.tmiRoom.sendMessage("/mod "+n)}return"Sent mod command for "+s+" users."},t.chat_commands.massmod.help="Usage: /ffz massmod \nBroadcaster only. Mod all the users in the provided list."},{}],4:[function(e,t){var o='',s="true"==localStorage.ffzDebugMode&&document.body.classList.contains("ffz-dev");t.exports={DEBUG:s,SERVER:s?"//localhost:8000/":"//cdn.frankerfacez.com/",SVGPATH:o,ZREKNARF:''+o+"",CHAT_BUTTON:''+o+""}},{}],5:[function(){var t=e.FrankerFaceZ;t.chat_commands.developer_mode=function(e,t){var o,t=t&&t.length?t[0].toLowerCase():null;return"y"==t||"yes"==t||"true"==t||"on"==t?o=!0:("n"==t||"no"==t||"false"==t||"off"==t)&&(o=!1),void 0===o?"Developer Mode is currently "+("true"==localStorage.ffzDebugMode?"enabled.":"disabled."):(localStorage.ffzDebugMode=o,"Developer Mode is now "+(o?"enabled":"disabled")+". Please refresh your browser.")},t.chat_commands.developer_mode.help="Usage: /ffz developer_mode \nEnable or disable Developer Mode. When Developer Mode is enabled, the script will be reloaded from //localhost:8000/script.js instead of from the CDN."},{}],6:[function(){var t=e.FrankerFaceZ;t.prototype.setup_chatview=function(){this.log("Hooking the Ember Chat view.");var e=App.__container__.resolve("view:chat");this._modify_cview(e),e.create().destroy();for(var t in Ember.View.views)if(Ember.View.views.hasOwnProperty(t)){var o=Ember.View.views[t];o instanceof e&&(this.log("Adding UI link manually to Chat view.",o),o.$(".textarea-contain").append(this.build_ui_link(o)))}},t.prototype._modify_cview=function(e){var t=this;e.reopen({didInsertElement:function(){this._super(),this.$()&&this.$(".textarea-contain").append(t.build_ui_link(this))},willClearRender:function(){this._super(),this.$(".ffz-ui-toggle").remove()},ffzUpdateLink:Ember.observer("controller.currentRoom",function(){t.update_ui_link()})})}},{}],7:[function(){var t=e.FrankerFaceZ;t.prototype.setup_line=function(){this.log("Hooking the Ember Line controller.");var e=App.__container__.resolve("controller:line"),t=this;e.reopen({tokenizedMessage:function(){return t._emoticonize(this,this._super())}.property("model.message","isModeratorOrHigher","controllers.emoticons.emoticons.[]")}),this.log("Hooking the Ember Line view.");var e=App.__container__.resolve("view:line");e.reopen({didInsertElement:function(){this._super();var e=this.get("element"),o=this.get("context.model.from");e.setAttribute("data-room",this.get("context.parentController.content.id")),e.setAttribute("data-sender",o),t.render_badge(this),"false"!=localStorage.ffzCapitalize&&t.capitalize(this,o)}})},t.capitalization={},t._cap_fetching=0,t.get_capitalization=function(e,o){e=e.toLowerCase();var s=t.capitalization[e];return s&&Date.now()-s[1]<36e5?s[0]:(t._cap_fetching<5&&(t._cap_fetching++,Twitch.api.get("users/"+e).always(function(s){var n=s.display_name||e;t.capitalization[e]=[n,Date.now()],t._cap_fetching--,o&&o(n)})),s?s[0]:e)},t.prototype.capitalize=function(e,o){var s=t.get_capitalization(o,this.capitalize.bind(this,e));s&&e.$(".from").text(s)},t.chat_commands.capitalization=function(e,t){var o,t=t&&t.length?t[0].toLowerCase():null;return"y"==t||"yes"==t||"true"==t||"on"==t?o=!0:("n"==t||"no"==t||"false"==t||"off"==t)&&(o=!1),void 0===o?"Chat Name Capitalization is currently "+("false"!=localStorage.ffzCapitalize?"enabled.":"disabled."):(localStorage.ffzCapitalize=o,"Chat Name Capitalization is now "+(o?"enabled.":"disabled."))},t.chat_commands.capitalization.help="Usage: /ffz capitalization \nEnable or disable Chat Name Capitalization. This setting does not work with BetterTTV.",t.prototype._emoticonize=function(e,t){var o=e.get("parentController.model.id"),s=e.get("model.from"),n=this,a=this.getEmotes(s,o),r=[];return _.each(a,function(e){var o=n.emote_sets[e];o&&_.each(o.emotes,function(e){_.any(t,function(t){return _.isString(t)&&t.match(e.regex)})&&r.push(e)})}),r.length?("string"==typeof t&&(t=[t]),_.each(r,function(e){var o={isEmoticon:!0,cls:e.klass,emoticonSrc:e.url,altText:e.hidden?"???":e.name};t=_.compact(_.flatten(_.map(t,function(t){if(_.isObject(t))return t;var s=t.split(e.regex),n=[];return s.forEach(function(e,t){n.push(e),t!==s.length-1&&n.push(o)}),n})))}),t):t}},{}],8:[function(t){var o=e.FrankerFaceZ,s=/\.([\w\-_]+)\s*?\{content:\s*?"([^"]+)";\s*?background-image:\s*?url\("([^"]+)"\);\s*?height:\s*?(\d+)px;\s*?width:\s*?(\d+)px;\s*?margin:([^;}]+);?([^}]*)\}/gm,n=/[^\n}]*\.badges\s+\.moderator\s*{\s*background-image:\s*url\(\s*['"]([^'"]+)['"][^}]+(?:}|$)/,a=/^_([^_]+)_\d+$/,r=t("../constants"),i=t("../utils"),l=function(e){return e.moderator_badge?'.chat-line[data-room="'+e.id+'"] .badges .moderator { background-image:url("'+e.moderator_badge+'") !important; }':""};o.prototype.setup_room=function(){this.rooms={},this.log("Creating room style element.");var e=this._room_style=document.createElement("style");e.id="ffz-room-css",document.head.appendChild(e),this.log("Hooking the Ember Room model.");var t=App.__container__.resolve("model:room");this._modify_room(t);var o=t.instances;for(var s in o)if(o.hasOwnProperty(s)){var n=o[s];this.add_room(n.id,n),this._modify_room(n)}},o.chat_commands={},o.prototype.room_message=function(e,t){var o=t.split("\n");if(this.has_bttv)for(var s=0;so?this._legacy_add_room(e,t,o):void 0)})},o.prototype._legacy_load_room_css=function(e,t,o){var r=e,i=r.match(a);i&&i[1]&&(r=i[1]);var l={id:e,menu_sets:[r],sets:[r],moderator_badge:null,css:null};return o&&(o=o.replace(s,"").trim()),o&&(o=o.replace(n,function(e,t){return l.moderator_badge||"modicon.png"!==t.substr(-11)?e:(l.moderator_badge=t,"")})),l.css=o||null,this._load_room_json(e,t,l)}},{"../constants":4,"../utils":22}],9:[function(){var t=e.FrankerFaceZ;t.prototype.setup_router=function(){this.log("Hooking the Ember router.");var e=this;App.__container__.lookup("router:main").reopen({ffzTransition:function(){e.track_page()}.on("didTransition")})}},{}],10:[function(){var t=e.FrankerFaceZ;t.prototype.setup_viewers=function(){this.log("Hooking the Ember Viewers controller.");var e=App.__container__.resolve("controller:viewers");this._modify_viewers(e)},t.prototype._modify_viewers=function(e){var o=this;e.reopen({lines:function(){var e=this._super(),s=[],n={},a=null,r=App.__container__.lookup("controller:channel"),i=this.get("parentController.model.id"),l=r&&r.get("id");if(l){var c=r.get("display_name");c&&(t.capitalization[l]=[c,Date.now()])}i!=l&&(l=null);for(var u=0;uo?this._legacy_load_set(e,t,o):t&&t(!1))})},o.prototype._legacy_load_css=function(e,t,o){var n={},a={id:e,emotes:n,extra_css:null},r=this;o.replace(s,function(e,t,o,s,a,l,c,u){a=parseInt(a),l=parseInt(l),c=i(c,a);var d="."===s.substr(s.lastIndexOf("/")+1,1),h=++r._last_emote_id,f={id:h,hidden:d,name:o,height:a,width:l,url:s,margins:c,extra_css:u};return n[h]=f,""}),this._load_set_json(e,t,a)}},{"./constants":4,"./utils":22}],12:[function(t){t("./shims");var o=e.FrankerFaceZ=function(){o.instance=this,this.initialize()};o.get=function(){return o.instance};var s=o.version_info={major:3,minor:0,revision:0,toString:function(){return[s.major,s.minor,s.revision].join(".")+(s.extra||"")}};o.prototype.log=function(e,t,o){e="FFZ: "+e+(o?" -- "+JSON.stringify(t):""),void 0!==t&&console.groupCollapsed&&console.dir?(console.groupCollapsed(e),-1!==navigator.userAgent.indexOf("Firefox/")?console.log(t):console.dir(t),console.groupEnd(e)):console.log(e)},o.prototype.get_user=function(){if(e.PP&&PP.login)return PP;if(e.App){var t=App.__container__.lookup("controller:navigation");return t?t.get("userData"):void 0}},t("./socket"),t("./emoticons"),t("./badges"),t("./ember/router"),t("./ember/room"),t("./ember/line"),t("./ember/chatview"),t("./ember/viewers"),t("./tracking"),t("./debug"),t("./betterttv"),t("./featurefriday"),t("./ui/styles"),t("./ui/notifications"),t("./ui/viewer_count"),t("./ui/menu_button"),t("./ui/menu"),t("./commands"),o.prototype.initialize=function(t,o){var s=void 0!=e.App&&void 0!=App.__container__&&void 0!=App.__container__.resolve("model:room");return s?void this.setup(o):(t=t||10,void(o>=6e4?this.log('Twitch application not detected in "'+location.toString()+'". Aborting.'):setTimeout(this.initialize.bind(this,t,(o||0)+t),t)))},o.prototype.setup=function(t){var s=e.performance&&performance.now?performance.now():Date.now();this.log("Found Twitch application after "+(t||0)+' ms in "'+location+'". Initializing FrankerFaceZ version '+o.version_info),this.users={};for(var n in localStorage)"ffz_"==n.substr(0,4)&&localStorage.removeItem(n);var a=this.get_user();a&&a.name&&(o.capitalization[a.login]=[a.name,Date.now()]);try{this.ws_create(),this.setup_emoticons(),this.setup_badges(),this.setup_piwik(),this.setup_router(),this.setup_room(),this.setup_line(),this.setup_chatview(),this.setup_viewers(),this.setup_css(),this.setup_menu(),this.find_bttv(10),this.check_ff()}catch(r){return void this.log("An error occurred while starting FrankerFaceZ: "+r)}e.console&&console.time&&console.timeEnd("FrankerFaceZ Initialization");var i=e.performance&&performance.now?performance.now():Date.now(),l=i-s;this.log("Initialization complete in "+l+"ms")}},{"./badges":1,"./betterttv":2,"./commands":3,"./debug":5,"./ember/chatview":6,"./ember/line":7,"./ember/room":8,"./ember/router":9,"./ember/viewers":10,"./emoticons":11,"./featurefriday":13,"./shims":14,"./socket":15,"./tracking":16,"./ui/menu":17,"./ui/menu_button":18,"./ui/notifications":19,"./ui/styles":20,"./ui/viewer_count":21}],13:[function(t){var o=e.FrankerFaceZ,s=t("./constants");o.prototype.feature_friday=null,o.prototype.check_ff=function(e){e||this.log("Checking for Feature Friday data..."),jQuery.ajax(s.SERVER+"script/event.json",{cache:!1,dataType:"json",context:this}).done(function(e){return this._load_ff(e)}).fail(function(t){return 404==t.status?this._load_ff(null):(e=e||0,e++,10>e?setTimeout(this.check_ff.bind(this,e),250):this._load_ff(null))})},o.ws_commands.reload_ff=function(){this.check_ff()},o.prototype._feature_friday_ui=function(e,t,o){if(this.feature_friday&&this.feature_friday.channel!=e){this._emotes_for_sets(t,o,[this.feature_friday.set],"Feature Friday");var s=App.__container__.lookup("controller:channel");if(!s||s.get("id")!=this.feature_friday.channel){var n=this.feature_friday,a=this,r=document.createElement("div"),i=document.createElement("a");r.className="chat-menu-content",r.style.textAlign="center";var l=n.display_name+(n.live?" is live now!":"");i.className="button primary",i.classList.toggle("live",n.live),i.classList.toggle("blue",this.has_bttv&&BetterTTV.settings.get("showBlueButtons")),i.href="http://www.twitch.tv/"+n.channel,i.title=l,i.target="_new",i.innerHTML=""+l+"",i.addEventListener("click",function(){a.track("trackLink",this.href,"link")}),r.appendChild(i),t.appendChild(r)}}},o.prototype._load_ff=function(e){if(this.feature_friday){this.global_sets.removeObject(this.feature_friday.set);var t=this.emote_sets[this.feature_friday.set];t&&(t.global=!1),this.feature_friday=null,this.update_ui_link()}e&&e.set&&e.channel&&(this.feature_friday={set:e.set,channel:e.channel,live:!1,display_name:o.get_capitalization(e.channel,this._update_ff_name.bind(this))},this.global_sets.push(e.set),this.load_set(e.set,this._update_ff_set.bind(this)),this._update_ff_live())},o.prototype._update_ff_live=function(){if(this.feature_friday){var e=this;Twitch.api.get("streams/"+this.feature_friday.channel).done(function(t){e.feature_friday.live=null!=t.stream,e.update_ui_link()}).always(function(){e.feature_friday.timer=setTimeout(e._update_ff_live.bind(e),12e4)})}},o.prototype._update_ff_set=function(e,t){t&&(t.global=!0)},o.prototype._update_ff_name=function(e){this.feature_friday&&(this.feature_friday.display_name=e)}},{"./constants":4}],14:[function(){Array.prototype.equals=function(e){if(!e)return!1;if(this.length!=e.length)return!1;for(var t=0,o=this.length;o>t;t++)if(this[t]instanceof Array&&e[t]instanceof Array){if(!this[t].equals(e[t]))return!1}else if(this[t]!=e[t])return!1;return!0}},{}],15:[function(){var t=e.FrankerFaceZ;t.prototype._ws_open=!1,t.prototype._ws_delay=0,t.ws_commands={},t.prototype.ws_create=function(){var e=this;this._ws_last_req=0,this._ws_callbacks={};var o=this._ws_sock=new WebSocket("ws://ffz.stendec.me/");o.onopen=function(){e._ws_open=!0,e._ws_delay=0,e.log("Socket connected.");var t=e.get_user();t&&e.ws_send("setuser",t.login);for(var o in e.rooms)e.ws_send("sub",o)},o.onclose=function(){e.log("Socket closed."),e._ws_open=!1,e._ws_delay<3e4&&(e._ws_delay+=5e3),setTimeout(e.ws_create.bind(e),e._ws_delay)},o.onmessage=function(o){var s,n,a=o.data.indexOf(" "),r=o.data.substr(a+1),i=parseInt(o.data.slice(0,a));if(a=r.indexOf(" "),-1===a&&(a=r.length),s=r.slice(0,a),r=r.substr(a+1),r&&(n=JSON.parse(r)),-1===i){var l=t.ws_commands[s];l?l.bind(e)(n):e.log("Invalid command: "+s,n)}else{var c="True"===s,u=e._ws_callbacks[i];e.log("Socket Reply to "+i+" - "+(c?"SUCCESS":"FAIL"),n),u&&(delete e._ws_callbacks[i],u(c,n))}}},t.prototype.ws_send=function(e,t,o){if(!this._ws_open)return!1;var s=++this._ws_last_req;return t=void 0!==t?" "+JSON.stringify(t):"",o&&(this._ws_callbacks[s]=o),this._ws_sock.send(s+" "+e+t),s}},{}],16:[function(t){var o=e.FrankerFaceZ,s=(t("./constants"),("https:"==document.location.protocol?"https:":"http:")+"//sir.stendec.me/ffz_piwik/");o.prototype.setup_piwik=function(){if(void 0!=e._paq)return this.log("Piwik is already present. Disabling analytics."),void(this._tracking=!1);if("false"==localStorage.ffzTracking)return this.log("The user has opted out of tracking. Disabling analytics."),void(this._tracking=!1);this.log("Initializing Piwik."),this._tracking=!0;var t=e._paq=[];t.push(["setSiteId",1]),t.push(["setTrackerUrl",s+"piwik.php"]),this.has_bttv&&t.push(["setCustomVariable","3","BetterTTV",BetterTTV.info.versionString()]);var o=this.get_user(),n=this;o?(t.push(["setCustomVariable","1","Partnered",o.is_partner?"Yes":"No"]),t.push(["setCustomVariable","2","User Type",o.is_staff?"Staff":o.is_admin?"Admin":"User"]),t.push(["setUserId",o.login]),Twitch.api.get("channels/"+o.login).done(function(e){e.logo&&n.track("setCustomVariable","4","Avatar",e.logo)}).always(function(){n.track_page()})):this.track_page(),"true"==localStorage.ffzTracking&&(this.track("trackEvent","Analytics","Enable"),localStorage.removeItem("ffzTracking"));var a=document.createElement("script");a.type="text/javascript",a.defer=!0,a.async=!0,a.src=s+"piwik.js",document.head.appendChild(a)},o.chat_commands.analytics=function(e,t){var o,t=t&&t.length?t[0].toLowerCase():null;return"y"==t||"yes"==t||"true"==t||"on"==t?o=!0:("n"==t||"no"==t||"false"==t||"off"==t)&&(o=!1),void 0===o?"Analytics are currently "+("false"!=localStorage.ffzTracking?"enabled.":"disabled."):(this._tracking&&!o&&"false"!=localStorage.ffzTracking&&this.track("trackEvent","Analytics","Disable"),localStorage.ffzTracking=o,"Analytics are now "+(o?"enabled":"disabled")+". Please refresh your browser.")},o.chat_commands.analytics.help="Usage: /ffz analytics \nEnable or disable FrankerFaceZ analytics. We collect some data about your browser and how you use FrankerFaceZ to help us improve the script. Turn off analytics if you'd rather we not.",o.prototype.track=function(){this._tracking&&e._paq&&_paq.push(Array.prototype.slice.call(arguments))},o.prototype.track_page=function(){if(this._tracking){this._old_url&&this.track("setReferrerUrl",this._old_url),this._old_url=document.location.toString(),this.track("setCustomUrl",this._old_url),this.track("deleteCustomVariable","1","page"),this.track("deleteCustomVariable","3","page");var e=App.__container__.resolve("router:main").router.currentHandlerInfos;if(e&&0!=e.length){var t=e[e.length-1];if("channel.index"==t.name&&t.context){var o=t.context.get("isFollowing.isFollowing");void 0!==o&&null!==o&&this.track("setCustomVariable","1","Following",o?"Yes":"No","page");var s=t.context.get("game");s&&this.track("setCustomVariable","3","Game",s,"page"),this.track("trackPageView",document.title)}}}}},{"./constants":4}],17:[function(){var t=e.FrankerFaceZ;t.prototype.setup_menu=function(){this.log("Installing mouse-up event to auto-close menus.");var e=this;jQuery(document).mouseup(function(t){var o,s=e._popup;s&&(s=jQuery(s),o=s.parent(),o.is(t.target)||0!==o.has(t.target).length||(s.remove(),delete e._popup))})},t.prototype.build_ui_popup=function(e){var t=this._popup;if(t)return t.parentElement.removeChild(t),void delete this._popup;var o=document.createElement("div"),s=document.createElement("div");o.className="emoticon-selector chat-menu ffz-ui-popup",s.className="emoticon-selector-box dropmenu",o.appendChild(s);var n=e.get("controller.currentRoom.id"),a=this.rooms[n];this.log("Menu for Room: "+n,a),this.track("trackEvent","Menu","Open",n);var r=document.createElement("a");r.className="button glyph-only ffz-button",r.title="Advertise for FrankerFaceZ in chat!",r.href="#",r.innerHTML='';var i=document.createElement("div");i.className="list-header first",i.appendChild(r),i.appendChild(document.createTextNode("FrankerFaceZ")),s.appendChild(i);var l=this._emotes_for_sets(s,e,a&&a.menu_sets||[]);0===l?r.addEventListener("click",this._add_emote.bind(this,e,"To use custom emoticons in tons of channels, get FrankerFaceZ from http://www.frankerfacez.com")):r.addEventListener("click",this._add_emote.bind(this,e,"To view this channel's emoticons, get FrankerFaceZ from http://www.frankerfacez.com")),this._feature_friday_ui(n,s,e),this._popup=o,s.style.maxHeight=Math.max(300,e.$().height()-171)+"px",e.$(".chat-interface").append(o)},t.prototype._emotes_for_sets=function(e,t,o,s,n){if(null!=s){var a=document.createElement("div");a.className="list-header",a.appendChild(document.createTextNode(s)),n&&a.appendChild(n),e.appendChild(a)}var r=document.createElement("div"),i=0;r.className="emoticon-grid"; -for(var l=0;l0){n=!0;break}}e.classList.toggle("no-emotes",!n),e.classList.toggle("live",i),e.classList.toggle("dark",a),e.classList.toggle("blue",r)}},{"../constants":4}],19:[function(){var t=e.FrankerFaceZ;t.prototype.show_notification=function(t){e.noty({text:t,theme:"ffzTheme",layout:"bottomCenter",closeWith:["button"]}).show()},t.ws_commands.message=function(e){this.show_notification(e)}},{}],20:[function(t){var o=e.FrankerFaceZ,s=t("../constants");o.prototype.setup_css=function(){this.log("Injecting main FrankerFaceZ CSS.");var e=this._main_style=document.createElement("link");e.id="ffz-ui-css",e.setAttribute("rel","stylesheet"),e.setAttribute("href",s.SERVER+"script/style.css"),document.head.appendChild(e),jQuery.noty.themes.ffzTheme={name:"ffzTheme",style:function(){this.$bar.removeClass().addClass("noty_bar").addClass("ffz-noty").addClass(this.options.type)},callback:{onShow:function(){},onClose:function(){}}}}},{"../constants":4}],21:[function(t){var o=e.FrankerFaceZ,s=t("../constants"),n=t("../utils");o.ws_commands.viewers=function(e){var t=e[0],o=e[1],a=App.__container__.lookup("controller:channel"),r=a&&a.get&&a.get("id");if(r===t){var i=document.querySelector(".channel-stats .ffz.stat"),l=s.ZREKNARF+" "+n.number_commas(o);if(i)i.innerHTML=l;else{var c=document.querySelector(".channel-stats");if(!c)return;i=document.createElement("span"),i.className="ffz stat",i.title="Viewers with FrankerFaceZ",i.innerHTML=l,c.appendChild(i),jQuery(i).tipsy()}}}},{"../constants":4,"../utils":22}],22:[function(t,o){e.FrankerFaceZ,t("./constants");o.exports={update_css:function(e,t,o){var s=e.innerHTML,n="/*BEGIN "+t+"*/",a="/*END "+t+"*/",r=s.indexOf(n),i=s.indexOf(a),l=-1!==r&&-1!==i&&i>r;(l||o)&&(l&&(s=s.substr(0,r)+s.substr(i+a.length)),o&&(s+=n+o+a),e.innerHTML=s)},number_commas:function(e){var t=e.toString().split(".");return t[0]=t[0].replace(/\B(?=(\d{3})+(?!\d))/g,","),t.join(".")}}},{"./constants":4}]},{},[12]),e.ffz=new FrankerFaceZ}(window); \ No newline at end of file +!function(e){!function t(e,o,n){function s(a,i){if(!o[a]){if(!e[a]){var l="function"==typeof require&&require;if(!i&&l)return l(a,!0);if(r)return r(a,!0);throw new Error("Cannot find module '"+a+"'")}var c=o[a]={exports:{}};e[a][0].call(c.exports,function(t){var o=e[a][1][t];return s(o?o:t)},c,c.exports,t,e,o,n)}return o[a].exports}for(var r="function"==typeof require&&require,a=0;ae?this._legacy_add_donors(e):void 0):void 0})},o.prototype._legacy_parse_donors=function(e){var t=0;if(null!=e)for(var o=e.trim().split(/\W+/),n=0;n50)return"Each user you unmod counts as a single message. To avoid being globally banned, please limit yourself to 50 at a time and wait between uses.";for(var n=t.length;t.length;){var s=t.shift();e.room.tmiRoom.sendMessage("/unmod "+s)}return"Sent unmod command for "+n+" users."},t.chat_commands.massunmod.help="Usage: /ffz massunmod \nBroadcaster only. Unmod all the users in the provided list.",t.chat_commands.massmod=function(e,t){if(t=t.join(" ").trim(),!t.length)return"You must provide a list of users to mod.";t=t.split(/\W*,\W*/);var o=this.get_user();if(!o||!o.login==e.id)return"You must be the broadcaster to use massmod.";if(t.length>50)return"Each user you mod counts as a single message. To avoid being globally banned, please limit yourself to 50 at a time and wait between uses.";for(var n=t.length;t.length;){var s=t.shift();e.room.tmiRoom.sendMessage("/mod "+s)}return"Sent mod command for "+n+" users."},t.chat_commands.massmod.help="Usage: /ffz massmod \nBroadcaster only. Mod all the users in the provided list."},{}],3:[function(e,t){var o='',n="true"==localStorage.ffzDebugMode&&document.body.classList.contains("ffz-dev");t.exports={DEBUG:n,SERVER:n?"//localhost:8000/":"//cdn.frankerfacez.com/",SVGPATH:o,ZREKNARF:''+o+"",CHAT_BUTTON:''+o+""}},{}],4:[function(){var t=e.FrankerFaceZ;t.chat_commands.developer_mode=function(e,t){var o,t=t&&t.length?t[0].toLowerCase():null;return"y"==t||"yes"==t||"true"==t||"on"==t?o=!0:("n"==t||"no"==t||"false"==t||"off"==t)&&(o=!1),void 0===o?"Developer Mode is currently "+("true"==localStorage.ffzDebugMode?"enabled.":"disabled."):(localStorage.ffzDebugMode=o,"Developer Mode is now "+(o?"enabled":"disabled")+". Please refresh your browser.")},t.chat_commands.developer_mode.help="Usage: /ffz developer_mode \nEnable or disable Developer Mode. When Developer Mode is enabled, the script will be reloaded from //localhost:8000/script.js instead of from the CDN."},{}],5:[function(){var t=e.FrankerFaceZ;t.prototype.setup_chatview=function(){this.log("Hooking the Ember Chat view.");var e=App.__container__.resolve("view:chat");this._modify_cview(e),e.create().destroy();for(var t in Ember.View.views)if(Ember.View.views.hasOwnProperty(t)){var o=Ember.View.views[t];o instanceof e&&(this.log("Adding UI link manually to Chat view.",o),o.$(".textarea-contain").append(this.build_ui_link(o)))}},t.prototype._modify_cview=function(e){var t=this;e.reopen({didInsertElement:function(){this._super(),this.$()&&this.$(".textarea-contain").append(t.build_ui_link(this))},willClearRender:function(){this._super(),this.$(".ffz-ui-toggle").remove()},ffzUpdateLink:Ember.observer("controller.currentRoom",function(){t.update_ui_link()})})}},{}],6:[function(){var t=e.FrankerFaceZ;t.prototype.setup_line=function(){this.log("Hooking the Ember Line controller.");var e=App.__container__.resolve("controller:line"),o=this;e.reopen({tokenizedMessage:function(){return o._emoticonize(this,this._super())}.property("model.message","isModeratorOrHigher","controllers.emoticons.emoticons.[]")}),this.log("Hooking the Ember Line view.");var e=App.__container__.resolve("view:line");e.reopen({didInsertElement:function(){this._super();var e=this.get("element"),t=this.get("context.model.from");e.setAttribute("data-room",this.get("context.parentController.content.id")),e.setAttribute("data-sender",t),o.render_badge(this),"false"!=localStorage.ffzCapitalize&&o.capitalize(this,t)}});var n=this.get_user();n&&n.name&&(t.capitalization[n.login]=[n.name,Date.now()])},t.capitalization={},t._cap_fetching=0,t.get_capitalization=function(e,o){if(e=e.toLowerCase(),"jtv"==e||"twitchnotify"==e)return e;var n=t.capitalization[e];return n&&Date.now()-n[1]<36e5?n[0]:(t._cap_fetching<5&&(t._cap_fetching++,Twitch.api.get("users/"+e).always(function(n){var s=n.display_name||e;t.capitalization[e]=[s,Date.now()],t._cap_fetching--,o&&o(s)})),n?n[0]:e)},t.prototype.capitalize=function(e,o){var n=t.get_capitalization(o,this.capitalize.bind(this,e));n&&e.$(".from").text(n)},t.chat_commands.capitalization=function(e,t){var o,t=t&&t.length?t[0].toLowerCase():null;return"y"==t||"yes"==t||"true"==t||"on"==t?o=!0:("n"==t||"no"==t||"false"==t||"off"==t)&&(o=!1),void 0===o?"Chat Name Capitalization is currently "+("false"!=localStorage.ffzCapitalize?"enabled.":"disabled."):(localStorage.ffzCapitalize=o,"Chat Name Capitalization is now "+(o?"enabled.":"disabled."))},t.chat_commands.capitalization.help="Usage: /ffz capitalization \nEnable or disable Chat Name Capitalization. This setting does not work with BetterTTV.",t.prototype._emoticonize=function(e,t){var o=e.get("parentController.model.id"),n=e.get("model.from"),s=this,r=this.getEmotes(n,o),a=[];return _.each(r,function(e){var o=s.emote_sets[e];o&&_.each(o.emotes,function(e){_.any(t,function(t){return _.isString(t)&&t.match(e.regex)})&&a.push(e)})}),a.length?("string"==typeof t&&(t=[t]),_.each(a,function(e){var o={isEmoticon:!0,cls:e.klass,emoticonSrc:e.url,altText:e.hidden?"???":e.name};t=_.compact(_.flatten(_.map(t,function(t){if(_.isObject(t))return t;var n=t.split(e.regex),s=[];return n.forEach(function(e,t){s.push(e),t!==n.length-1&&s.push(o)}),s})))}),t):t}},{}],7:[function(t){var o=e.FrankerFaceZ,n=/\.([\w\-_]+)\s*?\{content:\s*?"([^"]+)";\s*?background-image:\s*?url\("([^"]+)"\);\s*?height:\s*?(\d+)px;\s*?width:\s*?(\d+)px;\s*?margin:([^;}]+);?([^}]*)\}/gm,s=/[^\n}]*\.badges\s+\.moderator\s*{\s*background-image:\s*url\(\s*['"]([^'"]+)['"][^}]+(?:}|$)/,r=/^_([^_]+)_\d+$/,a=t("../constants"),i=t("../utils"),l=function(e){return e.moderator_badge?'.chat-line[data-room="'+e.id+'"] .badges .moderator { background-image:url("'+e.moderator_badge+'") !important; }':""};o.prototype.setup_room=function(){this.rooms={},this.log("Creating room style element.");var e=this._room_style=document.createElement("style");e.id="ffz-room-css",document.head.appendChild(e),this.log("Hooking the Ember Room model.");var t=App.__container__.resolve("model:room");this._modify_room(t);var o=t.instances;for(var n in o)if(o.hasOwnProperty(n)){var s=o[n];this.add_room(s.id,s),this._modify_room(s)}},o.chat_commands={},o.prototype.room_message=function(e,t){var o=t.split("\n");if(this.has_bttv)for(var n=0;no?this._legacy_add_room(e,t,o):void 0)})},o.prototype._legacy_load_room_css=function(e,t,o){var a=e,i=a.match(r);i&&i[1]&&(a=i[1]);var l={id:e,menu_sets:[a],sets:[a],moderator_badge:null,css:null};return o&&(o=o.replace(n,"").trim()),o&&(o=o.replace(s,function(e,t){return l.moderator_badge||"modicon.png"!==t.substr(-11)?e:(l.moderator_badge=t,"")})),l.css=o||null,this._load_room_json(e,t,l)}},{"../constants":3,"../utils":23}],8:[function(){var t=e.FrankerFaceZ;t.prototype.setup_router=function(){this.log("Hooking the Ember router.");var e=this;App.__container__.lookup("router:main").reopen({ffzTransition:function(){e.track_page()}.on("didTransition")})}},{}],9:[function(){var t=e.FrankerFaceZ;t.prototype.setup_viewers=function(){this.log("Hooking the Ember Viewers controller.");var e=App.__container__.resolve("controller:viewers");this._modify_viewers(e)},t.prototype._modify_viewers=function(e){var o=this;e.reopen({lines:function(){var e=this._super(),n=[],s={},r=null,a=App.__container__.lookup("controller:channel"),i=this.get("parentController.model.id"),l=a&&a.get("id");if(l){var c=a.get("display_name");c&&(t.capitalization[l]=[c,Date.now()])}i!=l&&(l=null);for(var u=0;uo?this._legacy_load_set(e,t,o):t&&t(!1))})},o.prototype._legacy_load_css=function(e,t,o){var r={},a={id:e,emotes:r,extra_css:null},i=this;o=o.replace(n,function(e,t,o,n,s,a,c,u){s=parseInt(s),a=parseInt(a),c=l(c,s);var d="."===n.substr(n.lastIndexOf("/")+1,1),h=++i._last_emote_id,m={id:h,hidden:d,name:o,height:s,width:a,url:n,margins:c,extra_css:u};return r[h]=m,""}).trim(),o&&o.replace(s,function(e,t){a.icon||"modicon.png"!==t.substr(-11)||(a.icon=t)}),this._load_set_json(e,t,a)}},{"./constants":3,"./utils":23}],11:[function(){var t=e.FrankerFaceZ,o=/(\sdata-sender="[^"]*"(?=>))/;t.prototype.find_bttv=function(t,o){return this.has_bttv=!1,e.BTTVLOADED?this.setup_bttv(o||0):void(o>=6e4?this.log("BetterTTV was not detected after 60 seconds."):setTimeout(this.find_bttv.bind(this,t,(o||0)+t),t))},t.prototype.setup_bttv=function(e){this.log("BetterTTV was detected after "+e+"ms. Hooking."),this.has_bttv=!0,this.track("setCustomVariable","3","BetterTTV",BetterTTV.info.versionString());var t=BetterTTV.chat.helpers.sendMessage,n=this;BetterTTV.chat.helpers.sendMessage=function(e){var o=e.split(" ",1)[0].toLowerCase();return"/ffz"!==o?t(e):void n.run_command(e.substr(5),BetterTTV.chat.store.currentRoom)};var s,r=BetterTTV.chat.handlers.privmsg;BetterTTV.chat.handlers.privmsg=function(e,t){s=e;var o=r(e,t);return s=null,o};var a=BetterTTV.chat.templates.privmsg;BetterTTV.chat.templates.privmsg=function(e,t,r,i,l){n.bttv_badges(l);var c=a(e,t,r,i,l);return c.replace(o,'$1 data-room="'+s+'"')};var i,l=BetterTTV.chat.templates.message;BetterTTV.chat.templates.message=function(e,t,o,n){i=e;var s=l(e,t,o,n);return i=null,s};var c=BetterTTV.chat.templates.emoticonize;BetterTTV.chat.templates.emoticonize=function(e,t){var o=c(e,t),r=n.getEmotes(i,s),t=[];return _.each(r,function(e){var s=n.emote_sets[e];s&&_.each(s.emotes,function(e){_.any(o,function(t){return _.isString(t)&&t.match(e.regex)})&&t.push(e)})}),t.length?(_.each(t,function(e){var t=[''+e.name+''],n=o;if(o=[],!n||!n.length)return o;for(var s=0;s=6e4?this.log("Emote Menu for Twitch was not detected after 60 seconds."):setTimeout(this.find_emote_menu.bind(this,t,(o||0)+t),t))},t.prototype.setup_emote_menu=function(e){this.log("Emote Menu for Twitch was detected after "+e+"ms. Registering emote enumerator."),emoteMenu.registerEmoteGetter("FrankerFaceZ",this._emote_menu_enumerator.bind(this))},t.prototype._emote_menu_enumerator=function(){for(var e=this.get_user(),o=e?e.login:null,n=App.__container__.lookup("controller:chat"),s=n?n.get("currentRoom.id"):null,r=this.getEmotes(o,s),a=[],i=0;i=6e4?this.log('Twitch application not detected in "'+location.toString()+'". Aborting.'):setTimeout(this.initialize.bind(this,t,(o||0)+t),t)))},o.prototype.setup_ember=function(t){var n=e.performance&&performance.now?performance.now():Date.now();this.log("Found Twitch application after "+(t||0)+' ms in "'+location+'". Initializing FrankerFaceZ version '+o.version_info),this.users={};for(var s in localStorage)"ffz_"==s.substr(0,4)&&localStorage.removeItem(s);this.ws_create(),this.setup_emoticons(),this.setup_badges(),this.setup_piwik(),this.setup_router(),this.setup_room(),this.setup_line(),this.setup_chatview(),this.setup_viewers(),this.setup_css(),this.setup_menu(),this.find_bttv(10),this.find_emote_menu(10),this.check_ff();var r=e.performance&&performance.now?performance.now():Date.now(),a=r-n;this.log("Initialization complete in "+a+"ms")}},{"./badges":1,"./commands":2,"./debug":4,"./ember/chatview":5,"./ember/line":6,"./ember/room":7,"./ember/router":8,"./ember/viewers":9,"./emoticons":10,"./ext/betterttv":11,"./ext/emote_menu":12,"./featurefriday":14,"./shims":15,"./socket":16,"./tracking":17,"./ui/menu":18,"./ui/menu_button":19,"./ui/notifications":20,"./ui/styles":21,"./ui/viewer_count":22}],14:[function(t){var o=e.FrankerFaceZ,n=t("./constants");o.prototype.feature_friday=null,o.prototype.check_ff=function(e){e||this.log("Checking for Feature Friday data..."),jQuery.ajax(n.SERVER+"script/event.json",{cache:!1,dataType:"json",context:this}).done(function(e){return this._load_ff(e)}).fail(function(t){return 404==t.status?this._load_ff(null):(e=e||0,e++,10>e?setTimeout(this.check_ff.bind(this,e),250):this._load_ff(null))})},o.ws_commands.reload_ff=function(){this.check_ff()},o.prototype._feature_friday_ui=function(e,t,o){if(this.feature_friday&&this.feature_friday.channel!=e){this._emotes_for_sets(t,o,[this.feature_friday.set],"Feature Friday");var n=App.__container__.lookup("controller:channel");if(!n||n.get("id")!=this.feature_friday.channel){var s=this.feature_friday,r=this,a=document.createElement("div"),i=document.createElement("a");a.className="chat-menu-content",a.style.textAlign="center";var l=s.display_name+(s.live?" is live now!":"");i.className="button primary",i.classList.toggle("live",s.live),i.classList.toggle("blue",this.has_bttv&&BetterTTV.settings.get("showBlueButtons")),i.href="http://www.twitch.tv/"+s.channel,i.title=l,i.target="_new",i.innerHTML=""+l+"",i.addEventListener("click",function(){r.track("trackLink",this.href,"link")}),a.appendChild(i),t.appendChild(a)}}},o.prototype._load_ff=function(e){if(this.feature_friday){this.global_sets.removeObject(this.feature_friday.set);var t=this.emote_sets[this.feature_friday.set];t&&(t.global=!1),this.feature_friday=null,this.update_ui_link()}e&&e.set&&e.channel&&(this.feature_friday={set:e.set,channel:e.channel,live:!1,display_name:o.get_capitalization(e.channel,this._update_ff_name.bind(this))},this.global_sets.push(e.set),this.load_set(e.set,this._update_ff_set.bind(this)),this._update_ff_live())},o.prototype._update_ff_live=function(){if(this.feature_friday){var e=this;Twitch.api.get("streams/"+this.feature_friday.channel).done(function(t){e.feature_friday.live=null!=t.stream,e.update_ui_link()}).always(function(){e.feature_friday.timer=setTimeout(e._update_ff_live.bind(e),12e4)})}},o.prototype._update_ff_set=function(e,t){t&&(t.global=!0)},o.prototype._update_ff_name=function(e){this.feature_friday&&(this.feature_friday.display_name=e)}},{"./constants":3}],15:[function(){Array.prototype.equals=function(e){if(!e)return!1;if(this.length!=e.length)return!1;for(var t=0,o=this.length;o>t;t++)if(this[t]instanceof Array&&e[t]instanceof Array){if(!this[t].equals(e[t]))return!1}else if(this[t]!=e[t])return!1;return!0}},{}],16:[function(){var t=e.FrankerFaceZ;t.prototype._ws_open=!1,t.prototype._ws_delay=0,t.ws_commands={},t.prototype.ws_create=function(){var e=this;this._ws_last_req=0,this._ws_callbacks={},this._ws_pending=this._ws_pending||[];var o=this._ws_sock=new WebSocket("ws://ffz.stendec.me/");o.onopen=function(){e._ws_open=!0,e._ws_delay=0,e.log("Socket connected.");var t=e.get_user();t&&e.ws_send("setuser",t.login);for(var o in e.rooms)e.ws_send("sub",o);var n=e._ws_pending;e._ws_pending=[];for(var s=0;s0){s=!0;break}}e.classList.toggle("no-emotes",!s),e.classList.toggle("live",i),e.classList.toggle("dark",r),e.classList.toggle("blue",a)}}},{"../constants":3}],20:[function(){var t=e.FrankerFaceZ;t.prototype.show_notification=function(t){e.noty({text:t,theme:"ffzTheme",layout:"bottomCenter",closeWith:["button"]}).show()},t.ws_commands.message=function(e){this.show_notification(e)}},{}],21:[function(t){var o=e.FrankerFaceZ,n=t("../constants");o.prototype.setup_css=function(){this.log("Injecting main FrankerFaceZ CSS.");var e=this._main_style=document.createElement("link");e.id="ffz-ui-css",e.setAttribute("rel","stylesheet"),e.setAttribute("href",n.SERVER+"script/style.css"),document.head.appendChild(e),jQuery.noty.themes.ffzTheme={name:"ffzTheme",style:function(){this.$bar.removeClass().addClass("noty_bar").addClass("ffz-noty").addClass(this.options.type)},callback:{onShow:function(){},onClose:function(){}}}}},{"../constants":3}],22:[function(t){var o=e.FrankerFaceZ,n=t("../constants"),s=t("../utils");o.ws_commands.viewers=function(e){var t=e[0],o=e[1],r=App.__container__.lookup("controller:channel"),a=r&&r.get&&r.get("id");if(a===t){var i=document.querySelector(".channel-stats .ffz.stat"),l=n.ZREKNARF+" "+s.number_commas(o);if(i)i.innerHTML=l;else{var c=document.querySelector(".channel-stats");if(!c)return;i=document.createElement("span"),i.className="ffz stat",i.title="Viewers with FrankerFaceZ",i.innerHTML=l,c.appendChild(i),jQuery(i).tipsy()}}}},{"../constants":3,"../utils":23}],23:[function(t,o){e.FrankerFaceZ,t("./constants");o.exports={update_css:function(e,t,o){var n=e.innerHTML,s="/*BEGIN "+t+"*/",r="/*END "+t+"*/",a=n.indexOf(s),i=n.indexOf(r),l=-1!==a&&-1!==i&&i>a;(l||o)&&(l&&(n=n.substr(0,a)+n.substr(i+r.length)),o&&(n+=s+o+r),e.innerHTML=n)},number_commas:function(e){var t=e.toString().split(".");return t[0]=t[0].replace(/\B(?=(\d{3})+(?!\d))/g,","),t.join(".")}}},{"./constants":3}]},{},[13]),e.ffz=new FrankerFaceZ}(window); \ No newline at end of file diff --git a/src/badges.js b/src/badges.js index 814ff962..a5505536 100644 --- a/src/badges.js +++ b/src/badges.js @@ -146,12 +146,12 @@ FFZ.prototype.render_badge = function(view) { // -------------------- FFZ.prototype._legacy_add_donors = function(tries) { - this.badges[1] = {id: 1, title: "FFZ Donor", color: "#755000", image: "http://cdn.frankerfacez.com/channel/global/donoricon.png"}; + this.badges[1] = {id: 1, title: "FFZ Donor", color: "#755000", image: "//cdn.frankerfacez.com/channel/global/donoricon.png"}; utils.update_css(this._badge_style, 1, badge_css(this.badges[1])); // Developer Badges // TODO: Upload the badge to the proper CDN. - this.badges[0] = {id: 0, title: "FFZ Developer", color: "#FAAF19", image: "http://sir.stendec.me/devicon.png"}; + this.badges[0] = {id: 0, title: "FFZ Developer", color: "#FAAF19", image: "//cdn.frankerfacez.com/channel/global/devicon.png"}; utils.update_css(this._badge_style, 0, badge_css(this.badges[0])); this.users.sirstendec = {badges: {0: {id:0}}}; diff --git a/src/commands.js b/src/commands.js index bdac82fc..a2400508 100644 --- a/src/commands.js +++ b/src/commands.js @@ -1,7 +1,8 @@ var FFZ = window.FrankerFaceZ; + // ----------------- -// Mass Unmod +// Mass Moderation // ----------------- FFZ.chat_commands.massunmod = function(room, args) { diff --git a/src/ember/line.js b/src/ember/line.js index ce52e6b6..638607d6 100644 --- a/src/ember/line.js +++ b/src/ember/line.js @@ -41,6 +41,11 @@ FFZ.prototype.setup_line = function() { } }); + + // Store the capitalization of our own name. + var user = this.get_user(); + if ( user && user.name ) + FFZ.capitalization[user.login] = [user.name, Date.now()]; } @@ -53,6 +58,9 @@ FFZ._cap_fetching = 0; FFZ.get_capitalization = function(name, callback) { name = name.toLowerCase(); + if ( name == "jtv" || name == "twitchnotify" ) + return name; + var old_data = FFZ.capitalization[name]; if ( old_data ) { if ( Date.now() - old_data[1] < 3600000 ) diff --git a/src/emoticons.js b/src/emoticons.js index d393fbda..251e94c1 100644 --- a/src/emoticons.js +++ b/src/emoticons.js @@ -1,5 +1,6 @@ var FFZ = window.FrankerFaceZ, CSS = /\.([\w\-_]+)\s*?\{content:\s*?"([^"]+)";\s*?background-image:\s*?url\("([^"]+)"\);\s*?height:\s*?(\d+)px;\s*?width:\s*?(\d+)px;\s*?margin:([^;}]+);?([^}]*)\}/mg, + MOD_CSS = /[^\n}]*\.badges\s+\.moderator\s*{\s*background-image:\s*url\(\s*['"]([^'"]+)['"][^}]+(?:}|$)/, constants = require('./constants'), utils = require('./utils'), @@ -173,7 +174,7 @@ FFZ.prototype._legacy_load_set = function(set_id, callback, tries) { FFZ.prototype._legacy_load_css = function(set_id, callback, data) { var emotes = {}, output = {id: set_id, emotes: emotes, extra_css: null}, f = this; - data.replace(CSS, function(match, klass, name, path, height, width, margins, extra) { + data = data.replace(CSS, function(match, klass, name, path, height, width, margins, extra) { height = parseInt(height); width = parseInt(width); margins = check_margins(margins, height); var hidden = path.substr(path.lastIndexOf("/") + 1, 1) === ".", @@ -182,7 +183,15 @@ FFZ.prototype._legacy_load_css = function(set_id, callback, data) { emotes[id] = emote; return ""; - }); + }).trim(); + + if ( data ) + data.replace(MOD_CSS, function(match, url) { + if ( output.icon || url.substr(-11) !== 'modicon.png' ) + return; + + output.icon = url; + }); this._load_set_json(set_id, callback, output); } \ No newline at end of file diff --git a/src/betterttv.js b/src/ext/betterttv.js similarity index 95% rename from src/betterttv.js rename to src/ext/betterttv.js index bf492794..eb631ec9 100644 --- a/src/betterttv.js +++ b/src/ext/betterttv.js @@ -9,7 +9,7 @@ var FFZ = window.FrankerFaceZ, FFZ.prototype.find_bttv = function(increment, delay) { this.has_bttv = false; if ( window.BTTVLOADED ) - return this.setup_bttv(); + return this.setup_bttv(delay||0); if ( delay >= 60000 ) this.log("BetterTTV was not detected after 60 seconds."); @@ -19,8 +19,8 @@ FFZ.prototype.find_bttv = function(increment, delay) { } -FFZ.prototype.setup_bttv = function() { - this.log("BetterTTV was detected. Hooking."); +FFZ.prototype.setup_bttv = function(delay) { + this.log("BetterTTV was detected after " + delay + "ms. Hooking."); this.has_bttv = true; this.track('setCustomVariable', '3', 'BetterTTV', BetterTTV.info.versionString()); diff --git a/src/ext/emote_menu.js b/src/ext/emote_menu.js new file mode 100644 index 00000000..3d38c55e --- /dev/null +++ b/src/ext/emote_menu.js @@ -0,0 +1,74 @@ +var FFZ = window.FrankerFaceZ; + + +// -------------------- +// Initialization +// -------------------- + +FFZ.prototype.find_emote_menu = function(increment, delay) { + this.has_emote_menu = false; + if ( window.emoteMenu && emoteMenu.registerEmoteGetter ) + return this.setup_emote_menu(delay||0); + + if ( delay >= 60000 ) + this.log("Emote Menu for Twitch was not detected after 60 seconds."); + else + setTimeout(this.find_emote_menu.bind(this, increment, (delay||0) + increment), + increment); +} + + +FFZ.prototype.setup_emote_menu = function(delay) { + this.log("Emote Menu for Twitch was detected after " + delay + "ms. Registering emote enumerator."); + emoteMenu.registerEmoteGetter("FrankerFaceZ", this._emote_menu_enumerator.bind(this)); +} + + +// -------------------- +// Emote Enumerator +// -------------------- + +FFZ.prototype._emote_menu_enumerator = function() { + var twitch_user = this.get_user(), + user_id = twitch_user ? twitch_user.login : null, + controller = App.__container__.lookup('controller:chat'), + room_id = controller ? controller.get('currentRoom.id') : null, + sets = this.getEmotes(user_id, room_id), + emotes = []; + + for(var x = 0; x < sets.length; x++) { + var set = this.emote_sets[sets[x]]; + if ( ! set || ! set.emotes ) + continue; + + for(var emote_id in set.emotes) { + if ( ! set.emotes.hasOwnProperty(emote_id) ) + continue; + + var emote = set.emotes[emote_id]; + if ( emote.hidden ) + continue; + + // TODO: Stop having to calculate this here. + var title = set.title, badge = set.icon || null; + if ( ! title ) { + if ( set.id == "global" ) + title = "FrankerFaceZ Global Emotes"; + + else if ( set.id == "globalevent" ) + title = "FrankerFaceZ Event Emotes"; + + else if ( this.feature_friday && set.id == this.feature_friday.set ) + title = "FrankerFaceZ Feature Friday: " + this.feature_friday.channel; + + else + title = "FrankerFaceZ Set: " + FFZ.get_capitalization(set.id); + } + + emotes.push({text: emote.name, url: emote.url, + hidden: false, channel: title, badge: badge}); + } + } + + return emotes; +} \ No newline at end of file diff --git a/src/main.js b/src/main.js index 2615fb4b..f2117ee3 100644 --- a/src/main.js +++ b/src/main.js @@ -70,12 +70,14 @@ require('./ember/room'); require('./ember/line'); require('./ember/chatview'); require('./ember/viewers'); +//require('./ember/teams'); require('./tracking'); require('./debug'); -require('./betterttv'); +require('./ext/betterttv'); +require('./ext/emote_menu'); require('./featurefriday'); @@ -113,11 +115,11 @@ FFZ.prototype.initialize = function(increment, delay) { return; } - this.setup(delay); + this.setup_ember(delay); } -FFZ.prototype.setup = function(delay) { +FFZ.prototype.setup_ember = function(delay) { var start = (window.performance && performance.now) ? performance.now() : Date.now(); this.log("Found Twitch application after " + (delay||0) + " ms in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info); @@ -129,40 +131,28 @@ FFZ.prototype.setup = function(delay) { localStorage.removeItem(key); } - // Store the capitalization of our own name. - var user = this.get_user(); - if ( user && user.name ) - FFZ.capitalization[user.login] = [user.name, Date.now()]; - - // Initialize all the modules. - try { - this.ws_create(); - this.setup_emoticons(); - this.setup_badges(); + this.ws_create(); + this.setup_emoticons(); + this.setup_badges(); - this.setup_piwik(); + this.setup_piwik(); - this.setup_router(); - this.setup_room(); - this.setup_line(); - this.setup_chatview(); - this.setup_viewers(); + this.setup_router(); + this.setup_room(); + this.setup_line(); + this.setup_chatview(); + this.setup_viewers(); - this.setup_css(); - this.setup_menu(); + //this.setup_teams(); - this.find_bttv(10); + this.setup_css(); + this.setup_menu(); - this.check_ff(); + this.find_bttv(10); + this.find_emote_menu(10); - } catch(err) { - this.log("An error occurred while starting FrankerFaceZ: " + err); - return; - } - - if ( window.console && console.time ) - console.timeEnd("FrankerFaceZ Initialization"); + this.check_ff(); var end = (window.performance && performance.now) ? performance.now() : Date.now(), duration = end - start; diff --git a/src/socket.js b/src/socket.js index e72a2cb4..ffb84135 100644 --- a/src/socket.js +++ b/src/socket.js @@ -15,6 +15,7 @@ FFZ.prototype.ws_create = function() { this._ws_last_req = 0; this._ws_callbacks = {}; + this._ws_pending = this._ws_pending || []; var ws = this._ws_sock = new WebSocket("ws://ffz.stendec.me/"); @@ -30,6 +31,15 @@ FFZ.prototype.ws_create = function() { // Send the current rooms. for(var room_id in f.rooms) f.ws_send("sub", room_id); + + // Send any pending commands. + var pending = f._ws_pending; + f._ws_pending = []; + + for(var i=0; i < pending.length; i++) { + var d = pending[i]; + f.ws_send(d[0], d[1], d[2]); + } } ws.onclose = function(e) { @@ -79,8 +89,15 @@ FFZ.prototype.ws_create = function() { } -FFZ.prototype.ws_send = function(func, data, callback) { - if ( ! this._ws_open ) return false; +FFZ.prototype.ws_send = function(func, data, callback, can_wait) { + if ( ! this._ws_open ) { + if ( can_wait ) { + var pending = this._ws_pending = this._ws_pending || []; + pending.push([func, data, callback]); + return true; + } else + return false; + } var request = ++this._ws_last_req; data = data !== undefined ? " " + JSON.stringify(data) : ""; diff --git a/src/ui/menu_button.js b/src/ui/menu_button.js index 2f571ed3..8a2fe0d0 100644 --- a/src/ui/menu_button.js +++ b/src/ui/menu_button.js @@ -20,7 +20,8 @@ FFZ.prototype.build_ui_link = function(view) { FFZ.prototype.update_ui_link = function(link) { var controller = App.__container__.lookup('controller:chat'); link = link || document.querySelector('a.ffz-ui-toggle'); - if ( !link || !controller ) return this.log("No button."); + if ( !link || !controller ) + return; var room_id = controller.get('currentRoom.id'), room = this.rooms[room_id],