diff --git a/script.js b/script.js index c89648a3..cb882c2d 100644 --- a/script.js +++ b/script.js @@ -190,7 +190,7 @@ FFZ.prototype._legacy_parse_donors = function(data) { this.log("Added donor badge to " + utils.number_commas(count) + " users."); } -},{"./constants":3,"./utils":18}],2:[function(require,module,exports){ +},{"./constants":3,"./utils":19}],2:[function(require,module,exports){ var FFZ = window.FrankerFaceZ, SENDER_REGEX = /(\sdata-sender="[^"]*"(?=>))/; @@ -292,7 +292,7 @@ FFZ.prototype.setup_bttv = function() { // Why is emote parsing so bad? ;_; _.each(emotes, function(emote) { - var eo = ['' + emote.title + ''], + var eo = ['' + emote.name + ''], old_tokens = tokens; tokens = []; @@ -453,7 +453,9 @@ FFZ.prototype.setup_line = function() { el.setAttribute('data-sender', user); f.render_badge(this); - f.capitalize(this, user); + + if ( localStorage['ffzCapitalize'] != 'false' ) + f.capitalize(this, user); } }); @@ -497,6 +499,23 @@ FFZ.prototype.capitalize = function(view, user) { } +FFZ.chat_commands.capitalization = function(room, args) { + var enabled, args = args && args.length ? args[0].toLowerCase() : null; + if ( args == "y" || args == "yes" || args == "true" || args == "on" ) + enabled = true; + else if ( args == "n" || args == "no" || args == "false" || args == "off" ) + enabled = false; + + if ( enabled === undefined ) + return "Chat Name Capitalization is currently " + (localStorage.ffzCapitalize != "false" ? "enabled." : "disabled."); + + localStorage.ffzCapitalize = enabled; + return "Chat Name Capitalization is now " + (enabled ? "enabled." : "disabled."); +} + +FFZ.chat_commands.capitalization.help = "Usage: /ffz capitalization \nEnable or disable Chat Name Capitalization. This setting does not work with BetterTTV."; + + // --------------------- // Emoticon Replacement // --------------------- @@ -540,7 +559,7 @@ FFZ.prototype._emoticonize = function(controller, tokens) { // emoticon. _.each(emotes, function(emote) { //var eo = {isEmoticon:true, cls: emote.klass}; - var eo = {isEmoticon:true, cls: emote.klass, emoticonSrc: emote.url, altText: emote.name}; + var eo = {isEmoticon:true, cls: emote.klass, emoticonSrc: emote.url, altText: (emote.hidden ? "???" : emote.name)}; tokens = _.compact(_.flatten(_.map(tokens, function(token) { if ( _.isObject(token) ) @@ -834,7 +853,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":3,"../utils":18}],8:[function(require,module,exports){ +},{"../constants":3,"../utils":19}],8:[function(require,module,exports){ var FFZ = window.FrankerFaceZ; @@ -1091,7 +1110,7 @@ FFZ.prototype._legacy_load_css = function(set_id, callback, data) { this._load_set_json(set_id, callback, output); } -},{"./constants":3,"./utils":18}],10:[function(require,module,exports){ +},{"./constants":3,"./utils":19}],10:[function(require,module,exports){ // Modify Array and others. require('./shims'); @@ -1168,6 +1187,8 @@ require('./debug'); require('./betterttv'); +require('./featurefriday'); + require('./ui/styles'); require('./ui/notifications'); require('./ui/viewer_count'); @@ -1238,6 +1259,7 @@ FFZ.prototype.setup = function(delay) { this.find_bttv(10); + this.check_ff(); } catch(err) { this.log("An error occurred while starting FrankerFaceZ: " + err); @@ -1252,7 +1274,153 @@ FFZ.prototype.setup = function(delay) { this.log("Initialization complete in " + duration + "ms"); } -},{"./badges":1,"./betterttv":2,"./debug":4,"./ember/chatview":5,"./ember/line":6,"./ember/room":7,"./ember/viewers":8,"./emoticons":9,"./shims":11,"./socket":12,"./ui/menu":13,"./ui/menu_button":14,"./ui/notifications":15,"./ui/styles":16,"./ui/viewer_count":17}],11:[function(require,module,exports){ +},{"./badges":1,"./betterttv":2,"./debug":4,"./ember/chatview":5,"./ember/line":6,"./ember/room":7,"./ember/viewers":8,"./emoticons":9,"./featurefriday":11,"./shims":12,"./socket":13,"./ui/menu":14,"./ui/menu_button":15,"./ui/notifications":16,"./ui/styles":17,"./ui/viewer_count":18}],11:[function(require,module,exports){ +var FFZ = window.FrankerFaceZ, + constants = require('./constants'); + + +// -------------------- +// Initialization +// -------------------- + +FFZ.prototype.feature_friday = null; + + +// -------------------- +// Check FF +// -------------------- + +FFZ.prototype.check_ff = function(tries) { + if ( ! tries ) + this.log("Checking for Feature Friday data..."); + + var f = this; + jQuery.getJSON(constants.SERVER + "script/event.json") + .done(function(data) { + return f._load_ff(data); + + }).fail(function(data) { + if ( data.status == 404 ) + return f._load_ff(null); + + tries = tries || 0; + tries++; + if ( tries < 10 ) + return setTimeout(f.check_ff.bind(this, tries), 250); + + return f._load_ff(null); + }); +} + + +FFZ.ws_commands.reload_ff = function() { + this.check_ff(); +} + + +// -------------------- +// Rendering UI +// -------------------- + +FFZ.prototype._feature_friday_ui = function(room_id, parent, view) { + if ( ! this.feature_friday || this.feature_friday.channel == room_id ) + return; + + this._emotes_for_sets(parent, view, [this.feature_friday.set], "Feature Friday"); + + // Before we add the button, make sure the channel isn't the + // current channel. + var Channel = App.__container__.lookup('controller:channel'); + if ( Channel && Channel.get('id') == this.feature_friday.channel ) + return; + + + var ff = this.feature_friday, + btnc = document.createElement('div'), + btn = document.createElement('a'); + + btnc.className = 'chat-menu-content'; + btnc.style.textAlign = 'center'; + + var message = ff.display_name + (ff.live ? " is live now!" : ""); + + btn.className = 'button primary'; + btn.classList.toggle('live', ff.live); + btn.classList.toggle('blue', this.has_bttv && BetterTTV.settings.get('showBlueButtons')); + + btn.href = "http://www.twitch.tv/" + ff.channel; + btn.title = message; + btn.target = "_new"; + btn.innerHTML = "" + message + ""; + + btnc.appendChild(btn); + parent.appendChild(btnc); +} + + +// -------------------- +// Loading Data +// -------------------- + +FFZ.prototype._load_ff = function(data) { + // Check for previous Feature Friday data and remove it. + if ( this.feature_friday ) { + // Remove the global set, delete the data, and reset the UI link. + this.global_sets.removeObject(this.feature_friday.set); + + var set = this.emote_sets[this.feature_friday.set]; + if ( set ) + set.global = false; + + this.feature_friday = null; + this.update_ui_link(); + } + + // If there's no data, just leave. + if ( ! data || ! data.set || ! data.channel ) + return; + + // We have our data! Set it up. + this.feature_friday = {set: data.set, channel: data.channel, live: false, + display_name: FFZ.get_capitalization(data.channel, this._update_ff_name.bind(this))}; + + // Add the set. + this.global_sets.push(data.set); + this.load_set(data.set, this._update_ff_set.bind(this)); + + // Check to see if the channel is live. + this._update_ff_live(); +} + + +FFZ.prototype._update_ff_live = function() { + if ( ! this.feature_friday ) + return; + + var f = this; + Twitch.api.get("streams/" + this.feature_friday.channel) + .done(function(data) { + f.feature_friday.live = data.stream != null; + f.update_ui_link(); + }) + .always(function() { + f.feature_friday.timer = setTimeout(f._update_ff_live.bind(f), 120000); + }); +} + + +FFZ.prototype._update_ff_set = function(success, set) { + // Prevent the set from being unloaded. + if ( set ) + set.global = true; +} + + +FFZ.prototype._update_ff_name = function(name) { + if ( this.feature_friday ) + this.feature_friday.display_name = name; +} +},{"./constants":3}],12:[function(require,module,exports){ Array.prototype.equals = function (array) { // if the other array is a falsy value, return if (!array) @@ -1278,7 +1446,7 @@ Array.prototype.equals = function (array) { } -},{}],12:[function(require,module,exports){ +},{}],13:[function(require,module,exports){ var FFZ = window.FrankerFaceZ; FFZ.prototype._ws_open = false; @@ -1372,7 +1540,7 @@ FFZ.prototype.ws_send = function(func, data, callback) { this._ws_sock.send(request + " " + func + data); return request; } -},{}],13:[function(require,module,exports){ +},{}],14:[function(require,module,exports){ var FFZ = window.FrankerFaceZ; @@ -1446,6 +1614,8 @@ FFZ.prototype.build_ui_popup = function(view) { else btn.addEventListener('click', this._add_emote.bind(this, view, "To view this channel's emoticons, get FrankerFaceZ from http://www.frankerfacez.com")); + // Feature Friday! + this._feature_friday_ui(room_id, inner, view); // Add the menu to the DOM. this._popup = container; @@ -1513,7 +1683,7 @@ FFZ.prototype._add_emote = function(view, emote) { room.set('messageToSend', current_text + (emote.name || emote)); } -},{}],14:[function(require,module,exports){ +},{}],15:[function(require,module,exports){ var FFZ = window.FrankerFaceZ, constants = require('../constants'); @@ -1543,7 +1713,8 @@ FFZ.prototype.update_ui_link = function(link) { has_emotes = false, dark = (this.has_bttv ? BetterTTV.settings.get('darkenedMode') : false), - blue = (this.has_bttv ? BetterTTV.settings.get('showBlueButtons') : false); + blue = (this.has_bttv ? BetterTTV.settings.get('showBlueButtons') : false), + live = (this.feature_friday && this.feature_friday.live); // Check for emoticons. @@ -1558,10 +1729,11 @@ FFZ.prototype.update_ui_link = function(link) { } link.classList.toggle('no-emotes', ! has_emotes); + link.classList.toggle('live', live); link.classList.toggle('dark', dark); link.classList.toggle('blue', blue); } -},{"../constants":3}],15:[function(require,module,exports){ +},{"../constants":3}],16:[function(require,module,exports){ var FFZ = window.FrankerFaceZ; FFZ.prototype.show_notification = function(message) { @@ -1577,7 +1749,7 @@ FFZ.prototype.show_notification = function(message) { FFZ.ws_commands.message = function(message) { this.show_notification(message); } -},{}],16:[function(require,module,exports){ +},{}],17:[function(require,module,exports){ var FFZ = window.FrankerFaceZ, constants = require('../constants'); @@ -1602,7 +1774,7 @@ FFZ.prototype.setup_css = function() { } }; } -},{"../constants":3}],17:[function(require,module,exports){ +},{"../constants":3}],18:[function(require,module,exports){ var FFZ = window.FrankerFaceZ, constants = require('../constants'), utils = require('../utils'); @@ -1639,7 +1811,7 @@ FFZ.ws_commands.viewers = function(data) { jQuery(view_count).tipsy(); } } -},{"../constants":3,"../utils":18}],18:[function(require,module,exports){ +},{"../constants":3,"../utils":19}],19:[function(require,module,exports){ var FFZ = window.FrankerFaceZ, constants = require('./constants'); diff --git a/script.min.js b/script.min.js index 9c94a7b5..e9336ab4 100644 --- a/script.min.js +++ b/script.min.js @@ -1 +1 @@ -!function(e){!function t(e,o,s){function n(i,a){if(!o[i]){if(!e[i]){var c="function"==typeof require&&require;if(!a&&c)return c(i,!0);if(r)return r(i,!0);throw new Error("Cannot find module '"+i+"'")}var l=o[i]={exports:{}};e[i][0].call(l.exports,function(t){var o=e[i][1][t];return n(o?o:t)},l,l.exports,t,e,o,s)}return o[i].exports}for(var r="function"==typeof require&&require,i=0;ie?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;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 r=BetterTTV.chat.templates.privmsg;BetterTTV.chat.templates.privmsg=function(e,n,i,a,c){t.bttv_badges(c);var l=r(e,n,i,a,c);return l.replace(o,'$1 data-room="'+s+'"')};var i,a=BetterTTV.chat.templates.message;BetterTTV.chat.templates.message=function(e,t,o,s){i=e;var n=a(e,t,o,s);return i=null,n};var c=BetterTTV.chat.templates.emoticonize;BetterTTV.chat.templates.emoticonize=function(e,o){var n=c(e,o),r=t.users[i],a=t.rooms[s],l=_.union(r&&r.sets||[],a&&a.sets||[],t.global_sets),o=[];return _.each(l,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.title+''],o=n;if(n=[],!o||!o.length)return n;for(var s=0;s'+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"),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),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.prototype._emoticonize=function(e,t){var o=e.get("parentController.model.id"),s=e.get("model.from"),n=this.users[s],r=this.rooms[o],i=this,a=_.union(n&&n.sets||[],r&&r.sets||[],i.global_sets),c=[];return _.each(a,function(e){var o=i.emote_sets[e];o&&_.each(o.emotes,function(e){_.any(t,function(t){return _.isString(t)&&t.match(e.regex)})&&c.push(e)})}),c.length?("string"==typeof t&&(t=[t]),_.each(c,function(e){var o={isEmoticon:!0,cls:e.klass,emoticonSrc:e.url,altText: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}},{}],7:[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*['"]([^'"]+)['"][^}]+(?:}|$)/,r=/^_([^_]+)_\d+$/,i=t("../constants"),a=t("../utils"),c=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 i=e,a=i.match(r);a&&a[1]&&(i=a[1]);var c={id:e,menu_sets:[i],sets:[i],moderator_badge:null,css:null};return o&&(o=o.replace(s,"").trim()),o&&(o=o.replace(n,function(e,t){return c.moderator_badge||"modicon.png"!==t.substr(-11)?e:(c.moderator_badge=t,"")})),c.css=o||null,this._load_room_json(e,t,c)}},{"../constants":3,"../utils":18}],8:[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={},r=null,i=App.__container__.lookup("controller:channel"),a=this.get("parentController.model.id"),c=i&&i.get("id");if(c){var l=i.get("display_name");l&&(t.capitalization[c]=[l,Date.now()])}a!=c&&(c=null);for(var d=0;do?this._legacy_load_set(e,t,o):t&&t(!1))})},o.prototype._legacy_load_css=function(e,t,o){var n={},r={id:e,emotes:n,extra_css:null},i=this;o.replace(s,function(e,t,o,s,r,c,l,d){r=parseInt(r),c=parseInt(c),l=a(l,r);var u="."===s.substr(s.lastIndexOf("/")+1,1),h=++i._last_emote_id,m={id:h,hidden:u,name:o,height:r,width:c,url:s,margins:l,extra_css:d};return n[h]=m,""}),this._load_set_json(e,t,r)}},{"./constants":3,"./utils":18}],10:[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/room"),t("./ember/line"),t("./ember/chatview"),t("./ember/viewers"),t("./debug"),t("./betterttv"),t("./ui/styles"),t("./ui/notifications"),t("./ui/viewer_count"),t("./ui/menu_button"),t("./ui/menu"),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 r=this.get_user();r&&r.name&&(o.capitalization[r.login]=[r.name,Date.now()]);try{this.ws_create(),this.setup_emoticons(),this.setup_badges(),this.setup_room(),this.setup_line(),this.setup_chatview(),this.setup_viewers(),this.setup_css(),this.setup_menu(),this.find_bttv(10)}catch(i){return void this.log("An error occurred while starting FrankerFaceZ: "+i)}e.console&&console.time&&console.timeEnd("FrankerFaceZ Initialization");var a=e.performance&&performance.now?performance.now():Date.now(),c=a-s;this.log("Initialization complete in "+c+"ms")}},{"./badges":1,"./betterttv":2,"./debug":4,"./ember/chatview":5,"./ember/line":6,"./ember/room":7,"./ember/viewers":8,"./emoticons":9,"./shims":11,"./socket":12,"./ui/menu":13,"./ui/menu_button":14,"./ui/notifications":15,"./ui/styles":16,"./ui/viewer_count":17}],11:[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}},{}],12:[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,r=o.data.indexOf(" "),i=o.data.substr(r+1),a=parseInt(o.data.slice(0,r));if(r=i.indexOf(" "),-1===r&&(r=i.length),s=i.slice(0,r),i=i.substr(r+1),i&&(n=JSON.parse(i)),-1===a){var c=t.ws_commands[s];c?c.bind(e)(n):e.log("Invalid command: "+s,n)}else{var l="True"===s,d=e._ws_callbacks[a];e.log("Socket Reply to "+a+" - "+(l?"SUCCESS":"FAIL"),n),d&&(delete e._ws_callbacks[a],d(l,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}},{}],13:[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"),r=this.rooms[n];this.log("Menu for Room: "+n,r);var i=document.createElement("a");i.className="button glyph-only ffz-button",i.title="Advertise for FrankerFaceZ in chat!",i.href="#",i.innerHTML='';var a=document.createElement("div");a.className="list-header first",a.appendChild(i),a.appendChild(document.createTextNode("FrankerFaceZ")),s.appendChild(a);var c=this._emotes_for_sets(s,e,r&&r.menu_sets||[]);0===c?i.addEventListener("click",this._add_emote.bind(this,e,"To use custom emoticons in tons of channels, get FrankerFaceZ from http://www.frankerfacez.com")):i.addEventListener("click",this._add_emote.bind(this,e,"To view this channel's emoticons, get FrankerFaceZ from http://www.frankerfacez.com")),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 r=document.createElement("div");r.className="list-header",r.appendChild(document.createTextNode(s)),n&&r.appendChild(n),e.appendChild(r)}var i=document.createElement("div"),a=0;i.className="emoticon-grid";for(var c=0;c0){n=!0;break}}e.classList.toggle("no-emotes",!n),e.classList.toggle("dark",r),e.classList.toggle("blue",i)}},{"../constants":3}],15:[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)}},{}],16:[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":3}],17:[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],r=App.__container__.lookup("controller:channel"),i=r&&r.get&&r.get("id");if(i===t){var a=document.querySelector(".channel-stats .ffz.stat"),c=s.ZREKNARF+" "+n.number_commas(o);if(a)a.innerHTML=c;else{var l=document.querySelector(".channel-stats");if(!l)return;a=document.createElement("span"),a.className="ffz stat",a.title="Viewers with FrankerFaceZ",a.innerHTML=c,l.appendChild(a),jQuery(a).tipsy()}}}},{"../constants":3,"../utils":18}],18:[function(t,o){e.FrankerFaceZ,t("./constants");o.exports={update_css:function(e,t,o){var s=e.innerHTML,n="/*BEGIN "+t+"*/",r="/*END "+t+"*/",i=s.indexOf(n),a=s.indexOf(r),c=-1!==i&&-1!==a&&a>i;(c||o)&&(c&&(s=s.substr(0,i)+s.substr(a+r.length)),o&&(s+=n+o+r),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":3}]},{},[10]),e.ffz=new FrankerFaceZ}(window); \ No newline at end of file +!function(e){!function t(e,o,s){function n(i,a){if(!o[i]){if(!e[i]){var l="function"==typeof require&&require;if(!a&&l)return l(i,!0);if(r)return r(i,!0);throw new Error("Cannot find module '"+i+"'")}var c=o[i]={exports:{}};e[i][0].call(c.exports,function(t){var o=e[i][1][t];return n(o?o:t)},c,c.exports,t,e,o,s)}return o[i].exports}for(var r="function"==typeof require&&require,i=0;ie?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;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 r=BetterTTV.chat.templates.privmsg;BetterTTV.chat.templates.privmsg=function(e,n,i,a,l){t.bttv_badges(l);var c=r(e,n,i,a,l);return c.replace(o,'$1 data-room="'+s+'"')};var i,a=BetterTTV.chat.templates.message;BetterTTV.chat.templates.message=function(e,t,o,s){i=e;var n=a(e,t,o,s);return i=null,n};var l=BetterTTV.chat.templates.emoticonize;BetterTTV.chat.templates.emoticonize=function(e,o){var n=l(e,o),r=t.users[i],a=t.rooms[s],c=_.union(r&&r.sets||[],a&&a.sets||[],t.global_sets),o=[];return _.each(c,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;s'+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"),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.users[s],r=this.rooms[o],i=this,a=_.union(n&&n.sets||[],r&&r.sets||[],i.global_sets),l=[];return _.each(a,function(e){var o=i.emote_sets[e];o&&_.each(o.emotes,function(e){_.any(t,function(t){return _.isString(t)&&t.match(e.regex)})&&l.push(e)})}),l.length?("string"==typeof t&&(t=[t]),_.each(l,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}},{}],7:[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*['"]([^'"]+)['"][^}]+(?:}|$)/,r=/^_([^_]+)_\d+$/,i=t("../constants"),a=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 i=e,a=i.match(r);a&&a[1]&&(i=a[1]);var l={id:e,menu_sets:[i],sets:[i],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":3,"../utils":19}],8:[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={},r=null,i=App.__container__.lookup("controller:channel"),a=this.get("parentController.model.id"),l=i&&i.get("id");if(l){var c=i.get("display_name");c&&(t.capitalization[l]=[c,Date.now()])}a!=l&&(l=null);for(var d=0;do?this._legacy_load_set(e,t,o):t&&t(!1))})},o.prototype._legacy_load_css=function(e,t,o){var n={},r={id:e,emotes:n,extra_css:null},i=this;o.replace(s,function(e,t,o,s,r,l,c,d){r=parseInt(r),l=parseInt(l),c=a(c,r);var u="."===s.substr(s.lastIndexOf("/")+1,1),h=++i._last_emote_id,f={id:h,hidden:u,name:o,height:r,width:l,url:s,margins:c,extra_css:d};return n[h]=f,""}),this._load_set_json(e,t,r)}},{"./constants":3,"./utils":19}],10:[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/room"),t("./ember/line"),t("./ember/chatview"),t("./ember/viewers"),t("./debug"),t("./betterttv"),t("./featurefriday"),t("./ui/styles"),t("./ui/notifications"),t("./ui/viewer_count"),t("./ui/menu_button"),t("./ui/menu"),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 r=this.get_user();r&&r.name&&(o.capitalization[r.login]=[r.name,Date.now()]);try{this.ws_create(),this.setup_emoticons(),this.setup_badges(),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(i){return void this.log("An error occurred while starting FrankerFaceZ: "+i)}e.console&&console.time&&console.timeEnd("FrankerFaceZ Initialization");var a=e.performance&&performance.now?performance.now():Date.now(),l=a-s;this.log("Initialization complete in "+l+"ms")}},{"./badges":1,"./betterttv":2,"./debug":4,"./ember/chatview":5,"./ember/line":6,"./ember/room":7,"./ember/viewers":8,"./emoticons":9,"./featurefriday":11,"./shims":12,"./socket":13,"./ui/menu":14,"./ui/menu_button":15,"./ui/notifications":16,"./ui/styles":17,"./ui/viewer_count":18}],11:[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...");var t=this;jQuery.getJSON(s.SERVER+"script/event.json").done(function(e){return t._load_ff(e)}).fail(function(o){return 404==o.status?t._load_ff(null):(e=e||0,e++,10>e?setTimeout(t.check_ff.bind(this,e),250):t._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,r=document.createElement("div"),i=document.createElement("a");r.className="chat-menu-content",r.style.textAlign="center";var a=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=a,i.target="_new",i.innerHTML=""+a+"",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":3}],12:[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}},{}],13:[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,r=o.data.indexOf(" "),i=o.data.substr(r+1),a=parseInt(o.data.slice(0,r));if(r=i.indexOf(" "),-1===r&&(r=i.length),s=i.slice(0,r),i=i.substr(r+1),i&&(n=JSON.parse(i)),-1===a){var l=t.ws_commands[s];l?l.bind(e)(n):e.log("Invalid command: "+s,n)}else{var c="True"===s,d=e._ws_callbacks[a];e.log("Socket Reply to "+a+" - "+(c?"SUCCESS":"FAIL"),n),d&&(delete e._ws_callbacks[a],d(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}},{}],14:[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"),r=this.rooms[n];this.log("Menu for Room: "+n,r);var i=document.createElement("a");i.className="button glyph-only ffz-button",i.title="Advertise for FrankerFaceZ in chat!",i.href="#",i.innerHTML='';var a=document.createElement("div");a.className="list-header first",a.appendChild(i),a.appendChild(document.createTextNode("FrankerFaceZ")),s.appendChild(a);var l=this._emotes_for_sets(s,e,r&&r.menu_sets||[]);0===l?i.addEventListener("click",this._add_emote.bind(this,e,"To use custom emoticons in tons of channels, get FrankerFaceZ from http://www.frankerfacez.com")):i.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 r=document.createElement("div");r.className="list-header",r.appendChild(document.createTextNode(s)),n&&r.appendChild(n),e.appendChild(r)}var i=document.createElement("div"),a=0;i.className="emoticon-grid";for(var l=0;l0){n=!0;break}}e.classList.toggle("no-emotes",!n),e.classList.toggle("live",a),e.classList.toggle("dark",r),e.classList.toggle("blue",i)}},{"../constants":3}],16:[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)}},{}],17:[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":3}],18:[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],r=App.__container__.lookup("controller:channel"),i=r&&r.get&&r.get("id");if(i===t){var a=document.querySelector(".channel-stats .ffz.stat"),l=s.ZREKNARF+" "+n.number_commas(o);if(a)a.innerHTML=l;else{var c=document.querySelector(".channel-stats");if(!c)return;a=document.createElement("span"),a.className="ffz stat",a.title="Viewers with FrankerFaceZ",a.innerHTML=l,c.appendChild(a),jQuery(a).tipsy()}}}},{"../constants":3,"../utils":19}],19:[function(t,o){e.FrankerFaceZ,t("./constants");o.exports={update_css:function(e,t,o){var s=e.innerHTML,n="/*BEGIN "+t+"*/",r="/*END "+t+"*/",i=s.indexOf(n),a=s.indexOf(r),l=-1!==i&&-1!==a&&a>i;(l||o)&&(l&&(s=s.substr(0,i)+s.substr(a+r.length)),o&&(s+=n+o+r),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":3}]},{},[10]),e.ffz=new FrankerFaceZ}(window); \ No newline at end of file diff --git a/src/betterttv.js b/src/betterttv.js index bd5b1581..d342bcaf 100644 --- a/src/betterttv.js +++ b/src/betterttv.js @@ -99,7 +99,7 @@ FFZ.prototype.setup_bttv = function() { // Why is emote parsing so bad? ;_; _.each(emotes, function(emote) { - var eo = ['' + emote.title + ''], + var eo = ['' + emote.name + ''], old_tokens = tokens; tokens = []; diff --git a/src/ember/line.js b/src/ember/line.js index 21a4567e..83d0a4e0 100644 --- a/src/ember/line.js +++ b/src/ember/line.js @@ -35,7 +35,9 @@ FFZ.prototype.setup_line = function() { el.setAttribute('data-sender', user); f.render_badge(this); - f.capitalize(this, user); + + if ( localStorage['ffzCapitalize'] != 'false' ) + f.capitalize(this, user); } }); @@ -79,6 +81,23 @@ FFZ.prototype.capitalize = function(view, user) { } +FFZ.chat_commands.capitalization = function(room, args) { + var enabled, args = args && args.length ? args[0].toLowerCase() : null; + if ( args == "y" || args == "yes" || args == "true" || args == "on" ) + enabled = true; + else if ( args == "n" || args == "no" || args == "false" || args == "off" ) + enabled = false; + + if ( enabled === undefined ) + return "Chat Name Capitalization is currently " + (localStorage.ffzCapitalize != "false" ? "enabled." : "disabled."); + + localStorage.ffzCapitalize = enabled; + return "Chat Name Capitalization is now " + (enabled ? "enabled." : "disabled."); +} + +FFZ.chat_commands.capitalization.help = "Usage: /ffz capitalization \nEnable or disable Chat Name Capitalization. This setting does not work with BetterTTV."; + + // --------------------- // Emoticon Replacement // --------------------- @@ -122,7 +141,7 @@ FFZ.prototype._emoticonize = function(controller, tokens) { // emoticon. _.each(emotes, function(emote) { //var eo = {isEmoticon:true, cls: emote.klass}; - var eo = {isEmoticon:true, cls: emote.klass, emoticonSrc: emote.url, altText: emote.name}; + var eo = {isEmoticon:true, cls: emote.klass, emoticonSrc: emote.url, altText: (emote.hidden ? "???" : emote.name)}; tokens = _.compact(_.flatten(_.map(tokens, function(token) { if ( _.isObject(token) ) diff --git a/src/featurefriday.js b/src/featurefriday.js new file mode 100644 index 00000000..b03067aa --- /dev/null +++ b/src/featurefriday.js @@ -0,0 +1,145 @@ +var FFZ = window.FrankerFaceZ, + constants = require('./constants'); + + +// -------------------- +// Initialization +// -------------------- + +FFZ.prototype.feature_friday = null; + + +// -------------------- +// Check FF +// -------------------- + +FFZ.prototype.check_ff = function(tries) { + if ( ! tries ) + this.log("Checking for Feature Friday data..."); + + var f = this; + jQuery.getJSON(constants.SERVER + "script/event.json") + .done(function(data) { + return f._load_ff(data); + + }).fail(function(data) { + if ( data.status == 404 ) + return f._load_ff(null); + + tries = tries || 0; + tries++; + if ( tries < 10 ) + return setTimeout(f.check_ff.bind(this, tries), 250); + + return f._load_ff(null); + }); +} + + +FFZ.ws_commands.reload_ff = function() { + this.check_ff(); +} + + +// -------------------- +// Rendering UI +// -------------------- + +FFZ.prototype._feature_friday_ui = function(room_id, parent, view) { + if ( ! this.feature_friday || this.feature_friday.channel == room_id ) + return; + + this._emotes_for_sets(parent, view, [this.feature_friday.set], "Feature Friday"); + + // Before we add the button, make sure the channel isn't the + // current channel. + var Channel = App.__container__.lookup('controller:channel'); + if ( Channel && Channel.get('id') == this.feature_friday.channel ) + return; + + + var ff = this.feature_friday, + btnc = document.createElement('div'), + btn = document.createElement('a'); + + btnc.className = 'chat-menu-content'; + btnc.style.textAlign = 'center'; + + var message = ff.display_name + (ff.live ? " is live now!" : ""); + + btn.className = 'button primary'; + btn.classList.toggle('live', ff.live); + btn.classList.toggle('blue', this.has_bttv && BetterTTV.settings.get('showBlueButtons')); + + btn.href = "http://www.twitch.tv/" + ff.channel; + btn.title = message; + btn.target = "_new"; + btn.innerHTML = "" + message + ""; + + btnc.appendChild(btn); + parent.appendChild(btnc); +} + + +// -------------------- +// Loading Data +// -------------------- + +FFZ.prototype._load_ff = function(data) { + // Check for previous Feature Friday data and remove it. + if ( this.feature_friday ) { + // Remove the global set, delete the data, and reset the UI link. + this.global_sets.removeObject(this.feature_friday.set); + + var set = this.emote_sets[this.feature_friday.set]; + if ( set ) + set.global = false; + + this.feature_friday = null; + this.update_ui_link(); + } + + // If there's no data, just leave. + if ( ! data || ! data.set || ! data.channel ) + return; + + // We have our data! Set it up. + this.feature_friday = {set: data.set, channel: data.channel, live: false, + display_name: FFZ.get_capitalization(data.channel, this._update_ff_name.bind(this))}; + + // Add the set. + this.global_sets.push(data.set); + this.load_set(data.set, this._update_ff_set.bind(this)); + + // Check to see if the channel is live. + this._update_ff_live(); +} + + +FFZ.prototype._update_ff_live = function() { + if ( ! this.feature_friday ) + return; + + var f = this; + Twitch.api.get("streams/" + this.feature_friday.channel) + .done(function(data) { + f.feature_friday.live = data.stream != null; + f.update_ui_link(); + }) + .always(function() { + f.feature_friday.timer = setTimeout(f._update_ff_live.bind(f), 120000); + }); +} + + +FFZ.prototype._update_ff_set = function(success, set) { + // Prevent the set from being unloaded. + if ( set ) + set.global = true; +} + + +FFZ.prototype._update_ff_name = function(name) { + if ( this.feature_friday ) + this.feature_friday.display_name = name; +} \ No newline at end of file diff --git a/src/main.js b/src/main.js index b7de1b35..19ab9c4b 100644 --- a/src/main.js +++ b/src/main.js @@ -74,6 +74,8 @@ require('./debug'); require('./betterttv'); +require('./featurefriday'); + require('./ui/styles'); require('./ui/notifications'); require('./ui/viewer_count'); @@ -144,6 +146,7 @@ FFZ.prototype.setup = function(delay) { this.find_bttv(10); + this.check_ff(); } catch(err) { this.log("An error occurred while starting FrankerFaceZ: " + err); diff --git a/src/ui/menu.js b/src/ui/menu.js index 1fdaf2eb..1a6b64ea 100644 --- a/src/ui/menu.js +++ b/src/ui/menu.js @@ -71,6 +71,8 @@ FFZ.prototype.build_ui_popup = function(view) { else btn.addEventListener('click', this._add_emote.bind(this, view, "To view this channel's emoticons, get FrankerFaceZ from http://www.frankerfacez.com")); + // Feature Friday! + this._feature_friday_ui(room_id, inner, view); // Add the menu to the DOM. this._popup = container; diff --git a/src/ui/menu_button.js b/src/ui/menu_button.js index b2b847bd..2f571ed3 100644 --- a/src/ui/menu_button.js +++ b/src/ui/menu_button.js @@ -27,7 +27,8 @@ FFZ.prototype.update_ui_link = function(link) { has_emotes = false, dark = (this.has_bttv ? BetterTTV.settings.get('darkenedMode') : false), - blue = (this.has_bttv ? BetterTTV.settings.get('showBlueButtons') : false); + blue = (this.has_bttv ? BetterTTV.settings.get('showBlueButtons') : false), + live = (this.feature_friday && this.feature_friday.live); // Check for emoticons. @@ -42,6 +43,7 @@ FFZ.prototype.update_ui_link = function(link) { } link.classList.toggle('no-emotes', ! has_emotes); + link.classList.toggle('live', live); link.classList.toggle('dark', dark); link.classList.toggle('blue', blue); } \ No newline at end of file