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