From c9bd39de8ad0c2b56f2e1b00dcbc1f9bc8a01a28 Mon Sep 17 00:00:00 2001 From: SirStendec Date: Tue, 27 Jan 2015 17:05:51 -0500 Subject: [PATCH] Fix issue where a new WebSocket() call can crash the script if it fails. Fix positioning of theater mode status and action overlay. --- .gitignore | 3 ++- script.js | 27 +++++++++++++++++++++------ script.min.js | 4 ++-- src/socket.js | 11 +++++++++-- src/ui/menu.js | 16 ++++++++++++---- style.css | 4 +++- 6 files changed, 49 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index af54d5fa..0fb636f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules npm-debug.log -build \ No newline at end of file +build +Extension Building \ No newline at end of file diff --git a/script.js b/script.js index a7f74838..9c1c533c 100644 --- a/script.js +++ b/script.js @@ -1742,13 +1742,20 @@ FFZ.ws_commands = {}; // ---------------- FFZ.prototype.ws_create = function() { - var f = this; + var f = this, ws; 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/"); + try { + ws = this._ws_sock = new WebSocket("ws://ffz.stendec.me/"); + } catch(err) { + this._ws_exists = false; + return this.log("Error Creating WebSocket: " + err); + } + + this._ws_exists = true; ws.onopen = function(e) { f._ws_open = true; @@ -2041,10 +2048,18 @@ FFZ.prototype.build_ui_popup = function(view) { var c = this._emotes_for_sets(inner, view, room && room.menu_sets || []); - if ( c === 0 ) - btn.addEventListener('click', this._add_emote.bind(this, view, "To use custom emoticons in tons of channels, get FrankerFaceZ from http://www.frankerfacez.com")); - else - btn.addEventListener('click', this._add_emote.bind(this, view, "To view this channel's emoticons, get FrankerFaceZ from http://www.frankerfacez.com")); + if ( ! this._ws_exists ) { + btn.className = "button ffz-button primary"; + btn.innerHTML = "Server Error"; + btn.title = "FFZ Server Error"; + btn.addEventListener('click', alert.bind(window, "The FrankerFaceZ client was unable to create a WebSocket to communicate with the FrankerFaceZ server.\n\nThis is most likely due to your browser's configuration either disabling WebSockets entirely or limiting the number of simultaneous connections. Please ensure that WebSockets have not been disabled.")); + + } else { + if ( c === 0 ) + btn.addEventListener('click', this._add_emote.bind(this, view, "To use custom emoticons in tons of channels, get FrankerFaceZ from http://www.frankerfacez.com")); + 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); diff --git a/script.min.js b/script.min.js index d57cdfac..f65111c0 100644 --- a/script.min.js +++ b/script.min.js @@ -1,2 +1,2 @@ -!function(e){!function t(e,o,n){function s(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 s(o?o:t)},c,c.exports,t,e,o,n)}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+/),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,o=function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")};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(){var e=o._emoticonize(this,this._super()),t=o.get_user();return t&&this.get("model.from")==t.login||(e=o._mentionize(this,e)),e}.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()]),this.mention_words=localStorage.ffzMentionize?JSON.parse(localStorage.ffzMentionize):[]},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--,"function"==typeof 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._regex_cache={},t.get_regex=function(e){return t._regex_cache[e]=t._regex_cache[e]||RegExp("\\b"+o(e)+"\\b","i")},t.prototype._mentionize=function(e,o){return this.mention_words?("string"==typeof o&&(o=[o]),_.each(this.mention_words,function(e){var n={mentionedUser:e,own:!1};o=_.compact(_.flatten(_.map(o,function(o){if(_.isObject(o))return o;var s=o.split(t.get_regex(e)),r=[];return s.forEach(function(e,t){r.push(e),t!==s.length-1&&r.push(n)}),r})))}),o):o},t.chat_commands.mentionize=function(e,t){return t&&t.length&&(this.mention_words=t.join(" ").trim().split(/\W*,\W*/),1==this.mention_words.length&&"disable"==this.mention_words[0]&&(this.mention_words=[]),localStorage.ffzMentionize=JSON.stringify(this.mention_words)),this.mention_words.length?"The following words will be treated as mentions: "+this.mention_words.join(", "):"There are no words set that will be treated as mentions."},t.chat_commands.mentionize.help="Usage: /ffz mentionize \nSet a list of words that will also be treated as mentions and be displayed specially in chat.",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),i=[];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)})&&i.push(e)})}),i.length?("string"==typeof t&&(t=[t]),_.each(i,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+$/,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 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 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(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,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 u=0;uo?this._legacy_load_set(e,t,o):"function"==typeof t&&t(!1))})},o.prototype._legacy_load_css=function(e,t,o){var r={},i={id:e,emotes:r,extra_css:null},a=this;o=o.replace(n,function(e,t,o,n,s,i,c,u){s=parseInt(s),i=parseInt(i),c=l(c,s);var d="."===n.substr(n.lastIndexOf("/")+1,1),h=++a._last_emote_id,m={id:h,hidden:d,name:o,height:s,width:i,url:n,margins:c,extra_css:u};return r[h]=m,""}).trim(),o&&o.replace(s,function(e,t){i.icon||"modicon.png"!==t.substr(-11)||(i.icon=t)}),this._load_set_json(e,t,i)}},{"./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 i=BetterTTV.chat.templates.privmsg;BetterTTV.chat.templates.privmsg=function(e,t,r,a,l){n.bttv_badges(l);var c=i(e,t,r,a,l);return c.replace(o,'$1 data-room="'+s+'"')};var a,l=BetterTTV.chat.templates.message;BetterTTV.chat.templates.message=function(e,t,o,n){a=e;var s=l(e,t,o,n);return a=null,s};var c=BetterTTV.chat.templates.emoticonize;BetterTTV.chat.templates.emoticonize=function(e,t){var o=c(e,t),r=n.getEmotes(a,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),i=[],a=0;a=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(),i=r-n;this.log("Initialization complete in "+i+"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,i=document.createElement("div"),a=document.createElement("a");i.className="chat-menu-content",i.style.textAlign="center";var l=s.display_name+(s.live?" is live now!":"");a.className="button primary",a.classList.toggle("live",s.live),a.classList.toggle("blue",this.has_bttv&&BetterTTV.settings.get("showBlueButtons")),a.href="http://www.twitch.tv/"+s.channel,a.title=l,a.target="_new",a.innerHTML=""+l+"",a.addEventListener("click",function(){r.track("trackLink",this.href,"link")}),i.appendChild(a),t.appendChild(i)}}},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",a),e.classList.toggle("dark",r),e.classList.toggle("blue",i)}}},{"../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"),i=r&&r.get&&r.get("id");if(i===t){var a=document.querySelector(".channel-stats .ffz.stat"),l=n.ZREKNARF+" "+s.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":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+"*/",i=n.indexOf(s),a=n.indexOf(r),l=-1!==i&&-1!==a&&a>i;(l||o)&&(l&&(n=n.substr(0,i)+n.substr(a+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 +!function(e){!function t(e,o,n){function s(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 s(o?o:t)},c,c.exports,t,e,o,n)}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+/),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,o=function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")};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(){var e=o._emoticonize(this,this._super()),t=o.get_user();return t&&this.get("model.from")==t.login||(e=o._mentionize(this,e)),e}.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()]),this.mention_words=localStorage.ffzMentionize?JSON.parse(localStorage.ffzMentionize):[]},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--,"function"==typeof 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._regex_cache={},t.get_regex=function(e){return t._regex_cache[e]=t._regex_cache[e]||RegExp("\\b"+o(e)+"\\b","i")},t.prototype._mentionize=function(e,o){return this.mention_words?("string"==typeof o&&(o=[o]),_.each(this.mention_words,function(e){var n={mentionedUser:e,own:!1};o=_.compact(_.flatten(_.map(o,function(o){if(_.isObject(o))return o;var s=o.split(t.get_regex(e)),r=[];return s.forEach(function(e,t){r.push(e),t!==s.length-1&&r.push(n)}),r})))}),o):o},t.chat_commands.mentionize=function(e,t){return t&&t.length&&(this.mention_words=t.join(" ").trim().split(/\W*,\W*/),1==this.mention_words.length&&"disable"==this.mention_words[0]&&(this.mention_words=[]),localStorage.ffzMentionize=JSON.stringify(this.mention_words)),this.mention_words.length?"The following words will be treated as mentions: "+this.mention_words.join(", "):"There are no words set that will be treated as mentions."},t.chat_commands.mentionize.help="Usage: /ffz mentionize \nSet a list of words that will also be treated as mentions and be displayed specially in chat.",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),i=[];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)})&&i.push(e)})}),i.length?("string"==typeof t&&(t=[t]),_.each(i,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+$/,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 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 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(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,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 u=0;uo?this._legacy_load_set(e,t,o):"function"==typeof t&&t(!1))})},o.prototype._legacy_load_css=function(e,t,o){var r={},i={id:e,emotes:r,extra_css:null},a=this;o=o.replace(n,function(e,t,o,n,s,i,c,u){s=parseInt(s),i=parseInt(i),c=l(c,s);var d="."===n.substr(n.lastIndexOf("/")+1,1),h=++a._last_emote_id,m={id:h,hidden:d,name:o,height:s,width:i,url:n,margins:c,extra_css:u};return r[h]=m,""}).trim(),o&&o.replace(s,function(e,t){i.icon||"modicon.png"!==t.substr(-11)||(i.icon=t)}),this._load_set_json(e,t,i)}},{"./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 i=BetterTTV.chat.templates.privmsg;BetterTTV.chat.templates.privmsg=function(e,t,r,a,l){n.bttv_badges(l);var c=i(e,t,r,a,l);return c.replace(o,'$1 data-room="'+s+'"')};var a,l=BetterTTV.chat.templates.message;BetterTTV.chat.templates.message=function(e,t,o,n){a=e;var s=l(e,t,o,n);return a=null,s};var c=BetterTTV.chat.templates.emoticonize;BetterTTV.chat.templates.emoticonize=function(e,t){var o=c(e,t),r=n.getEmotes(a,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),i=[],a=0;a=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(),i=r-n;this.log("Initialization complete in "+i+"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,i=document.createElement("div"),a=document.createElement("a");i.className="chat-menu-content",i.style.textAlign="center";var l=s.display_name+(s.live?" is live now!":"");a.className="button primary",a.classList.toggle("live",s.live),a.classList.toggle("blue",this.has_bttv&&BetterTTV.settings.get("showBlueButtons")),a.href="http://www.twitch.tv/"+s.channel,a.title=l,a.target="_new",a.innerHTML=""+l+"",a.addEventListener("click",function(){r.track("trackLink",this.href,"link")}),i.appendChild(a),t.appendChild(i)}}},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,o=this;this._ws_last_req=0,this._ws_callbacks={},this._ws_pending=this._ws_pending||[];try{e=this._ws_sock=new WebSocket("ws://ffz.stendec.me/")}catch(n){return this._ws_exists=!1,this.log("Error Creating WebSocket: "+n)}this._ws_exists=!0,e.onopen=function(){o._ws_open=!0,o._ws_delay=0,o.log("Socket connected.");var e=o.get_user();e&&o.ws_send("setuser",e.login);for(var t in o.rooms)o.ws_send("sub",t);var n=o._ws_pending;o._ws_pending=[];for(var s=0;s0){s=!0;break}}e.classList.toggle("no-emotes",!s),e.classList.toggle("live",a),e.classList.toggle("dark",r),e.classList.toggle("blue",i)}}},{"../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"),i=r&&r.get&&r.get("id");if(i===t){var a=document.querySelector(".channel-stats .ffz.stat"),l=n.ZREKNARF+" "+s.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":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+"*/",i=n.indexOf(s),a=n.indexOf(r),l=-1!==i&&-1!==a&&a>i;(l||o)&&(l&&(n=n.substr(0,i)+n.substr(a+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/socket.js b/src/socket.js index ad4fbec0..35e8785d 100644 --- a/src/socket.js +++ b/src/socket.js @@ -11,13 +11,20 @@ FFZ.ws_commands = {}; // ---------------- FFZ.prototype.ws_create = function() { - var f = this; + var f = this, ws; 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/"); + try { + ws = this._ws_sock = new WebSocket("ws://ffz.stendec.me/"); + } catch(err) { + this._ws_exists = false; + return this.log("Error Creating WebSocket: " + err); + } + + this._ws_exists = true; ws.onopen = function(e) { f._ws_open = true; diff --git a/src/ui/menu.js b/src/ui/menu.js index 9c325218..fa8dbce8 100644 --- a/src/ui/menu.js +++ b/src/ui/menu.js @@ -67,10 +67,18 @@ FFZ.prototype.build_ui_popup = function(view) { var c = this._emotes_for_sets(inner, view, room && room.menu_sets || []); - if ( c === 0 ) - btn.addEventListener('click', this._add_emote.bind(this, view, "To use custom emoticons in tons of channels, get FrankerFaceZ from http://www.frankerfacez.com")); - else - btn.addEventListener('click', this._add_emote.bind(this, view, "To view this channel's emoticons, get FrankerFaceZ from http://www.frankerfacez.com")); + if ( ! this._ws_exists ) { + btn.className = "button ffz-button primary"; + btn.innerHTML = "Server Error"; + btn.title = "FFZ Server Error"; + btn.addEventListener('click', alert.bind(window, "The FrankerFaceZ client was unable to create a WebSocket to communicate with the FrankerFaceZ server.\n\nThis is most likely due to your browser's configuration either disabling WebSockets entirely or limiting the number of simultaneous connections. Please ensure that WebSockets have not been disabled.")); + + } else { + if ( c === 0 ) + btn.addEventListener('click', this._add_emote.bind(this, view, "To use custom emoticons in tons of channels, get FrankerFaceZ from http://www.frankerfacez.com")); + 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); diff --git a/style.css b/style.css index 1fb4bc08..9b754b8d 100644 --- a/style.css +++ b/style.css @@ -59,6 +59,7 @@ .ffz-button { float: right; margin-top: -6px; + text-transform: none; } .ffz-noty .noty_message { @@ -147,7 +148,8 @@ position: absolute; bottom: 10px; - right: 10px; + margin-right: 150px; + left: 10px; z-index: 7; padding: 10px; opacity: 0.95;